Merge "Clean up status bar, system bar, navigation bar management."
diff --git a/api/current.txt b/api/current.txt
index 10377fd..d8461e7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -596,7 +596,7 @@
     field public static final int layerType = 16843604; // 0x1010354
     field public static final int layout = 16842994; // 0x10100f2
     field public static final int layoutAnimation = 16842988; // 0x10100ec
-    field public static final int layoutDirection = 16843690; // 0x10103aa
+    field public static final int layoutDirection = 16843691; // 0x10103ab
     field public static final int layout_above = 16843140; // 0x1010184
     field public static final int layout_alignBaseline = 16843142; // 0x1010186
     field public static final int layout_alignBottom = 16843146; // 0x101018a
@@ -618,10 +618,10 @@
     field public static final int layout_height = 16842997; // 0x10100f5
     field public static final int layout_margin = 16842998; // 0x10100f6
     field public static final int layout_marginBottom = 16843002; // 0x10100fa
-    field public static final int layout_marginEnd = 16843694; // 0x10103ae
+    field public static final int layout_marginEnd = 16843695; // 0x10103af
     field public static final int layout_marginLeft = 16842999; // 0x10100f7
     field public static final int layout_marginRight = 16843001; // 0x10100f9
-    field public static final int layout_marginStart = 16843693; // 0x10103ad
+    field public static final int layout_marginStart = 16843694; // 0x10103ae
     field public static final int layout_marginTop = 16843000; // 0x10100f8
     field public static final int layout_row = 16843643; // 0x101037b
     field public static final int layout_rowSpan = 16843644; // 0x101037c
@@ -717,10 +717,10 @@
     field public static final int packageNames = 16843649; // 0x1010381
     field public static final int padding = 16842965; // 0x10100d5
     field public static final int paddingBottom = 16842969; // 0x10100d9
-    field public static final int paddingEnd = 16843692; // 0x10103ac
+    field public static final int paddingEnd = 16843693; // 0x10103ad
     field public static final int paddingLeft = 16842966; // 0x10100d6
     field public static final int paddingRight = 16842968; // 0x10100d8
-    field public static final int paddingStart = 16843691; // 0x10103ab
+    field public static final int paddingStart = 16843692; // 0x10103ac
     field public static final int paddingTop = 16842967; // 0x10100d7
     field public static final int panelBackground = 16842846; // 0x101005e
     field public static final int panelColorBackground = 16842849; // 0x1010061
@@ -962,6 +962,7 @@
     field public static final int tension = 16843370; // 0x101026a
     field public static final int testOnly = 16843378; // 0x1010272
     field public static final int text = 16843087; // 0x101014f
+    field public static final int textAlignment = 16843690; // 0x10103aa
     field public static final int textAllCaps = 16843660; // 0x101038c
     field public static final int textAppearance = 16842804; // 0x1010034
     field public static final int textAppearanceButton = 16843271; // 0x1010207
@@ -23210,6 +23211,7 @@
     method public void buildLayer();
     method public boolean callOnClick();
     method public boolean canResolveLayoutDirection();
+    method public boolean canResolveTextAlignment();
     method public boolean canResolveTextDirection();
     method public boolean canScrollHorizontally(int);
     method public boolean canScrollVertically(int);
@@ -23312,6 +23314,8 @@
     method public final int getMeasuredState();
     method public final int getMeasuredWidth();
     method public final int getMeasuredWidthAndState();
+    method public int getMinimumHeight();
+    method public int getMinimumWidth();
     method public int getNextFocusDownId();
     method public int getNextFocusForwardId();
     method public int getNextFocusLeftId();
@@ -23330,6 +23334,7 @@
     method public float getPivotY();
     method public int getResolvedLayoutDirection();
     method public int getResolvedLayoutDirection(android.graphics.drawable.Drawable);
+    method public int getResolvedTextAlignment();
     method public int getResolvedTextDirection();
     method public android.content.res.Resources getResources();
     method public final int getRight();
@@ -23341,6 +23346,9 @@
     method public float getRotationY();
     method public float getScaleX();
     method public float getScaleY();
+    method public int getScrollBarDefaultDelayBeforeFade();
+    method public int getScrollBarFadeDuration();
+    method public int getScrollBarSize();
     method public int getScrollBarStyle();
     method public final int getScrollX();
     method public final int getScrollY();
@@ -23350,6 +23358,7 @@
     method public int getSystemUiVisibility();
     method public java.lang.Object getTag();
     method public java.lang.Object getTag(int);
+    method public int getTextAlignment();
     method public int getTextDirection();
     method public final int getTop();
     method protected float getTopFadingEdgeStrength();
@@ -23410,6 +23419,7 @@
     method public boolean isPressed();
     method public boolean isSaveEnabled();
     method public boolean isSaveFromParentEnabled();
+    method public boolean isScrollContainer();
     method public boolean isScrollbarFadingEnabled();
     method public boolean isSelected();
     method public boolean isShown();
@@ -23457,6 +23467,8 @@
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public void onResolvedLayoutDirectionChanged();
     method public void onResolvedLayoutDirectionReset();
+    method public void onResolvedTextAlignmentChanged();
+    method public void onResolvedTextAlignmentReset();
     method public void onResolvedTextDirectionChanged();
     method public void onResolvedTextDirectionReset();
     method protected void onRestoreInstanceState(android.os.Parcelable);
@@ -23497,11 +23509,13 @@
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
     method public void resetResolvedLayoutDirection();
+    method public void resetResolvedTextAlignment();
     method public void resetResolvedTextDirection();
     method public void resolveLayoutDirection();
     method public void resolvePadding();
     method public static int resolveSize(int, int);
     method public static int resolveSizeAndState(int, int, int);
+    method public void resolveTextAlignment();
     method public void resolveTextDirection();
     method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>);
     method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable>);
@@ -23514,8 +23528,9 @@
     method public void setActivated(boolean);
     method public void setAlpha(float);
     method public void setAnimation(android.view.animation.Animation);
+    method public void setBackground(android.graphics.drawable.Drawable);
     method public void setBackgroundColor(int);
-    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+    method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
     method public void setBackgroundResource(int);
     method public final void setBottom(int);
     method public void setCameraDistance(float);
@@ -23575,6 +23590,9 @@
     method public void setSaveFromParentEnabled(boolean);
     method public void setScaleX(float);
     method public void setScaleY(float);
+    method public void setScrollBarDefaultDelayBeforeFade(int);
+    method public void setScrollBarFadeDuration(int);
+    method public void setScrollBarSize(int);
     method public void setScrollBarStyle(int);
     method public void setScrollContainer(boolean);
     method public void setScrollX(int);
@@ -23585,6 +23603,7 @@
     method public void setSystemUiVisibility(int);
     method public void setTag(java.lang.Object);
     method public void setTag(int, java.lang.Object);
+    method public void setTextAlignment(int);
     method public void setTextDirection(int);
     method public final void setTop(int);
     method public void setTouchDelegate(android.view.TouchDelegate);
@@ -23695,6 +23714,15 @@
     field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
     field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
     field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
+    field public static final int TEXT_ALIGNMENT_CENTER = 4; // 0x4
+    field protected static int TEXT_ALIGNMENT_DEFAULT;
+    field public static final int TEXT_ALIGNMENT_GRAVITY = 1; // 0x1
+    field public static final int TEXT_ALIGNMENT_INHERIT = 0; // 0x0
+    field public static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT = 131072; // 0x20000
+    field public static final int TEXT_ALIGNMENT_TEXT_END = 3; // 0x3
+    field public static final int TEXT_ALIGNMENT_TEXT_START = 2; // 0x2
+    field public static final int TEXT_ALIGNMENT_VIEW_END = 6; // 0x6
+    field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
     field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
     field protected static int TEXT_DIRECTION_DEFAULT;
     field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
@@ -24120,15 +24148,18 @@
   }
 
   public final class ViewTreeObserver {
+    method public void addOnDrawListener(android.view.ViewTreeObserver.OnDrawListener);
     method public void addOnGlobalFocusChangeListener(android.view.ViewTreeObserver.OnGlobalFocusChangeListener);
     method public void addOnGlobalLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
     method public void addOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener);
     method public void addOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener);
     method public void addOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
+    method public final void dispatchOnDraw();
     method public final void dispatchOnGlobalLayout();
     method public final boolean dispatchOnPreDraw();
     method public boolean isAlive();
     method public deprecated void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
+    method public void removeOnDrawListener(android.view.ViewTreeObserver.OnDrawListener);
     method public void removeOnGlobalFocusChangeListener(android.view.ViewTreeObserver.OnGlobalFocusChangeListener);
     method public void removeOnGlobalLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
     method public void removeOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener);
@@ -24136,6 +24167,10 @@
     method public void removeOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
   }
 
+  public static abstract interface ViewTreeObserver.OnDrawListener {
+    method public abstract void onDraw();
+  }
+
   public static abstract interface ViewTreeObserver.OnGlobalFocusChangeListener {
     method public abstract void onGlobalFocusChanged(android.view.View, android.view.View);
   }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 096af93..04c64a0 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -26,6 +26,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.IntProperty;
 import android.util.Log;
@@ -808,6 +809,7 @@
     @Deprecated
     public void setLatestEventInfo(Context context,
             CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
+        // TODO: rewrite this to use Builder
         RemoteViews contentView = new RemoteViews(context.getPackageName(),
                 R.layout.notification_template_base);
         if (this.icon != 0) {
@@ -820,6 +822,7 @@
             contentView.setTextViewText(R.id.text, contentText);
         }
         if (this.when != 0) {
+            contentView.setViewVisibility(R.id.time, View.VISIBLE);
             contentView.setLong(R.id.time, "setTime", when);
         }
 
@@ -942,6 +945,7 @@
         private ArrayList<Action> mActions = new ArrayList<Action>(3);
         private boolean mCanHasIntruder;
         private boolean mIntruderActionsShowText;
+        private boolean mUseChronometer;
 
         /**
          * Constructs a new Builder with the defaults:
@@ -983,6 +987,18 @@
         }
 
         /**
+         * @hide
+         * 
+         * Show the {@link Notification#when} field as a countdown (or count-up) timer instead of a timestamp.  
+         *
+         * @see Notification#when
+         */
+        public Builder setUsesChronometer(boolean b) {
+            mUseChronometer = b;
+            return this;
+        }
+
+        /**
          * Set the small icon resource, which will be used to represent the notification in the
          * status bar.
          *
@@ -1434,7 +1450,15 @@
                 }
             }
             if (mWhen != 0) {
-                contentView.setLong(R.id.time, "setTime", mWhen);
+                if (mUseChronometer) {
+                    contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
+                    contentView.setLong(R.id.chronometer, "setBase",
+                            mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
+                    contentView.setBoolean(R.id.chronometer, "setStarted", true);
+                } else {
+                    contentView.setViewVisibility(R.id.time, View.VISIBLE);
+                    contentView.setLong(R.id.time, "setTime", mWhen);
+                }
             }
             contentView.setViewVisibility(R.id.line3, hasLine3 ? View.VISIBLE : View.GONE);
             return contentView;
diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/INdefPushCallback.aidl
index e60a5b0..4e79822 100644
--- a/core/java/android/nfc/INdefPushCallback.aidl
+++ b/core/java/android/nfc/INdefPushCallback.aidl
@@ -17,6 +17,7 @@
 package android.nfc;
 
 import android.nfc.NdefMessage;
+import android.net.Uri;
 
 /**
  * @hide
@@ -24,5 +25,7 @@
 interface INdefPushCallback
 {
     NdefMessage createMessage();
+    Uri getUri();
+    String getMimeType();
     void onNdefPushComplete();
 }
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 2c73056..f80dae4 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.app.Application;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.util.Log;
@@ -107,10 +108,16 @@
         NdefMessage ndefMessage = null;  // static NDEF message
         NfcAdapter.CreateNdefMessageCallback ndefMessageCallback = null;
         NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
+        Uri uri = null;
+        String mimeType = null;
         public NfcActivityState(Activity activity) {
             if (activity.getWindow().isDestroyed()) {
                 throw new IllegalStateException("activity is already destroyed");
             }
+            // Check if activity is resumed right now, as we will not
+            // immediately get a callback for that.
+            resumed = activity.isResumed();
+
             this.activity = activity;
             registerApplication(activity.getApplication());
         }
@@ -121,12 +128,14 @@
             ndefMessage = null;
             ndefMessageCallback = null;
             onNdefPushCompleteCallback = null;
+            uri = null;
+            mimeType = null;
         }
         @Override
         public String toString() {
             StringBuilder s = new StringBuilder("[").append(" ");
             s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
-            s.append(onNdefPushCompleteCallback).append("]");
+            s.append(onNdefPushCompleteCallback).append(" ").append(uri).append("]");
             return s.toString();
         }
     }
@@ -175,6 +184,19 @@
         mDefaultEvent = new NfcEvent(mAdapter);
     }
 
+    public void setNdefPushContentUri(Activity activity, String mimeType, Uri uri) {
+        boolean isResumed;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.uri = uri;
+            state.mimeType = mimeType;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            requestNfcServiceCallback(true);
+        }
+    }
+
     public void setNdefPushMessage(Activity activity, NdefMessage message) {
         boolean isResumed;
         synchronized (NfcActivityManager.this) {
@@ -249,6 +271,26 @@
 
     /** Callback from NFC service, usually on binder thread */
     @Override
+    public Uri getUri() {
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = findResumedActivityState();
+            if (state == null) return null;
+
+            return state.uri;
+        }
+    }
+    /** Callback from NFC service, usually on binder thread */
+    @Override
+    public String getMimeType() {
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = findResumedActivityState();
+            if (state == null) return null;
+
+            return state.mimeType;
+        }
+    }
+    /** Callback from NFC service, usually on binder thread */
+    @Override
     public void onNdefPushComplete() {
         NfcAdapter.OnNdefPushCompleteCallback callback;
         synchronized (NfcActivityManager.this) {
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index d78e06c..917751c 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -28,6 +28,7 @@
 import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.net.Uri;
 import android.nfc.tech.MifareClassic;
 import android.nfc.tech.Ndef;
 import android.nfc.tech.NfcA;
@@ -555,6 +556,18 @@
         }
     }
 
+    //TODO: Consider a callback alternative
+    //TOOD: See if we get rid of mimeType
+    //TODO: make sure NFC service has permission for URI
+    //TODO: javadoc
+    /** @hide */
+    public void setBeamPushUri(String mimeType, Uri uri, Activity activity) {
+        if (activity == null) {
+            throw new NullPointerException("activity cannot be null");
+        }
+        mNfcActivityManager.setNdefPushContentUri(activity, mimeType, uri);
+    }
+
     /**
      * Set a static {@link NdefMessage} to send using Android Beam (TM).
      *
diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java
index 9d1e6a1..8c92288 100644
--- a/core/java/android/nfc/tech/MifareClassic.java
+++ b/core/java/android/nfc/tech/MifareClassic.java
@@ -150,6 +150,7 @@
         mIsEmulated = false;
 
         switch (a.getSak()) {
+        case 0x01:
         case 0x08:
             mType = TYPE_CLASSIC;
             mSize = SIZE_1K;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d74ccb8..830a85f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1506,13 +1506,22 @@
         public static final String VOLUME_MASTER = "volume_master";
 
         /**
-         * Whether the notifications should use the ring volume (value of 1) or a separate
-         * notification volume (value of 0). In most cases, users will have this enabled so the
-         * notification and ringer volumes will be the same. However, power users can disable this
-         * and use the separate notification volume control.
+         * Master volume mute (int 1 = mute, 0 = not muted).
+         *
+         * @hide
+         */
+        public static final String VOLUME_MASTER_MUTE = "volume_master_mute";
+
+        /**
+         * Whether the notifications should use the ring volume (value of 1) or
+         * a separate notification volume (value of 0). In most cases, users
+         * will have this enabled so the notification and ringer volumes will be
+         * the same. However, power users can disable this and use the separate
+         * notification volume control.
          * <p>
-         * Note: This is a one-off setting that will be removed in the future when there is profile
-         * support. For this reason, it is kept hidden from the public APIs.
+         * Note: This is a one-off setting that will be removed in the future
+         * when there is profile support. For this reason, it is kept hidden
+         * from the public APIs.
          *
          * @hide
          * @deprecated
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c40a7d5..5d7c8cd 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -71,6 +71,7 @@
 import android.widget.ScrollBarDrawable;
 
 import static android.os.Build.VERSION_CODES.*;
+import static java.lang.Math.max;
 
 import com.android.internal.R;
 import com.android.internal.util.Predicate;
@@ -126,7 +127,7 @@
  * example, all views will let you set a listener to be notified when the view
  * gains or loses focus. You can register such a listener using
  * {@link #setOnFocusChangeListener(android.view.View.OnFocusChangeListener)}.
- * Other view subclasses offer more specialized listeners. For example, a Button 
+ * Other view subclasses offer more specialized listeners. For example, a Button
  * exposes a listener to notify clients when the button is clicked.</li>
  * <li><strong>Set visibility:</strong> You can hide or show views using
  * {@link #setVisibility(int)}.</li>
@@ -579,6 +580,7 @@
  * @attr ref android.R.styleable#View_duplicateParentState
  * @attr ref android.R.styleable#View_id
  * @attr ref android.R.styleable#View_requiresFadingEdge
+ * @attr ref android.R.styleable#View_fadeScrollbars
  * @attr ref android.R.styleable#View_fadingEdgeLength
  * @attr ref android.R.styleable#View_filterTouchesWhenObscured
  * @attr ref android.R.styleable#View_fitsSystemWindows
@@ -624,6 +626,7 @@
  * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack
  * @attr ref android.R.styleable#View_soundEffectsEnabled
  * @attr ref android.R.styleable#View_tag
+ * @attr ref android.R.styleable#View_textAlignment
  * @attr ref android.R.styleable#View_transformPivotX
  * @attr ref android.R.styleable#View_transformPivotY
  * @attr ref android.R.styleable#View_translationX
@@ -1827,17 +1830,17 @@
     public static final int TEXT_DIRECTION_LOCALE = 5;
 
     /**
+     * Default text direction is inherited
+     */
+    protected static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
+
+    /**
      * Bit shift to get the horizontal layout direction. (bits after LAYOUT_DIRECTION_RESOLVED)
      * @hide
      */
     static final int TEXT_DIRECTION_MASK_SHIFT = 6;
 
     /**
-     * Default text direction is inherited
-     */
-    protected static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
-
-    /**
      * Mask for use with private flags indicating bits used for text direction.
      * @hide
      */
@@ -1882,6 +1885,113 @@
     static final int TEXT_DIRECTION_RESOLVED_DEFAULT =
             TEXT_DIRECTION_FIRST_STRONG << TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
 
+    /*
+     * Default text alignment. The text alignment of this View is inherited from its parent.
+     * Use with {@link #setTextAlignment(int)}
+     */
+    public static final int TEXT_ALIGNMENT_INHERIT = 0;
+
+    /**
+     * Default for the root view. The gravity determines the text alignment, ALIGN_NORMAL,
+     * ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s text direction.
+     *
+     * Use with {@link #setTextAlignment(int)}
+     */
+    public static final int TEXT_ALIGNMENT_GRAVITY = 1;
+
+    /**
+     * Align to the start of the paragraph, e.g. ALIGN_NORMAL.
+     *
+     * Use with {@link #setTextAlignment(int)}
+     */
+    public static final int TEXT_ALIGNMENT_TEXT_START = 2;
+
+    /**
+     * Align to the end of the paragraph, e.g. ALIGN_OPPOSITE.
+     *
+     * Use with {@link #setTextAlignment(int)}
+     */
+    public static final int TEXT_ALIGNMENT_TEXT_END = 3;
+
+    /**
+     * Center the paragraph, e.g. ALIGN_CENTER.
+     *
+     * Use with {@link #setTextAlignment(int)}
+     */
+    public static final int TEXT_ALIGNMENT_CENTER = 4;
+
+    /**
+     * Align to the start of the view, which is ALIGN_LEFT if the view’s resolved
+     * layoutDirection is LTR, and ALIGN_RIGHT otherwise.
+     *
+     * Use with {@link #setTextAlignment(int)}
+     */
+    public static final int TEXT_ALIGNMENT_VIEW_START = 5;
+
+    /**
+     * Align to the end of the view, which is ALIGN_RIGHT if the view’s resolved
+     * layoutDirection is LTR, and ALIGN_LEFT otherwise.
+     *
+     * Use with {@link #setTextAlignment(int)}
+     */
+    public static final int TEXT_ALIGNMENT_VIEW_END = 6;
+
+    /**
+     * Default text alignment is inherited
+     */
+    protected static int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY;
+
+    /**
+      * Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED)
+      * @hide
+      */
+    static final int TEXT_ALIGNMENT_MASK_SHIFT = 13;
+
+    /**
+      * Mask for use with private flags indicating bits used for text alignment.
+      * @hide
+      */
+    static final int TEXT_ALIGNMENT_MASK = 0x00000007 << TEXT_ALIGNMENT_MASK_SHIFT;
+
+    /**
+     * Array of text direction flags for mapping attribute "textAlignment" to correct
+     * flag value.
+     * @hide
+     */
+    private static final int[] TEXT_ALIGNMENT_FLAGS = {
+            TEXT_ALIGNMENT_INHERIT << TEXT_ALIGNMENT_MASK_SHIFT,
+            TEXT_ALIGNMENT_GRAVITY << TEXT_ALIGNMENT_MASK_SHIFT,
+            TEXT_ALIGNMENT_TEXT_START << TEXT_ALIGNMENT_MASK_SHIFT,
+            TEXT_ALIGNMENT_TEXT_END << TEXT_ALIGNMENT_MASK_SHIFT,
+            TEXT_ALIGNMENT_CENTER << TEXT_ALIGNMENT_MASK_SHIFT,
+            TEXT_ALIGNMENT_VIEW_START << TEXT_ALIGNMENT_MASK_SHIFT,
+            TEXT_ALIGNMENT_VIEW_END << TEXT_ALIGNMENT_MASK_SHIFT
+    };
+
+    /**
+     * Indicates whether the view text alignment has been resolved.
+     * @hide
+     */
+    static final int TEXT_ALIGNMENT_RESOLVED = 0x00000008 << TEXT_ALIGNMENT_MASK_SHIFT;
+
+    /**
+     * Bit shift to get the resolved text alignment.
+     * @hide
+     */
+    static final int TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT = 17;
+
+    /**
+     * Mask for use with private flags indicating bits used for text alignment.
+     * @hide
+     */
+    static final int TEXT_ALIGNMENT_RESOLVED_MASK = 0x00000007 << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+
+    /**
+     * Indicates whether if the view text alignment has been resolved to gravity
+     */
+    public static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT =
+            TEXT_ALIGNMENT_GRAVITY << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+
 
     /* End of masks for mPrivateFlags2 */
 
@@ -1926,7 +2036,7 @@
      * system UI to enter an unobtrusive "low profile" mode.
      *
      * <p>This is for use in games, book readers, video players, or any other
-     * "immersive" application where the usual system chrome is deemed too distracting. 
+     * "immersive" application where the usual system chrome is deemed too distracting.
      *
      * <p>In low profile mode, the status bar and/or navigation icons may dim.
      *
@@ -1942,7 +2052,7 @@
      * {@link #SYSTEM_UI_FLAG_LOW_PROFILE}; on devices that draw essential navigation controls
      * (Home, Back, and the like) on screen, <code>SYSTEM_UI_FLAG_HIDE_NAVIGATION</code> will cause
      * those to disappear. This is useful (in conjunction with the
-     * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN FLAG_FULLSCREEN} and 
+     * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN FLAG_FULLSCREEN} and
      * {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN FLAG_LAYOUT_IN_SCREEN}
      * window flags) for displaying content using every last pixel on the display.
      *
@@ -2339,7 +2449,7 @@
          */
         private int mPrevWidth = -1;
         private int mPrevHeight = -1;
-        
+
         /**
          * The degrees rotation around the vertical axis through the pivot point.
          */
@@ -2546,7 +2656,7 @@
      */
     int mOldHeightMeasureSpec = Integer.MIN_VALUE;
 
-    private Drawable mBGDrawable;
+    private Drawable mBackground;
 
     private int mBackgroundResource;
     private boolean mBackgroundSizeChanged;
@@ -2620,7 +2730,7 @@
 
     /**
      * Set to true when drawing cache is enabled and cannot be created.
-     * 
+     *
      * @hide
      */
     public boolean mCachingFailed;
@@ -2841,7 +2951,8 @@
         mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
         // Set layout and text direction defaults
         mPrivateFlags2 = (LAYOUT_DIRECTION_DEFAULT << LAYOUT_DIRECTION_MASK_SHIFT) |
-                (TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT);
+                (TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT) |
+                (TEXT_ALIGNMENT_DEFAULT << TEXT_ALIGNMENT_MASK_SHIFT);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
         mUserPaddingStart = -1;
@@ -3222,6 +3333,13 @@
                         mPrivateFlags2 |= TEXT_DIRECTION_FLAGS[textDirection];
                     }
                     break;
+                case R.styleable.View_textAlignment:
+                    // Clear any text alignment flag already set
+                    mPrivateFlags2 &= ~TEXT_ALIGNMENT_MASK;
+                    // Set the text alignment flag depending on the value of the attribute
+                    final int textAlignment = a.getInt(attr, TEXT_ALIGNMENT_DEFAULT);
+                    mPrivateFlags2 |= TEXT_ALIGNMENT_FLAGS[textAlignment];
+                    break;
             }
         }
 
@@ -3230,7 +3348,7 @@
         setOverScrollMode(overScrollMode);
 
         if (background != null) {
-            setBackgroundDrawable(background);
+            setBackground(background);
         }
 
         // Cache user padding as we cannot fully resolve padding here (we dont have yet the resolved
@@ -3494,6 +3612,11 @@
         }
     }
 
+    private ScrollabilityCache getScrollCache() {
+        initScrollCache();
+        return mScrollCache;
+    }
+
     /**
      * Set the position of the vertical scroll bar. Should be one of
      * {@link #SCROLLBAR_POSITION_DEFAULT}, {@link #SCROLLBAR_POSITION_LEFT} or
@@ -3909,8 +4032,9 @@
      * <p>
      * <strong>Note:</strong> When a View clears focus the framework is trying
      * to give focus to the first focusable View from the top. Hence, if this
-     * View is the first from the top that can take focus, then its focus will
-     * not be cleared nor will the focus change callback be invoked.
+     * View is the first from the top that can take focus, then all callbacks
+     * related to clearing focus will be invoked after wich the framework will
+     * give focus to this view.
      * </p>
      */
     public void clearFocus() {
@@ -3927,25 +4051,22 @@
 
             onFocusChanged(false, 0, null);
             refreshDrawableState();
+
+            ensureInputFocusOnFirstFocusable();
         }
     }
 
-    /**
-     * Called to clear the focus of a view that is about to be removed.
-     * Doesn't call clearChildFocus, which prevents this view from taking
-     * focus again before it has been removed from the parent
-     */
-    void clearFocusForRemoval() {
-        if ((mPrivateFlags & FOCUSED) != 0) {
-            mPrivateFlags &= ~FOCUSED;
-
-            onFocusChanged(false, 0, null);
-            refreshDrawableState();
-
-            // The view cleared focus and invoked the callbacks, so  now is the
-            // time to give focus to the the first focusable from the top to
-            // ensure that the gain focus is announced after clear focus.
-            getRootView().requestFocus(FOCUS_FORWARD);
+    void ensureInputFocusOnFirstFocusable() {
+        View root = getRootView();
+        if (root != null) {
+            // Find the first focusble from the top.
+            View next = root.focusSearch(FOCUS_FORWARD);
+            if (next != null) {
+                // Giving focus to the found focusable will not
+                // perform a search since we found a view that is
+                // guaranteed to be able to take focus.
+                next.requestFocus(FOCUS_FORWARD);
+            }
         }
     }
 
@@ -4562,11 +4683,26 @@
     }
 
     /**
+     * Indicates whether this view is one of the set of scrollable containers in
+     * its window.
+     *
+     * @return whether this view is one of the set of scrollable containers in
+     * its window
+     *
+     * @attr ref android.R.styleable#View_isScrollContainer
+     */
+    public boolean isScrollContainer() {
+        return (mPrivateFlags & SCROLL_CONTAINER_ADDED) != 0;
+    }
+
+    /**
      * Change whether this view is one of the set of scrollable containers in
      * its window.  This will be used to determine whether the window can
      * resize or must pan when a soft input area is open -- scrollable
      * containers allow the window to use resize mode since the container
      * will appropriately shrink.
+     *
+     * @attr ref android.R.styleable#View_isScrollContainer
      */
     public void setScrollContainer(boolean isScrollContainer) {
         if (isScrollContainer) {
@@ -4898,7 +5034,7 @@
     @RemotableViewMethod
     public void setVisibility(int visibility) {
         setFlags(visibility, VISIBILITY_MASK);
-        if (mBGDrawable != null) mBGDrawable.setVisible(visibility == VISIBLE, false);
+        if (mBackground != null) mBackground.setVisible(visibility == VISIBLE, false);
     }
 
     /**
@@ -5279,7 +5415,7 @@
      * {@link #setPressed(boolean)} is explicitly called, only clickable views can enter
      * the pressed state.
      *
-     * @see #setPressed(boolean) 
+     * @see #setPressed(boolean)
      * @see #isClickable()
      * @see #setClickable(boolean)
      *
@@ -5965,7 +6101,7 @@
     /**
      * Dispatch a hover event.
      * <p>
-     * Do not call this method directly. 
+     * Do not call this method directly.
      * Call {@link #dispatchGenericMotionEvent(MotionEvent)} instead.
      * </p>
      *
@@ -6152,7 +6288,7 @@
      *
      * @param visibility The new visibility of the window.
      *
-     * @see #onWindowVisibilityChanged(int) 
+     * @see #onWindowVisibilityChanged(int)
      */
     public void dispatchWindowVisibilityChanged(int visibility) {
         onWindowVisibilityChanged(visibility);
@@ -6228,7 +6364,7 @@
      *
      * @param newConfig The new resource configuration.
      *
-     * @see #onConfigurationChanged(android.content.res.Configuration) 
+     * @see #onConfigurationChanged(android.content.res.Configuration)
      */
     public void dispatchConfigurationChanged(Configuration newConfig) {
         onConfigurationChanged(newConfig);
@@ -7096,7 +7232,7 @@
 
         if ((changed & DRAW_MASK) != 0) {
             if ((mViewFlags & WILL_NOT_DRAW) != 0) {
-                if (mBGDrawable != null) {
+                if (mBackground != null) {
                     mPrivateFlags &= ~SKIP_DRAW;
                     mPrivateFlags |= ONLY_DRAWS_BACKGROUND;
                 } else {
@@ -7484,39 +7620,39 @@
      * views are drawn) from the camera to this view. The camera's distance
      * affects 3D transformations, for instance rotations around the X and Y
      * axis. If the rotationX or rotationY properties are changed and this view is
-     * large (more than half the size of the screen), it is recommended to always 
+     * large (more than half the size of the screen), it is recommended to always
      * use a camera distance that's greater than the height (X axis rotation) or
      * the width (Y axis rotation) of this view.</p>
-     * 
+     *
      * <p>The distance of the camera from the view plane can have an affect on the
      * perspective distortion of the view when it is rotated around the x or y axis.
      * For example, a large distance will result in a large viewing angle, and there
      * will not be much perspective distortion of the view as it rotates. A short
-     * distance may cause much more perspective distortion upon rotation, and can 
+     * distance may cause much more perspective distortion upon rotation, and can
      * also result in some drawing artifacts if the rotated view ends up partially
      * behind the camera (which is why the recommendation is to use a distance at
      * least as far as the size of the view, if the view is to be rotated.)</p>
-     * 
+     *
      * <p>The distance is expressed in "depth pixels." The default distance depends
      * on the screen density. For instance, on a medium density display, the
      * default distance is 1280. On a high density display, the default distance
      * is 1920.</p>
-     * 
+     *
      * <p>If you want to specify a distance that leads to visually consistent
      * results across various densities, use the following formula:</p>
      * <pre>
      * float scale = context.getResources().getDisplayMetrics().density;
      * view.setCameraDistance(distance * scale);
      * </pre>
-     * 
+     *
      * <p>The density scale factor of a high density display is 1.5,
      * and 1920 = 1280 * 1.5.</p>
-     * 
+     *
      * @param distance The distance in "depth pixels", if negative the opposite
      *        value is used
-     * 
-     * @see #setRotationX(float) 
-     * @see #setRotationY(float) 
+     *
+     * @see #setRotationX(float)
+     * @see #setRotationY(float)
      */
     public void setCameraDistance(float distance) {
         invalidateViewProperty(true, false);
@@ -7541,10 +7677,10 @@
     /**
      * The degrees that the view is rotated around the pivot point.
      *
-     * @see #setRotation(float) 
+     * @see #setRotation(float)
      * @see #getPivotX()
      * @see #getPivotY()
-     * 
+     *
      * @return The degrees of rotation.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
@@ -7557,12 +7693,12 @@
      * result in clockwise rotation.
      *
      * @param rotation The degrees of rotation.
-     * 
-     * @see #getRotation() 
+     *
+     * @see #getRotation()
      * @see #getPivotX()
      * @see #getPivotY()
-     * @see #setRotationX(float) 
-     * @see #setRotationY(float) 
+     * @see #setRotationX(float)
+     * @see #setRotationY(float)
      *
      * @attr ref android.R.styleable#View_rotation
      */
@@ -7586,8 +7722,8 @@
      *
      * @see #getPivotX()
      * @see #getPivotY()
-     * @see #setRotationY(float) 
-     * 
+     * @see #setRotationY(float)
+     *
      * @return The degrees of Y rotation.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
@@ -7599,18 +7735,18 @@
      * Sets the degrees that the view is rotated around the vertical axis through the pivot point.
      * Increasing values result in counter-clockwise rotation from the viewpoint of looking
      * down the y axis.
-     * 
+     *
      * When rotating large views, it is recommended to adjust the camera distance
      * accordingly. Refer to {@link #setCameraDistance(float)} for more information.
      *
      * @param rotationY The degrees of Y rotation.
-     * 
-     * @see #getRotationY() 
+     *
+     * @see #getRotationY()
      * @see #getPivotX()
      * @see #getPivotY()
      * @see #setRotation(float)
-     * @see #setRotationX(float) 
-     * @see #setCameraDistance(float) 
+     * @see #setRotationX(float)
+     * @see #setCameraDistance(float)
      *
      * @attr ref android.R.styleable#View_rotationY
      */
@@ -7633,8 +7769,8 @@
      *
      * @see #getPivotX()
      * @see #getPivotY()
-     * @see #setRotationX(float) 
-     * 
+     * @see #setRotationX(float)
+     *
      * @return The degrees of X rotation.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
@@ -7646,18 +7782,18 @@
      * Sets the degrees that the view is rotated around the horizontal axis through the pivot point.
      * Increasing values result in clockwise rotation from the viewpoint of looking down the
      * x axis.
-     * 
+     *
      * When rotating large views, it is recommended to adjust the camera distance
      * accordingly. Refer to {@link #setCameraDistance(float)} for more information.
      *
      * @param rotationX The degrees of X rotation.
-     * 
-     * @see #getRotationX() 
+     *
+     * @see #getRotationX()
      * @see #getPivotX()
      * @see #getPivotY()
      * @see #setRotation(float)
-     * @see #setRotationY(float) 
-     * @see #setCameraDistance(float) 
+     * @see #setRotationY(float)
+     * @see #setCameraDistance(float)
      *
      * @attr ref android.R.styleable#View_rotationX
      */
@@ -7762,6 +7898,8 @@
      * @see #getScaleY()
      * @see #getPivotY()
      * @return The x location of the pivot point.
+     *
+     * @attr ref android.R.styleable#View_transformPivotX
      */
     @ViewDebug.ExportedProperty(category = "drawing")
     public float getPivotX() {
@@ -7807,6 +7945,8 @@
      * @see #getScaleY()
      * @see #getPivotY()
      * @return The y location of the pivot point.
+     *
+     * @attr ref android.R.styleable#View_transformPivotY
      */
     @ViewDebug.ExportedProperty(category = "drawing")
     public float getPivotY() {
@@ -9022,7 +9162,7 @@
         //   - Background is opaque
         //   - Doesn't have scrollbars or scrollbars are inside overlay
 
-        if (mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE) {
+        if (mBackground != null && mBackground.getOpacity() == PixelFormat.OPAQUE) {
             mPrivateFlags |= OPAQUE_BACKGROUND;
         } else {
             mPrivateFlags &= ~OPAQUE_BACKGROUND;
@@ -9070,7 +9210,7 @@
     /**
      * <p>Causes the Runnable to be added to the message queue.
      * The runnable will be run on the user interface thread.</p>
-     * 
+     *
      * <p>This method can be invoked from outside of the UI thread
      * only when this View is attached to a window.</p>
      *
@@ -9094,7 +9234,7 @@
      * <p>Causes the Runnable to be added to the message queue, to be run
      * after the specified amount of time elapses.
      * The runnable will be run on the user interface thread.</p>
-     * 
+     *
      * <p>This method can be invoked from outside of the UI thread
      * only when this View is attached to a window.</p>
      *
@@ -9168,7 +9308,7 @@
 
     /**
      * <p>Removes the specified Runnable from the message queue.</p>
-     * 
+     *
      * <p>This method can be invoked from outside of the UI thread
      * only when this View is attached to a window.</p>
      *
@@ -9200,7 +9340,7 @@
      *
      * <p>This method can be invoked from outside of the UI thread
      * only when this View is attached to a window.</p>
-     * 
+     *
      * @see #invalidate()
      */
     public void postInvalidate() {
@@ -9210,7 +9350,7 @@
     /**
      * <p>Cause an invalidate of the specified area to happen on a subsequent cycle
      * through the event loop. Use this to invalidate the View from a non-UI thread.</p>
-     * 
+     *
      * <p>This method can be invoked from outside of the UI thread
      * only when this View is attached to a window.</p>
      *
@@ -9229,7 +9369,7 @@
     /**
      * <p>Cause an invalidate to happen on a subsequent cycle through the event
      * loop. Waits for the specified amount of time.</p>
-     * 
+     *
      * <p>This method can be invoked from outside of the UI thread
      * only when this View is attached to a window.</p>
      *
@@ -9248,7 +9388,7 @@
     /**
      * <p>Cause an invalidate of the specified area to happen on a subsequent cycle
      * through the event loop. Waits for the specified amount of time.</p>
-     * 
+     *
      * <p>This method can be invoked from outside of the UI thread
      * only when this View is attached to a window.</p>
      *
@@ -9358,6 +9498,7 @@
      *         otherwise
      *
      * @see #setHorizontalFadingEdgeEnabled(boolean)
+     *
      * @attr ref android.R.styleable#View_requiresFadingEdge
      */
     public boolean isHorizontalFadingEdgeEnabled() {
@@ -9373,6 +9514,7 @@
      *                                    horizontally
      *
      * @see #isHorizontalFadingEdgeEnabled()
+     *
      * @attr ref android.R.styleable#View_requiresFadingEdge
      */
     public void setHorizontalFadingEdgeEnabled(boolean horizontalFadingEdgeEnabled) {
@@ -9393,6 +9535,7 @@
      *         otherwise
      *
      * @see #setVerticalFadingEdgeEnabled(boolean)
+     *
      * @attr ref android.R.styleable#View_requiresFadingEdge
      */
     public boolean isVerticalFadingEdgeEnabled() {
@@ -9408,6 +9551,7 @@
      *                                  vertically
      *
      * @see #isVerticalFadingEdgeEnabled()
+     *
      * @attr ref android.R.styleable#View_requiresFadingEdge
      */
     public void setVerticalFadingEdgeEnabled(boolean verticalFadingEdgeEnabled) {
@@ -9550,6 +9694,7 @@
      *
      * @param fadeScrollbars wheter to enable fading
      *
+     * @attr ref android.R.styleable#View_fadeScrollbars
      */
     public void setScrollbarFadingEnabled(boolean fadeScrollbars) {
         initScrollCache();
@@ -9567,12 +9712,86 @@
      * Returns true if scrollbars will fade when this view is not scrolling
      *
      * @return true if scrollbar fading is enabled
+     *
+     * @attr ref android.R.styleable#View_fadeScrollbars
      */
     public boolean isScrollbarFadingEnabled() {
         return mScrollCache != null && mScrollCache.fadeScrollBars;
     }
 
     /**
+     *
+     * Returns the delay before scrollbars fade.
+     *
+     * @return the delay before scrollbars fade
+     *
+     * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade
+     */
+    public int getScrollBarDefaultDelayBeforeFade() {
+        return mScrollCache == null ? ViewConfiguration.getScrollDefaultDelay() :
+                mScrollCache.scrollBarDefaultDelayBeforeFade;
+    }
+
+    /**
+     * Define the delay before scrollbars fade.
+     *
+     * @param scrollBarDefaultDelayBeforeFade - the delay before scrollbars fade
+     *
+     * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade
+     */
+    public void setScrollBarDefaultDelayBeforeFade(int scrollBarDefaultDelayBeforeFade) {
+        getScrollCache().scrollBarDefaultDelayBeforeFade = scrollBarDefaultDelayBeforeFade;
+    }
+
+    /**
+     *
+     * Returns the scrollbar fade duration.
+     *
+     * @return the scrollbar fade duration
+     *
+     * @attr ref android.R.styleable#View_scrollbarFadeDuration
+     */
+    public int getScrollBarFadeDuration() {
+        return mScrollCache == null ? ViewConfiguration.getScrollBarFadeDuration() :
+                mScrollCache.scrollBarFadeDuration;
+    }
+
+    /**
+     * Define the scrollbar fade duration.
+     *
+     * @param scrollBarFadeDuration - the scrollbar fade duration
+     *
+     * @attr ref android.R.styleable#View_scrollbarFadeDuration
+     */
+    public void setScrollBarFadeDuration(int scrollBarFadeDuration) {
+        getScrollCache().scrollBarFadeDuration = scrollBarFadeDuration;
+    }
+
+    /**
+     *
+     * Returns the scrollbar size.
+     *
+     * @return the scrollbar size
+     *
+     * @attr ref android.R.styleable#View_scrollbarSize
+     */
+    public int getScrollBarSize() {
+        return mScrollCache == null ? ViewConfiguration.getScrollBarSize() :
+                mScrollCache.scrollBarSize;
+    }
+
+    /**
+     * Define the scrollbar size.
+     *
+     * @param scrollBarSize - the scrollbar size
+     *
+     * @attr ref android.R.styleable#View_scrollbarSize
+     */
+    public void setScrollBarSize(int scrollBarSize) {
+        getScrollCache().scrollBarSize = scrollBarSize;
+    }
+
+    /**
      * <p>Specify the style of the scrollbars. The scrollbars can be overlaid or
      * inset. When inset, they add to the padding of the view. And the scrollbars
      * can be drawn inside the padding area or on the edge of the view. For example,
@@ -9588,6 +9807,8 @@
      * @see #SCROLLBARS_INSIDE_INSET
      * @see #SCROLLBARS_OUTSIDE_OVERLAY
      * @see #SCROLLBARS_OUTSIDE_INSET
+     *
+     * @attr ref android.R.styleable#View_scrollbarStyle
      */
     public void setScrollBarStyle(int style) {
         if (style != (mViewFlags & SCROLLBARS_STYLE_MASK)) {
@@ -9604,6 +9825,8 @@
      * @see #SCROLLBARS_INSIDE_INSET
      * @see #SCROLLBARS_OUTSIDE_OVERLAY
      * @see #SCROLLBARS_OUTSIDE_INSET
+     *
+     * @attr ref android.R.styleable#View_scrollbarStyle
      */
     @ViewDebug.ExportedProperty(mapping = {
             @ViewDebug.IntToString(from = SCROLLBARS_INSIDE_OVERLAY, to = "INSIDE_OVERLAY"),
@@ -9989,6 +10212,7 @@
         resolveLayoutDirection();
         resolvePadding();
         resolveTextDirection();
+        resolveTextAlignment();
         if (isFocused()) {
             InputMethodManager imm = InputMethodManager.peekInstance();
             imm.focusIn(this);
@@ -10218,6 +10442,7 @@
         mCurrentAnimation = null;
 
         resetResolvedLayoutDirection();
+        resetResolvedTextAlignment();
     }
 
     /**
@@ -10348,9 +10573,9 @@
      *
      * @param container The SparseArray in which to save the view's state.
      *
-     * @see #restoreHierarchyState(android.util.SparseArray) 
-     * @see #dispatchSaveInstanceState(android.util.SparseArray) 
-     * @see #onSaveInstanceState() 
+     * @see #restoreHierarchyState(android.util.SparseArray)
+     * @see #dispatchSaveInstanceState(android.util.SparseArray)
+     * @see #onSaveInstanceState()
      */
     public void saveHierarchyState(SparseArray<Parcelable> container) {
         dispatchSaveInstanceState(container);
@@ -10363,9 +10588,9 @@
      *
      * @param container The SparseArray in which to save the view's state.
      *
-     * @see #dispatchRestoreInstanceState(android.util.SparseArray) 
-     * @see #saveHierarchyState(android.util.SparseArray) 
-     * @see #onSaveInstanceState() 
+     * @see #dispatchRestoreInstanceState(android.util.SparseArray)
+     * @see #saveHierarchyState(android.util.SparseArray)
+     * @see #onSaveInstanceState()
      */
     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
         if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
@@ -10399,9 +10624,9 @@
      * @return Returns a Parcelable object containing the view's current dynamic
      *         state, or null if there is nothing interesting to save. The
      *         default implementation returns null.
-     * @see #onRestoreInstanceState(android.os.Parcelable) 
-     * @see #saveHierarchyState(android.util.SparseArray) 
-     * @see #dispatchSaveInstanceState(android.util.SparseArray) 
+     * @see #onRestoreInstanceState(android.os.Parcelable)
+     * @see #saveHierarchyState(android.util.SparseArray)
+     * @see #dispatchSaveInstanceState(android.util.SparseArray)
      * @see #setSaveEnabled(boolean)
      */
     protected Parcelable onSaveInstanceState() {
@@ -10414,9 +10639,9 @@
      *
      * @param container The SparseArray which holds previously frozen states.
      *
-     * @see #saveHierarchyState(android.util.SparseArray) 
-     * @see #dispatchRestoreInstanceState(android.util.SparseArray) 
-     * @see #onRestoreInstanceState(android.os.Parcelable) 
+     * @see #saveHierarchyState(android.util.SparseArray)
+     * @see #dispatchRestoreInstanceState(android.util.SparseArray)
+     * @see #onRestoreInstanceState(android.os.Parcelable)
      */
     public void restoreHierarchyState(SparseArray<Parcelable> container) {
         dispatchRestoreInstanceState(container);
@@ -10430,9 +10655,9 @@
      *
      * @param container The SparseArray which holds previously saved state.
      *
-     * @see #dispatchSaveInstanceState(android.util.SparseArray) 
-     * @see #restoreHierarchyState(android.util.SparseArray) 
-     * @see #onRestoreInstanceState(android.os.Parcelable) 
+     * @see #dispatchSaveInstanceState(android.util.SparseArray)
+     * @see #restoreHierarchyState(android.util.SparseArray)
+     * @see #onRestoreInstanceState(android.os.Parcelable)
      */
     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
         if (mID != NO_ID) {
@@ -10458,9 +10683,9 @@
      * @param state The frozen state that had previously been returned by
      *        {@link #onSaveInstanceState}.
      *
-     * @see #onSaveInstanceState() 
-     * @see #restoreHierarchyState(android.util.SparseArray) 
-     * @see #dispatchRestoreInstanceState(android.util.SparseArray) 
+     * @see #onSaveInstanceState()
+     * @see #restoreHierarchyState(android.util.SparseArray)
+     * @see #dispatchRestoreInstanceState(android.util.SparseArray)
      */
     protected void onRestoreInstanceState(Parcelable state) {
         mPrivateFlags |= SAVE_STATE_CALLED;
@@ -10614,7 +10839,7 @@
      *         {@link #LAYER_TYPE_HARDWARE}
      *
      * @see #setLayerType(int, android.graphics.Paint)
-     * @see #buildLayer() 
+     * @see #buildLayer()
      * @see #LAYER_TYPE_NONE
      * @see #LAYER_TYPE_SOFTWARE
      * @see #LAYER_TYPE_HARDWARE
@@ -10627,14 +10852,14 @@
      * Forces this view's layer to be created and this view to be rendered
      * into its layer. If this view's layer type is set to {@link #LAYER_TYPE_NONE},
      * invoking this method will have no effect.
-     * 
+     *
      * This method can for instance be used to render a view into its layer before
      * starting an animation. If this view is complex, rendering into the layer
      * before starting the animation will avoid skipping frames.
-     * 
+     *
      * @throws IllegalStateException If this view is not attached to a window
-     * 
-     * @see #setLayerType(int, android.graphics.Paint) 
+     *
+     * @see #setLayerType(int, android.graphics.Paint)
      */
     public void buildLayer() {
         if (mLayerType == LAYER_TYPE_NONE) return;
@@ -10656,7 +10881,7 @@
                 break;
         }
     }
-    
+
     // Make sure the HardwareRenderer.validate() was invoked before calling this method
     void flushLayer() {
         if (mLayerType == LAYER_TYPE_HARDWARE && mHardwareLayer != null) {
@@ -10675,7 +10900,7 @@
                 !mAttachInfo.mHardwareRenderer.isEnabled()) {
             return null;
         }
-        
+
         if (!mAttachInfo.mHardwareRenderer.validate()) return null;
 
         final int width = mRight - mLeft;
@@ -10709,10 +10934,10 @@
 
     /**
      * Destroys this View's hardware layer if possible.
-     * 
+     *
      * @return True if the layer was destroyed, false otherwise.
-     * 
-     * @see #setLayerType(int, android.graphics.Paint) 
+     *
+     * @see #setLayerType(int, android.graphics.Paint)
      * @see #LAYER_TYPE_HARDWARE
      */
     boolean destroyLayer(boolean valid) {
@@ -10736,11 +10961,11 @@
      * Destroys all hardware rendering resources. This method is invoked
      * when the system needs to reclaim resources. Upon execution of this
      * method, you should free any OpenGL resources created by the view.
-     * 
+     *
      * Note: you <strong>must</strong> call
      * <code>super.destroyHardwareResources()</code> when overriding
      * this method.
-     * 
+     *
      * @hide
      */
     protected void destroyHardwareResources() {
@@ -11451,17 +11676,17 @@
         if (offsetRequired) top += getTopPaddingOffset();
         return top;
     }
-    
+
     /**
      * @hide
      * @param offsetRequired
      */
     protected int getFadeHeight(boolean offsetRequired) {
         int padding = mPaddingTop;
-        if (offsetRequired) padding += getTopPaddingOffset();        
+        if (offsetRequired) padding += getTopPaddingOffset();
         return mBottom - mTop - mPaddingBottom - padding;
     }
-    
+
     /**
      * <p>Indicates whether this view is attached to a hardware accelerated
      * window or not.</p>
@@ -11962,7 +12187,7 @@
         int saveCount;
 
         if (!dirtyOpaque) {
-            final Drawable background = mBGDrawable;
+            final Drawable background = mBackground;
             if (background != null) {
                 final int scrollX = mScrollX;
                 final int scrollY = mScrollY;
@@ -12036,7 +12261,7 @@
         }
 
         final ScrollabilityCache scrollabilityCache = mScrollCache;
-        final float fadeHeight = scrollabilityCache.fadingEdgeLength;        
+        final float fadeHeight = scrollabilityCache.fadingEdgeLength;
         int length = (int) fadeHeight;
 
         // clip the fade length if top and bottom fades overlap
@@ -12143,8 +12368,8 @@
      * optimize the drawing of the fading edges. If you do return a non-zero color, the alpha
      * should be set to 0xFF.
      *
-     * @see #setVerticalFadingEdgeEnabled(boolean) 
-     * @see #setHorizontalFadingEdgeEnabled(boolean) 
+     * @see #setVerticalFadingEdgeEnabled(boolean)
+     * @see #setHorizontalFadingEdgeEnabled(boolean)
      *
      * @return The known solid color background for this view, or 0 if the color may vary
      */
@@ -12493,7 +12718,7 @@
     * @param who the Drawable to query
     */
     public int getResolvedLayoutDirection(Drawable who) {
-        return (who == mBGDrawable) ? getResolvedLayoutDirection() : LAYOUT_DIRECTION_DEFAULT;
+        return (who == mBackground) ? getResolvedLayoutDirection() : LAYOUT_DIRECTION_DEFAULT;
     }
 
     /**
@@ -12512,11 +12737,11 @@
      * @return boolean If true than the Drawable is being displayed in the
      *         view; else false and it is not allowed to animate.
      *
-     * @see #unscheduleDrawable(android.graphics.drawable.Drawable) 
-     * @see #drawableStateChanged() 
+     * @see #unscheduleDrawable(android.graphics.drawable.Drawable)
+     * @see #drawableStateChanged()
      */
     protected boolean verifyDrawable(Drawable who) {
-        return who == mBGDrawable;
+        return who == mBackground;
     }
 
     /**
@@ -12526,10 +12751,10 @@
      * <p>Be sure to call through to the superclass when overriding this
      * function.
      *
-     * @see Drawable#setState(int[]) 
+     * @see Drawable#setState(int[])
      */
     protected void drawableStateChanged() {
-        Drawable d = mBGDrawable;
+        Drawable d = mBackground;
         if (d != null && d.isStateful()) {
             d.setState(getDrawableState());
         }
@@ -12559,9 +12784,9 @@
      *
      * @return The current drawable state
      *
-     * @see Drawable#setState(int[]) 
-     * @see #drawableStateChanged() 
-     * @see #onCreateDrawableState(int) 
+     * @see Drawable#setState(int[])
+     * @see #drawableStateChanged()
+     * @see #onCreateDrawableState(int)
      */
     public final int[] getDrawableState() {
         if ((mDrawableState != null) && ((mPrivateFlags & DRAWABLE_STATE_DIRTY) == 0)) {
@@ -12586,7 +12811,7 @@
      * @return Returns an array holding the current {@link Drawable} state of
      * the view.
      *
-     * @see #mergeDrawableStates(int[], int[]) 
+     * @see #mergeDrawableStates(int[], int[])
      */
     protected int[] onCreateDrawableState(int extraSpace) {
         if ((mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE &&
@@ -12662,7 +12887,7 @@
      * @return As a convenience, the <var>baseState</var> array you originally
      * passed into the function is returned.
      *
-     * @see #onCreateDrawableState(int) 
+     * @see #onCreateDrawableState(int)
      */
     protected static int[] mergeDrawableStates(int[] baseState, int[] additionalState) {
         final int N = baseState.length;
@@ -12679,8 +12904,8 @@
      * on all Drawable objects associated with this view.
      */
     public void jumpDrawablesToCurrentState() {
-        if (mBGDrawable != null) {
-            mBGDrawable.jumpToCurrentState();
+        if (mBackground != null) {
+            mBackground.jumpToCurrentState();
         }
     }
 
@@ -12690,10 +12915,10 @@
      */
     @RemotableViewMethod
     public void setBackgroundColor(int color) {
-        if (mBGDrawable instanceof ColorDrawable) {
-            ((ColorDrawable) mBGDrawable).setColor(color);
+        if (mBackground instanceof ColorDrawable) {
+            ((ColorDrawable) mBackground).setColor(color);
         } else {
-            setBackgroundDrawable(new ColorDrawable(color));
+            setBackground(new ColorDrawable(color));
         }
     }
 
@@ -12701,6 +12926,7 @@
      * Set the background to a given resource. The resource should refer to
      * a Drawable object or 0 to remove the background.
      * @param resid The identifier of the resource.
+     *
      * @attr ref android.R.styleable#View_background
      */
     @RemotableViewMethod
@@ -12713,7 +12939,7 @@
         if (resid != 0) {
             d = mResources.getDrawable(resid);
         }
-        setBackgroundDrawable(d);
+        setBackground(d);
 
         mBackgroundResource = resid;
     }
@@ -12725,11 +12951,19 @@
      * touched. If setting the padding is desired, please use
      * {@link #setPadding(int, int, int, int)}.
      *
-     * @param d The Drawable to use as the background, or null to remove the
+     * @param background The Drawable to use as the background, or null to remove the
      *        background
      */
-    public void setBackgroundDrawable(Drawable d) {
-        if (d == mBGDrawable) {
+    public void setBackground(Drawable background) {
+        setBackgroundDrawable(background);
+    }
+
+    /**
+     * @deprecated use {@link #setBackground(Drawable)} instead
+     */
+    @Deprecated
+    public void setBackgroundDrawable(Drawable background) {
+        if (background == mBackground) {
             return;
         }
 
@@ -12741,19 +12975,19 @@
          * Regardless of whether we're setting a new background or not, we want
          * to clear the previous drawable.
          */
-        if (mBGDrawable != null) {
-            mBGDrawable.setCallback(null);
-            unscheduleDrawable(mBGDrawable);
+        if (mBackground != null) {
+            mBackground.setCallback(null);
+            unscheduleDrawable(mBackground);
         }
 
-        if (d != null) {
+        if (background != null) {
             Rect padding = sThreadLocal.get();
             if (padding == null) {
                 padding = new Rect();
                 sThreadLocal.set(padding);
             }
-            if (d.getPadding(padding)) {
-                switch (d.getResolvedLayoutDirectionSelf()) {
+            if (background.getPadding(padding)) {
+                switch (background.getResolvedLayoutDirectionSelf()) {
                     case LAYOUT_DIRECTION_RTL:
                         setPadding(padding.right, padding.top, padding.left, padding.bottom);
                         break;
@@ -12765,17 +12999,17 @@
 
             // Compare the minimum sizes of the old Drawable and the new.  If there isn't an old or
             // if it has a different minimum size, we should layout again
-            if (mBGDrawable == null || mBGDrawable.getMinimumHeight() != d.getMinimumHeight() ||
-                    mBGDrawable.getMinimumWidth() != d.getMinimumWidth()) {
+            if (mBackground == null || mBackground.getMinimumHeight() != background.getMinimumHeight() ||
+                    mBackground.getMinimumWidth() != background.getMinimumWidth()) {
                 requestLayout = true;
             }
 
-            d.setCallback(this);
-            if (d.isStateful()) {
-                d.setState(getDrawableState());
+            background.setCallback(this);
+            if (background.isStateful()) {
+                background.setState(getDrawableState());
             }
-            d.setVisible(getVisibility() == VISIBLE, false);
-            mBGDrawable = d;
+            background.setVisible(getVisibility() == VISIBLE, false);
+            mBackground = background;
 
             if ((mPrivateFlags & SKIP_DRAW) != 0) {
                 mPrivateFlags &= ~SKIP_DRAW;
@@ -12784,7 +13018,7 @@
             }
         } else {
             /* Remove the background */
-            mBGDrawable = null;
+            mBackground = null;
 
             if ((mPrivateFlags & ONLY_DRAWS_BACKGROUND) != 0) {
                 /*
@@ -12820,10 +13054,15 @@
 
     /**
      * Gets the background drawable
+     *
      * @return The drawable used as the background for this view, if any.
+     *
+     * @see #setBackground(Drawable)
+     *
+     * @attr ref android.R.styleable#View_background
      */
     public Drawable getBackground() {
-        return mBGDrawable;
+        return mBackground;
     }
 
     /**
@@ -13364,8 +13603,8 @@
      * number.
      *
      * @see #NO_ID
-     * @see #getId() 
-     * @see #findViewById(int) 
+     * @see #getId()
+     * @see #findViewById(int)
      *
      * @param id a number used to identify the view
      *
@@ -13404,8 +13643,8 @@
      * @return a positive integer used to identify the view or {@link #NO_ID}
      *         if the view has no ID
      *
-     * @see #setId(int) 
-     * @see #findViewById(int) 
+     * @see #setId(int)
+     * @see #findViewById(int)
      * @attr ref android.R.styleable#View_id
      */
     @ViewDebug.CapturedViewProperty
@@ -13917,16 +14156,8 @@
      * @return The suggested minimum height of the view.
      */
     protected int getSuggestedMinimumHeight() {
-        int suggestedMinHeight = mMinHeight;
+        return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
 
-        if (mBGDrawable != null) {
-            final int bgMinHeight = mBGDrawable.getMinimumHeight();
-            if (suggestedMinHeight < bgMinHeight) {
-                suggestedMinHeight = bgMinHeight;
-            }
-        }
-
-        return suggestedMinHeight;
     }
 
     /**
@@ -13941,16 +14172,20 @@
      * @return The suggested minimum width of the view.
      */
     protected int getSuggestedMinimumWidth() {
-        int suggestedMinWidth = mMinWidth;
+        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
+    }
 
-        if (mBGDrawable != null) {
-            final int bgMinWidth = mBGDrawable.getMinimumWidth();
-            if (suggestedMinWidth < bgMinWidth) {
-                suggestedMinWidth = bgMinWidth;
-            }
-        }
-
-        return suggestedMinWidth;
+    /**
+     * Returns the minimum height of the view.
+     *
+     * @return the minimum height the view will try to be.
+     *
+     * @see #setMinimumHeight(int)
+     *
+     * @attr ref android.R.styleable#View_minHeight
+     */
+    public int getMinimumHeight() {
+        return mMinHeight;
     }
 
     /**
@@ -13959,9 +14194,27 @@
      * constrains it with less available height).
      *
      * @param minHeight The minimum height the view will try to be.
+     *
+     * @see #getMinimumHeight()
+     *
+     * @attr ref android.R.styleable#View_minHeight
      */
     public void setMinimumHeight(int minHeight) {
         mMinHeight = minHeight;
+        requestLayout();
+    }
+
+    /**
+     * Returns the minimum width of the view.
+     *
+     * @return the minimum width the view will try to be.
+     *
+     * @see #setMinimumWidth(int)
+     *
+     * @attr ref android.R.styleable#View_minWidth
+     */
+    public int getMinimumWidth() {
+        return mMinWidth;
     }
 
     /**
@@ -13970,9 +14223,15 @@
      * constrains it with less available width).
      *
      * @param minWidth The minimum width the view will try to be.
+     *
+     * @see #getMinimumWidth()
+     *
+     * @attr ref android.R.styleable#View_minWidth
      */
     public void setMinimumWidth(int minWidth) {
         mMinWidth = minWidth;
+        requestLayout();
+
     }
 
     /**
@@ -14091,11 +14350,11 @@
                 getLocationInWindow(location);
                 region.op(location[0], location[1], location[0] + mRight - mLeft,
                         location[1] + mBottom - mTop, Region.Op.DIFFERENCE);
-            } else if ((pflags & ONLY_DRAWS_BACKGROUND) != 0 && mBGDrawable != null) {
+            } else if ((pflags & ONLY_DRAWS_BACKGROUND) != 0 && mBackground != null) {
                 // The ONLY_DRAWS_BACKGROUND flag IS set and the background drawable
                 // exists, so we remove the background drawable's non-transparent
                 // parts from this transparent region.
-                applyDrawableToTransparentRegion(mBGDrawable, region);
+                applyDrawableToTransparentRegion(mBackground, region);
             }
         }
         return true;
@@ -14766,7 +15025,7 @@
      * {@link #TEXT_DIRECTION_ANY_RTL},
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
-     * {@link #TEXT_DIRECTION_LOCALE},
+     * {@link #TEXT_DIRECTION_LOCALE}
      */
     @ViewDebug.ExportedProperty(category = "text", mapping = {
             @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
@@ -14790,7 +15049,7 @@
      * {@link #TEXT_DIRECTION_ANY_RTL},
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
-     * {@link #TEXT_DIRECTION_LOCALE},
+     * {@link #TEXT_DIRECTION_LOCALE}
      */
     public void setTextDirection(int textDirection) {
         if (getTextDirection() != textDirection) {
@@ -14799,6 +15058,7 @@
             resetResolvedTextDirection();
             // Set the new text direction
             mPrivateFlags2 |= ((textDirection << TEXT_DIRECTION_MASK_SHIFT) & TEXT_DIRECTION_MASK);
+            // Refresh
             requestLayout();
             invalidate(true);
         }
@@ -14818,7 +15078,7 @@
      * {@link #TEXT_DIRECTION_ANY_RTL},
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
-     * {@link #TEXT_DIRECTION_LOCALE},
+     * {@link #TEXT_DIRECTION_LOCALE}
      */
     public int getResolvedTextDirection() {
         // The text direction will be resolved only if needed
@@ -14927,6 +15187,199 @@
     public void onResolvedTextDirectionReset() {
     }
 
+    /**
+     * Return the value specifying the text alignment or policy that was set with
+     * {@link #setTextAlignment(int)}.
+     *
+     * @return the defined text alignment. It can be one of:
+     *
+     * {@link #TEXT_ALIGNMENT_INHERIT},
+     * {@link #TEXT_ALIGNMENT_GRAVITY},
+     * {@link #TEXT_ALIGNMENT_CENTER},
+     * {@link #TEXT_ALIGNMENT_TEXT_START},
+     * {@link #TEXT_ALIGNMENT_TEXT_END},
+     * {@link #TEXT_ALIGNMENT_VIEW_START},
+     * {@link #TEXT_ALIGNMENT_VIEW_END}
+     */
+    @ViewDebug.ExportedProperty(category = "text", mapping = {
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"),
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_GRAVITY, to = "GRAVITY"),
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_START, to = "TEXT_START"),
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_END, to = "TEXT_END"),
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_CENTER, to = "CENTER"),
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"),
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END")
+    })
+    public int getTextAlignment() {
+        return (mPrivateFlags2 & TEXT_ALIGNMENT_MASK) >> TEXT_ALIGNMENT_MASK_SHIFT;
+    }
+
+    /**
+     * Set the text alignment.
+     *
+     * @param textAlignment The text alignment to set. Should be one of
+     *
+     * {@link #TEXT_ALIGNMENT_INHERIT},
+     * {@link #TEXT_ALIGNMENT_GRAVITY},
+     * {@link #TEXT_ALIGNMENT_CENTER},
+     * {@link #TEXT_ALIGNMENT_TEXT_START},
+     * {@link #TEXT_ALIGNMENT_TEXT_END},
+     * {@link #TEXT_ALIGNMENT_VIEW_START},
+     * {@link #TEXT_ALIGNMENT_VIEW_END}
+     *
+     * @attr ref android.R.styleable#View_textAlignment
+     */
+    public void setTextAlignment(int textAlignment) {
+        if (textAlignment != getTextAlignment()) {
+            // Reset the current and resolved text alignment
+            mPrivateFlags2 &= ~TEXT_ALIGNMENT_MASK;
+            resetResolvedTextAlignment();
+            // Set the new text alignment
+            mPrivateFlags2 |= ((textAlignment << TEXT_ALIGNMENT_MASK_SHIFT) & TEXT_ALIGNMENT_MASK);
+            // Refresh
+            requestLayout();
+            invalidate(true);
+        }
+    }
+
+    /**
+     * Return the resolved text alignment.
+     *
+     * The resolved text alignment. This needs resolution if the value is
+     * TEXT_ALIGNMENT_INHERIT. The resolution matches {@link #setTextAlignment(int)}  if it is
+     * not TEXT_ALIGNMENT_INHERIT, otherwise resolution proceeds up the parent chain of the view.
+     *
+     * @return the resolved text alignment. Returns one of:
+     *
+     * {@link #TEXT_ALIGNMENT_GRAVITY},
+     * {@link #TEXT_ALIGNMENT_CENTER},
+     * {@link #TEXT_ALIGNMENT_TEXT_START},
+     * {@link #TEXT_ALIGNMENT_TEXT_END},
+     * {@link #TEXT_ALIGNMENT_VIEW_START},
+     * {@link #TEXT_ALIGNMENT_VIEW_END}
+     */
+    @ViewDebug.ExportedProperty(category = "text", mapping = {
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"),
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_GRAVITY, to = "GRAVITY"),
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_START, to = "TEXT_START"),
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_END, to = "TEXT_END"),
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_CENTER, to = "CENTER"),
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"),
+            @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END")
+    })
+    public int getResolvedTextAlignment() {
+        // If text alignment is not resolved, then resolve it
+        if ((mPrivateFlags2 & TEXT_ALIGNMENT_RESOLVED) != TEXT_ALIGNMENT_RESOLVED) {
+            resolveTextAlignment();
+        }
+        return (mPrivateFlags2 & TEXT_ALIGNMENT_RESOLVED_MASK) >> TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+    }
+
+    /**
+     * Resolve the text alignment. Will call {@link View#onResolvedTextAlignmentChanged} when
+     * resolution is done.
+     */
+    public void resolveTextAlignment() {
+        // Reset any previous text alignment resolution
+        mPrivateFlags2 &= ~(TEXT_ALIGNMENT_RESOLVED | TEXT_ALIGNMENT_RESOLVED_MASK);
+
+        if (hasRtlSupport()) {
+            // Set resolved text alignment flag depending on text alignment flag
+            final int textAlignment = getTextAlignment();
+            switch (textAlignment) {
+                case TEXT_ALIGNMENT_INHERIT:
+                    // Check if we can resolve the text alignment
+                    if (canResolveLayoutDirection() && mParent instanceof View) {
+                        View view = (View) mParent;
+
+                        final int parentResolvedTextAlignment = view.getResolvedTextAlignment();
+                        switch (parentResolvedTextAlignment) {
+                            case TEXT_ALIGNMENT_GRAVITY:
+                            case TEXT_ALIGNMENT_TEXT_START:
+                            case TEXT_ALIGNMENT_TEXT_END:
+                            case TEXT_ALIGNMENT_CENTER:
+                            case TEXT_ALIGNMENT_VIEW_START:
+                            case TEXT_ALIGNMENT_VIEW_END:
+                                // Resolved text alignment is the same as the parent resolved
+                                // text alignment
+                                mPrivateFlags2 |=
+                                        (parentResolvedTextAlignment << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT);
+                                break;
+                            default:
+                                // Use default resolved text alignment
+                                mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+                        }
+                    }
+                    else {
+                        // We cannot do the resolution if there is no parent so use the default
+                        mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+                    }
+                    break;
+                case TEXT_ALIGNMENT_GRAVITY:
+                case TEXT_ALIGNMENT_TEXT_START:
+                case TEXT_ALIGNMENT_TEXT_END:
+                case TEXT_ALIGNMENT_CENTER:
+                case TEXT_ALIGNMENT_VIEW_START:
+                case TEXT_ALIGNMENT_VIEW_END:
+                    // Resolved text alignment is the same as text alignment
+                    mPrivateFlags2 |= (textAlignment << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT);
+                    break;
+                default:
+                    // Use default resolved text alignment
+                    mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+            }
+        } else {
+            // Use default resolved text alignment
+            mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+        }
+
+        // Set the resolved
+        mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED;
+        onResolvedTextAlignmentChanged();
+    }
+
+    /**
+     * Check if text alignment resolution can be done.
+     *
+     * @return true if text alignment resolution can be done otherwise return false.
+     */
+    public boolean canResolveTextAlignment() {
+        switch (getTextAlignment()) {
+            case TEXT_DIRECTION_INHERIT:
+                return (mParent != null);
+            default:
+                return true;
+        }
+    }
+
+    /**
+     * Called when text alignment has been resolved. Subclasses that care about text alignment
+     * resolution should override this method.
+     *
+     * The default implementation does nothing.
+     */
+    public void onResolvedTextAlignmentChanged() {
+    }
+
+    /**
+     * Reset resolved text alignment. Text alignment can be resolved with a call to
+     * getResolvedTextAlignment(). Will call {@link View#onResolvedTextAlignmentReset} when
+     * reset is done.
+     */
+    public void resetResolvedTextAlignment() {
+        // Reset any previous text alignment resolution
+        mPrivateFlags2 &= ~(TEXT_ALIGNMENT_RESOLVED | TEXT_ALIGNMENT_RESOLVED_MASK);
+        onResolvedTextAlignmentReset();
+    }
+
+    /**
+     * Called when text alignment is reset. Subclasses that care about text alignment reset should
+     * override this method and do a reset of the text alignment of their children. The default
+     * implementation does nothing.
+     */
+    public void onResolvedTextAlignmentReset() {
+    }
+
     //
     // Properties
     //
@@ -15419,7 +15872,7 @@
      * visibility.  This reports <strong>global</strong> changes to the system UI
      * state, not just what the application is requesting.
      *
-     * @see View#setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener) 
+     * @see View#setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener)
      */
     public interface OnSystemUiVisibilityChangeListener {
         /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index d5c783f..121b544 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3375,7 +3375,7 @@
 
         boolean clearChildFocus = false;
         if (view == mFocused) {
-            view.clearFocusForRemoval();
+            view.unFocus();
             clearChildFocus = true;
         }
 
@@ -3398,6 +3398,7 @@
 
         if (clearChildFocus) {
             clearChildFocus(view);
+            ensureInputFocusOnFirstFocusable();
         }
     }
 
@@ -3450,7 +3451,7 @@
             }
 
             if (view == focused) {
-                view.clearFocusForRemoval();
+                view.unFocus();
                 clearChildFocus = view;
             }
 
@@ -3474,6 +3475,7 @@
 
         if (clearChildFocus != null) {
             clearChildFocus(clearChildFocus);
+            ensureInputFocusOnFirstFocusable();
         }
     }
 
@@ -3519,7 +3521,7 @@
             }
 
             if (view == focused) {
-                view.clearFocusForRemoval();
+                view.unFocus();
                 clearChildFocus = view;
             }
 
@@ -3542,6 +3544,7 @@
 
         if (clearChildFocus != null) {
             clearChildFocus(clearChildFocus);
+            ensureInputFocusOnFirstFocusable();
         }
     }
 
@@ -5056,6 +5059,18 @@
         }
     }
 
+    @Override
+    public void onResolvedTextAlignmentReset() {
+        // Take care of resetting the children resolution too
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            if (child.getTextAlignment() == TEXT_ALIGNMENT_INHERIT) {
+                child.resetResolvedTextAlignment();
+            }
+        }
+    }
+
     /**
      * Return true if the pressed state should be delayed for children or descendants of this
      * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d72f3b7..899fb32 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2039,9 +2039,10 @@
 
         scrollToRectOrFocus(null, false);
 
-        if (mAttachInfo.mViewScrollChanged) {
-            mAttachInfo.mViewScrollChanged = false;
-            mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
+        final AttachInfo attachInfo = mAttachInfo;
+        if (attachInfo.mViewScrollChanged) {
+            attachInfo.mViewScrollChanged = false;
+            attachInfo.mTreeObserver.dispatchOnScrollChanged();
         }
 
         int yoff;
@@ -2056,8 +2057,8 @@
             fullRedrawNeeded = true;
         }
 
-        final float appScale = mAttachInfo.mApplicationScale;
-        final boolean scalingRequired = mAttachInfo.mScalingRequired;
+        final float appScale = attachInfo.mApplicationScale;
+        final boolean scalingRequired = attachInfo.mScalingRequired;
 
         int resizeAlpha = 0;
         if (mResizeBuffer != null) {
@@ -2086,7 +2087,7 @@
         }
 
         if (fullRedrawNeeded) {
-            mAttachInfo.mIgnoreDirtyState = true;
+            attachInfo.mIgnoreDirtyState = true;
             dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
         }
 
@@ -2099,8 +2100,10 @@
                     appScale + ", width=" + mWidth + ", height=" + mHeight);
         }
 
+        attachInfo.mTreeObserver.dispatchOnDraw();
+
         if (!dirty.isEmpty() || mIsAnimating) {
-            if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
+            if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {
                 // Draw with hardware renderer.
                 mIsAnimating = false;
                 mHardwareYOffset = yoff;
@@ -2111,147 +2114,12 @@
                 mPreviousDirty.set(dirty);
                 dirty.setEmpty();
 
-                if (mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this,
+                if (attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,
                         animating ? null : mCurrentDirty)) {
                     mPreviousDirty.set(0, 0, mWidth, mHeight);
                 }
-            } else {
-                // Draw with software renderer.
-                Canvas canvas;
-                try {
-                    int left = dirty.left;
-                    int top = dirty.top;
-                    int right = dirty.right;
-                    int bottom = dirty.bottom;
-
-                    final long lockCanvasStartTime;
-                    if (ViewDebug.DEBUG_LATENCY) {
-                        lockCanvasStartTime = System.nanoTime();
-                    }
-
-                    canvas = mSurface.lockCanvas(dirty);
-
-                    if (ViewDebug.DEBUG_LATENCY) {
-                        long now = System.nanoTime();
-                        Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- lockCanvas() took "
-                                + ((now - lockCanvasStartTime) * 0.000001f) + "ms");
-                    }
-
-                    if (left != dirty.left || top != dirty.top || right != dirty.right ||
-                            bottom != dirty.bottom) {
-                        mAttachInfo.mIgnoreDirtyState = true;
-                    }
-
-                    // TODO: Do this in native
-                    canvas.setDensity(mDensity);
-                } catch (Surface.OutOfResourcesException e) {
-                    Log.e(TAG, "OutOfResourcesException locking surface", e);
-                    try {
-                        if (!sWindowSession.outOfMemory(mWindow)) {
-                            Slog.w(TAG, "No processes killed for memory; killing self");
-                            Process.killProcess(Process.myPid());
-                        }
-                    } catch (RemoteException ex) {
-                    }
-                    mLayoutRequested = true;    // ask wm for a new surface next time.
-                    return;
-                } catch (IllegalArgumentException e) {
-                    Log.e(TAG, "IllegalArgumentException locking surface", e);
-                    // Don't assume this is due to out of memory, it could be
-                    // something else, and if it is something else then we could
-                    // kill stuff (or ourself) for no reason.
-                    mLayoutRequested = true;    // ask wm for a new surface next time.
-                    return;
-                }
-
-                try {
-                    if (DEBUG_ORIENTATION || DEBUG_DRAW) {
-                        Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
-                                + canvas.getWidth() + ", h=" + canvas.getHeight());
-                        //canvas.drawARGB(255, 255, 0, 0);
-                    }
-
-                    long startTime = 0L;
-                    if (ViewDebug.DEBUG_PROFILE_DRAWING) {
-                        startTime = SystemClock.elapsedRealtime();
-                    }
-
-                    // If this bitmap's format includes an alpha channel, we
-                    // need to clear it before drawing so that the child will
-                    // properly re-composite its drawing on a transparent
-                    // background. This automatically respects the clip/dirty region
-                    // or
-                    // If we are applying an offset, we need to clear the area
-                    // where the offset doesn't appear to avoid having garbage
-                    // left in the blank areas.
-                    if (!canvas.isOpaque() || yoff != 0) {
-                        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
-                    }
-
-                    dirty.setEmpty();
-                    mIsAnimating = false;
-                    mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
-                    mView.mPrivateFlags |= View.DRAWN;
-
-                    if (DEBUG_DRAW) {
-                        Context cxt = mView.getContext();
-                        Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
-                                ", metrics=" + cxt.getResources().getDisplayMetrics() +
-                                ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
-                    }
-                    try {
-                        canvas.translate(0, -yoff);
-                        if (mTranslator != null) {
-                            mTranslator.translateCanvas(canvas);
-                        }
-                        canvas.setScreenDensity(scalingRequired
-                                ? DisplayMetrics.DENSITY_DEVICE : 0);
-                        mAttachInfo.mSetIgnoreDirtyState = false;
-
-                        final long drawStartTime;
-                        if (ViewDebug.DEBUG_LATENCY) {
-                            drawStartTime = System.nanoTime();
-                        }
-
-                        mView.draw(canvas);
-
-                        if (ViewDebug.DEBUG_LATENCY) {
-                            long now = System.nanoTime();
-                            Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- draw() took "
-                                    + ((now - drawStartTime) * 0.000001f) + "ms");
-                        }
-                    } finally {
-                        if (!mAttachInfo.mSetIgnoreDirtyState) {
-                            // Only clear the flag if it was not set during the mView.draw() call
-                            mAttachInfo.mIgnoreDirtyState = false;
-                        }
-                    }
-
-                    if (false && ViewDebug.consistencyCheckEnabled) {
-                        mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
-                    }
-
-                    if (ViewDebug.DEBUG_PROFILE_DRAWING) {
-                        EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
-                    }
-                } finally {
-                    final long unlockCanvasAndPostStartTime;
-                    if (ViewDebug.DEBUG_LATENCY) {
-                        unlockCanvasAndPostStartTime = System.nanoTime();
-                    }
-
-                    surface.unlockCanvasAndPost(canvas);
-
-                    if (ViewDebug.DEBUG_LATENCY) {
-                        long now = System.nanoTime();
-                        Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- unlockCanvasAndPost() took "
-                                + ((now - unlockCanvasAndPostStartTime) * 0.000001f) + "ms");
-                    }
-
-                    if (LOCAL_LOGV) {
-                        Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
-                    }
-                }
+            } else if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) {
+                return;
             }
         }
 
@@ -2261,6 +2129,151 @@
         }
     }
 
+    /**
+     * @return true if drawing was succesfull, false if an error occurred
+     */
+    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff,
+            boolean scalingRequired, Rect dirty) {
+
+        // Draw with software renderer.
+        Canvas canvas;
+        try {
+            int left = dirty.left;
+            int top = dirty.top;
+            int right = dirty.right;
+            int bottom = dirty.bottom;
+
+            final long lockCanvasStartTime;
+            if (ViewDebug.DEBUG_LATENCY) {
+                lockCanvasStartTime = System.nanoTime();
+            }
+
+            canvas = mSurface.lockCanvas(dirty);
+
+            if (ViewDebug.DEBUG_LATENCY) {
+                long now = System.nanoTime();
+                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- lockCanvas() took "
+                        + ((now - lockCanvasStartTime) * 0.000001f) + "ms");
+            }
+
+            if (left != dirty.left || top != dirty.top || right != dirty.right ||
+                    bottom != dirty.bottom) {
+                attachInfo.mIgnoreDirtyState = true;
+            }
+
+            // TODO: Do this in native
+            canvas.setDensity(mDensity);
+        } catch (Surface.OutOfResourcesException e) {
+            Log.e(TAG, "OutOfResourcesException locking surface", e);
+            try {
+                if (!sWindowSession.outOfMemory(mWindow)) {
+                    Slog.w(TAG, "No processes killed for memory; killing self");
+                    Process.killProcess(Process.myPid());
+                }
+            } catch (RemoteException ex) {
+            }
+            mLayoutRequested = true;    // ask wm for a new surface next time.
+            return false;
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "IllegalArgumentException locking surface", e);
+            // Don't assume this is due to out of memory, it could be
+            // something else, and if it is something else then we could
+            // kill stuff (or ourself) for no reason.
+            mLayoutRequested = true;    // ask wm for a new surface next time.
+            return false;
+        }
+
+        try {
+            if (DEBUG_ORIENTATION || DEBUG_DRAW) {
+                Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
+                        + canvas.getWidth() + ", h=" + canvas.getHeight());
+                //canvas.drawARGB(255, 255, 0, 0);
+            }
+
+            long startTime = 0L;
+            if (ViewDebug.DEBUG_PROFILE_DRAWING) {
+                startTime = SystemClock.elapsedRealtime();
+            }
+
+            // If this bitmap's format includes an alpha channel, we
+            // need to clear it before drawing so that the child will
+            // properly re-composite its drawing on a transparent
+            // background. This automatically respects the clip/dirty region
+            // or
+            // If we are applying an offset, we need to clear the area
+            // where the offset doesn't appear to avoid having garbage
+            // left in the blank areas.
+            if (!canvas.isOpaque() || yoff != 0) {
+                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+            }
+
+            dirty.setEmpty();
+            mIsAnimating = false;
+            attachInfo.mDrawingTime = SystemClock.uptimeMillis();
+            mView.mPrivateFlags |= View.DRAWN;
+
+            if (DEBUG_DRAW) {
+                Context cxt = mView.getContext();
+                Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
+                        ", metrics=" + cxt.getResources().getDisplayMetrics() +
+                        ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
+            }
+            try {
+                canvas.translate(0, -yoff);
+                if (mTranslator != null) {
+                    mTranslator.translateCanvas(canvas);
+                }
+                canvas.setScreenDensity(scalingRequired
+                        ? DisplayMetrics.DENSITY_DEVICE : 0);
+                attachInfo.mSetIgnoreDirtyState = false;
+
+                final long drawStartTime;
+                if (ViewDebug.DEBUG_LATENCY) {
+                    drawStartTime = System.nanoTime();
+                }
+
+                mView.draw(canvas);
+
+                if (ViewDebug.DEBUG_LATENCY) {
+                    long now = System.nanoTime();
+                    Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- draw() took "
+                            + ((now - drawStartTime) * 0.000001f) + "ms");
+                }
+            } finally {
+                if (!attachInfo.mSetIgnoreDirtyState) {
+                    // Only clear the flag if it was not set during the mView.draw() call
+                    attachInfo.mIgnoreDirtyState = false;
+                }
+            }
+
+            if (false && ViewDebug.consistencyCheckEnabled) {
+                mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
+            }
+
+            if (ViewDebug.DEBUG_PROFILE_DRAWING) {
+                EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
+            }
+        } finally {
+            final long unlockCanvasAndPostStartTime;
+            if (ViewDebug.DEBUG_LATENCY) {
+                unlockCanvasAndPostStartTime = System.nanoTime();
+            }
+
+            surface.unlockCanvasAndPost(canvas);
+
+            if (ViewDebug.DEBUG_LATENCY) {
+                long now = System.nanoTime();
+                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- unlockCanvasAndPost() took "
+                        + ((now - unlockCanvasAndPostStartTime) * 0.000001f) + "ms");
+            }
+
+            if (LOCAL_LOGV) {
+                Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
+            }
+        }
+        return true;
+    }
+
     void invalidateDisplayLists() {
         final ArrayList<DisplayList> displayLists = mDisplayLists;
         final int count = displayLists.size();
@@ -3830,30 +3843,33 @@
         if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
         synchronized (this) {
             if (mAdded) {
-                mAdded = false;
                 dispatchDetachedFromWindow();
             }
 
             if (mAdded && !mFirst) {
                 destroyHardwareRenderer();
 
-                int viewVisibility = mView.getVisibility();
-                boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
-                if (mWindowAttributesChanged || viewVisibilityChanged) {
-                    // If layout params have been changed, first give them
-                    // to the window manager to make sure it has the correct
-                    // animation info.
-                    try {
-                        if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
-                                & WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) {
-                            sWindowSession.finishDrawing(mWindow);
+                if (mView != null) {
+                    int viewVisibility = mView.getVisibility();
+                    boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
+                    if (mWindowAttributesChanged || viewVisibilityChanged) {
+                        // If layout params have been changed, first give them
+                        // to the window manager to make sure it has the correct
+                        // animation info.
+                        try {
+                            if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
+                                    & WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) {
+                                sWindowSession.finishDrawing(mWindow);
+                            }
+                        } catch (RemoteException e) {
                         }
-                    } catch (RemoteException e) {
                     }
+    
+                    mSurface.release();
                 }
-
-                mSurface.release();
             }
+
+            mAdded = false;
         }
     }
 
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 7fd3389..1c5d436 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -38,6 +38,7 @@
     private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
     private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners;
     private ArrayList<OnPreDrawListener> mOnPreDrawListeners;
+    private ArrayList<OnDrawListener> mOnDrawListeners;
 
     private boolean mAlive = true;
 
@@ -90,6 +91,27 @@
     }
 
     /**
+     * Interface definition for a callback to be invoked when the view tree is about to be drawn.
+     */
+    public interface OnDrawListener {
+        /**
+         * <p>Callback method to be invoked when the view tree is about to be drawn. At this point,
+         * views cannot be modified in any way.</p>
+         * 
+         * <p>Unlike with {@link OnPreDrawListener}, this method cannot be used to cancel the
+         * current drawing pass.</p>
+         * 
+         * <p>An {@link OnDrawListener} listener <strong>cannot be added or removed</strong>
+         * from this method.</p>
+         *
+         * @see android.view.View#onMeasure
+         * @see android.view.View#onLayout
+         * @see android.view.View#onDraw
+         */
+        public void onDraw();
+    }
+
+    /**
      * Interface definition for a callback to be invoked when the touch mode changes.
      */
     public interface OnTouchModeChangeListener {
@@ -171,11 +193,7 @@
         public void setTouchableInsets(int val) {
             mTouchableInsets = val;
         }
-        
-        public int getTouchableInsets() {
-            return mTouchableInsets;
-        }
-        
+
         int mTouchableInsets;
         
         void reset() {
@@ -184,29 +202,28 @@
             touchableRegion.setEmpty();
             mTouchableInsets = TOUCHABLE_INSETS_FRAME;
         }
-        
+
+        @Override
+        public int hashCode() {
+            int result = contentInsets != null ? contentInsets.hashCode() : 0;
+            result = 31 * result + (visibleInsets != null ? visibleInsets.hashCode() : 0);
+            result = 31 * result + (touchableRegion != null ? touchableRegion.hashCode() : 0);
+            result = 31 * result + mTouchableInsets;
+            return result;
+        }
+
         @Override
         public boolean equals(Object o) {
-            try {
-                if (o == null) {
-                    return false;
-                }
-                InternalInsetsInfo other = (InternalInsetsInfo)o;
-                if (mTouchableInsets != other.mTouchableInsets) {
-                    return false;
-                }
-                if (!contentInsets.equals(other.contentInsets)) {
-                    return false;
-                }
-                if (!visibleInsets.equals(other.visibleInsets)) {
-                    return false;
-                }
-                return touchableRegion.equals(other.touchableRegion);
-            } catch (ClassCastException e) {
-                return false;
-            }
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            InternalInsetsInfo other = (InternalInsetsInfo)o;
+            return mTouchableInsets == other.mTouchableInsets &&
+                    contentInsets.equals(other.contentInsets) &&
+                    visibleInsets.equals(other.visibleInsets) &&
+                    touchableRegion.equals(other.touchableRegion);
         }
-        
+
         void set(InternalInsetsInfo other) {
             contentInsets.set(other.contentInsets);
             visibleInsets.set(other.visibleInsets);
@@ -420,6 +437,44 @@
     }
 
     /**
+     * <p>Register a callback to be invoked when the view tree is about to be drawn.</p>
+     * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
+     * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
+     *
+     * @param listener The callback to add
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     */
+    public void addOnDrawListener(OnDrawListener listener) {
+        checkIsAlive();
+
+        if (mOnDrawListeners == null) {
+            mOnDrawListeners = new ArrayList<OnDrawListener>();
+        }
+
+        mOnDrawListeners.add(listener);
+    }
+
+    /**
+     * <p>Remove a previously installed pre-draw callback.</p>
+     * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
+     * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
+     *
+     * @param victim The callback to remove
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     *
+     * @see #addOnDrawListener(OnDrawListener)
+     */
+    public void removeOnDrawListener(OnDrawListener victim) {
+        checkIsAlive();
+        if (mOnDrawListeners == null) {
+            return;
+        }
+        mOnDrawListeners.remove(victim);
+    }
+
+    /**
      * Register a callback to be invoked when a view has been scrolled.
      *
      * @param listener The callback to add
@@ -601,6 +656,7 @@
      *
      * @return True if the current draw should be canceled and resceduled, false otherwise.
      */
+    @SuppressWarnings("unchecked")
     public final boolean dispatchOnPreDraw() {
         // NOTE: we *must* clone the listener list to perform the dispatching.
         // The clone is a safe guard against listeners that
@@ -619,6 +675,19 @@
     }
 
     /**
+     * Notifies registered listeners that the drawing pass is about to start.
+     */
+    public final void dispatchOnDraw() {
+        if (mOnDrawListeners != null) {
+            final ArrayList<OnDrawListener> listeners = mOnDrawListeners;
+            int numListeners = listeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                listeners.get(i).onDraw();
+            }
+        }
+    }
+
+    /**
      * Notifies registered listeners that the touch mode has changed.
      *
      * @param inTouchMode True if the touch mode is now enabled, false otherwise.
diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java
index 94b46fc..aa3d8d3 100644
--- a/core/java/android/webkit/WebSettingsClassic.java
+++ b/core/java/android/webkit/WebSettingsClassic.java
@@ -33,7 +33,7 @@
  */
 public class WebSettingsClassic extends WebSettings {
     // TODO: Keep this up to date
-    private static final String PREVIOUS_VERSION = "4.0.3";
+    private static final String PREVIOUS_VERSION = "4.0.4";
 
     // WebView associated with this WebSettings.
     private WebViewClassic mWebView;
diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java
index 0370049..9c9eb4b 100644
--- a/core/java/android/widget/Chronometer.java
+++ b/core/java/android/widget/Chronometer.java
@@ -25,6 +25,7 @@
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Slog;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.RemoteViews.RemoteView;
@@ -247,6 +248,7 @@
             }
         }
         setText(text);
+        Slog.v("Chronometer", "updateText: sec=" + seconds + " mFormat=" + mFormat + " text=" + text);
     }
 
     private void updateRunning() {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d2a1755..9867e47 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5340,24 +5340,63 @@
                       physicalWidth, false);
     }
 
+    @Override
+    public void onResolvedLayoutDirectionReset() {
+        if (mLayoutAlignment != null) {
+            int resolvedTextAlignment = getResolvedTextAlignment();
+            if (resolvedTextAlignment == TEXT_ALIGNMENT_VIEW_START ||
+                 resolvedTextAlignment == TEXT_ALIGNMENT_VIEW_END) {
+                mLayoutAlignment = null;
+            }
+        }
+    }
+
     private Layout.Alignment getLayoutAlignment() {
         if (mLayoutAlignment == null) {
-            switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
-                case Gravity.START:
+            int textAlign = getResolvedTextAlignment();
+            switch (textAlign) {
+                case TEXT_ALIGNMENT_GRAVITY:
+                    switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
+                        case Gravity.START:
+                            mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
+                            break;
+                        case Gravity.END:
+                            mLayoutAlignment = Layout.Alignment.ALIGN_OPPOSITE;
+                            break;
+                        case Gravity.LEFT:
+                            mLayoutAlignment = Layout.Alignment.ALIGN_LEFT;
+                            break;
+                        case Gravity.RIGHT:
+                            mLayoutAlignment = Layout.Alignment.ALIGN_RIGHT;
+                            break;
+                        case Gravity.CENTER_HORIZONTAL:
+                            mLayoutAlignment = Layout.Alignment.ALIGN_CENTER;
+                            break;
+                        default:
+                            mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
+                            break;
+                    }
+                    break;
+                case TEXT_ALIGNMENT_TEXT_START:
                     mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
                     break;
-                case Gravity.END:
+                case TEXT_ALIGNMENT_TEXT_END:
                     mLayoutAlignment = Layout.Alignment.ALIGN_OPPOSITE;
                     break;
-                case Gravity.LEFT:
-                    mLayoutAlignment = Layout.Alignment.ALIGN_LEFT;
-                    break;
-                case Gravity.RIGHT:
-                    mLayoutAlignment = Layout.Alignment.ALIGN_RIGHT;
-                    break;
-                case Gravity.CENTER_HORIZONTAL:
+                case TEXT_ALIGNMENT_CENTER:
                     mLayoutAlignment = Layout.Alignment.ALIGN_CENTER;
                     break;
+                case TEXT_ALIGNMENT_VIEW_START:
+                    mLayoutAlignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+                            Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT;
+                    break;
+                case TEXT_ALIGNMENT_VIEW_END:
+                    mLayoutAlignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+                            Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT;
+                    break;
+                case TEXT_ALIGNMENT_INHERIT:
+                    // This should never happen as we have already resolved the text alignment
+                    // but better safe than sorry so we just fall through
                 default:
                     mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
                     break;
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 5e73a5f..3c27caf 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -236,7 +236,7 @@
                                0, 0, width, height, bitmap);

     }

 

-    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL);

+    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL, NULL);

 }

 

 static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,

@@ -248,7 +248,7 @@
         return NULL;

     }

 

-    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL);

+    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL, NULL);

 }

 

 static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {

@@ -407,7 +407,7 @@
     bitmap->unlockPixels();

 

     blob.release();

-    return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, density);

+    return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, NULL, density);

 }

 

 static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,

@@ -485,7 +485,7 @@
         env->ReleaseIntArrayElements(offsetXY, array, 0);

     }

 

-    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL);

+    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL, NULL);

 }

 

 ///////////////////////////////////////////////////////////////////////////////

diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index dcd1d28..dd59444 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -35,6 +35,7 @@
 jfieldID gOptions_mCancelID;
 jfieldID gOptions_bitmapFieldID;
 jfieldID gBitmap_nativeBitmapFieldID;
+jfieldID gBitmap_layoutBoundsFieldID;
 
 #if 0
     #define TRACE_BITMAP(code)  code
@@ -276,7 +277,7 @@
     }
 
     jbyteArray ninePatchChunk = NULL;
-    if (peeker.fPatchIsValid) {
+    if (peeker.fPatch != NULL) {
         if (willScale) {
             scaleNinePatchChunk(peeker.fPatch, scale);
         }
@@ -296,6 +297,18 @@
         env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
     }
 
+    jintArray layoutBounds = NULL;
+    if (peeker.fLayoutBounds != NULL) {
+        layoutBounds = env->NewIntArray(4);
+        if (layoutBounds == NULL) {
+            return nullObjectReturn("layoutBounds == null");
+        }
+
+        env->SetIntArrayRegion(layoutBounds, 0, 4, (jint*) peeker.fLayoutBounds);
+        if (javaBitmap != NULL) {
+            env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds);
+        }
+    }
     // detach bitmap from its autodeleter, since we want to own it now
     adb.detach();
 
@@ -321,7 +334,7 @@
     }
 
     if (padding) {
-        if (peeker.fPatchIsValid) {
+        if (peeker.fPatch != NULL) {
             GraphicsJNI::set_jrect(env, padding,
                     peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop,
                     peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom);
@@ -350,7 +363,7 @@
     }
     // now create the java bitmap
     return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(),
-            isMutable, ninePatchChunk);
+            isMutable, ninePatchChunk, layoutBounds, -1);
 }
 
 static jobject nativeDecodeStreamScaled(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
@@ -576,7 +589,7 @@
     jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
     SkASSERT(bitmap_class);
     gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I");
-
+    gBitmap_layoutBoundsFieldID = getFieldIDCheck(env, bitmap_class, "mLayoutBounds", "[I");
     int ret = AndroidRuntime::registerNativeMethods(env,
                                     "android/graphics/BitmapFactory$Options",
                                     gOptionsMethods,
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 682877a..dd8e84f 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -244,7 +244,7 @@
 
     JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
     jbyteArray buff = allocator->getStorageObjAndReset();
-    return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, -1);
+    return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, NULL, -1);
 }
 
 static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index a1d41ee..d4c7600 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -345,14 +345,14 @@
 ///////////////////////////////////////////////////////////////////////////////////////////
 
 jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
-                                  bool isMutable, jbyteArray ninepatch, int density)
+                                  bool isMutable, jbyteArray ninepatch, jintArray layoutbounds,
+                                  int density)
 {
     SkASSERT(bitmap);
     SkASSERT(bitmap->pixelRef());
-
     jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
             static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)),
-            buffer, isMutable, ninepatch, density);
+            buffer, isMutable, ninepatch, layoutbounds, density);
     hasException(env); // For the side effect of logging.
     return obj;
 }
@@ -360,7 +360,7 @@
 jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
                             jbyteArray ninepatch, int density)
 {
-    return createBitmap(env, bitmap, NULL, isMutable, ninepatch, density);
+    return createBitmap(env, bitmap, NULL, isMutable, ninepatch, NULL, density);
 }
 
 
@@ -587,7 +587,7 @@
     gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
     gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
     gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
-                                            "(I[BZ[BI)V");
+                                            "(I[BZ[B[II)V");
     gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
     gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
 
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index cc32f44..c5b06f5 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -53,7 +53,8 @@
         storage array (may be null).
     */
     static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
-                                bool isMutable, jbyteArray ninepatch, int density = -1);
+                                bool isMutable, jbyteArray ninepatch, jintArray layoutbounds,
+                                int density = -1);
 
     static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
                                 jbyteArray ninepatch, int density = -1);
diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/core/jni/android/graphics/NinePatchPeeker.cpp
index 365d985..df996af 100644
--- a/core/jni/android/graphics/NinePatchPeeker.cpp
+++ b/core/jni/android/graphics/NinePatchPeeker.cpp
@@ -31,14 +31,11 @@
         // this relies on deserialization being done in place
         Res_png_9patch::deserialize(patchNew);
         patchNew->fileToDevice();
-        if (fPatchIsValid) {
-            free(fPatch);
-        }
+        free(fPatch);
         fPatch = patchNew;
         //printf("9patch: (%d,%d)-(%d,%d)\n",
         //       fPatch.sizeLeft, fPatch.sizeTop,
         //       fPatch.sizeRight, fPatch.sizeBottom);
-        fPatchIsValid = true;
 
         // now update our host to force index or 32bit config
         // 'cause we don't want 565 predithered, since as a 9patch, we know
@@ -52,8 +49,9 @@
             SkBitmap::kARGB_8888_Config,
         };
         fHost->setPrefConfigTable(gNo565Pref);
-    } else {
-        fPatch = NULL;
+    } else if (strcmp("npLb", tag) == 0 && length == sizeof(int) * 4) {
+        fLayoutBounds = new int[4];
+        memcpy(fLayoutBounds, data, sizeof(int) * 4);
     }
     return true;    // keep on decoding
 }
diff --git a/core/jni/android/graphics/NinePatchPeeker.h b/core/jni/android/graphics/NinePatchPeeker.h
index 207536c..10d268a 100644
--- a/core/jni/android/graphics/NinePatchPeeker.h
+++ b/core/jni/android/graphics/NinePatchPeeker.h
@@ -28,17 +28,17 @@
     NinePatchPeeker(SkImageDecoder* host) {
         // the host lives longer than we do, so a raw ptr is safe
         fHost = host;
-        fPatchIsValid = false;
+        fPatch = NULL;
+        fLayoutBounds = NULL;
     }
 
     ~NinePatchPeeker() {
-        if (fPatchIsValid) {
-            free(fPatch);
-        }
+        free(fPatch);
+        delete fLayoutBounds;
     }
 
-    bool    fPatchIsValid;
     Res_png_9patch*  fPatch;
+    int    *fLayoutBounds;
 
     virtual bool peek(const char tag[], const void* data, size_t length);
 };
diff --git a/core/res/res/layout/notification_template_base.xml b/core/res/res/layout/notification_template_base.xml
index 93843fd..b9710d6 100644
--- a/core/res/res/layout/notification_template_base.xml
+++ b/core/res/res/layout/notification_template_base.xml
@@ -53,15 +53,21 @@
                 android:fadingEdge="horizontal"
                 android:layout_weight="1"
                 />
-            <DateTimeView android:id="@+id/time"
-                android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Time"
+            <ViewStub android:id="@+id/time"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center"
                 android:layout_weight="0"
-                android:singleLine="true"
-                android:gravity="center"
-                android:paddingLeft="8dp"
+                android:visibility="gone"
+                android:layout="@layout/notification_template_part_time"
+                />
+            <ViewStub android:id="@+id/chronometer"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:layout_weight="0"
+                android:visibility="gone"
+                android:layout="@layout/notification_template_part_chronometer"
                 />
         </LinearLayout>
         <TextView android:id="@+id/text2"
diff --git a/core/res/res/layout/notification_template_part_chronometer.xml b/core/res/res/layout/notification_template_part_chronometer.xml
new file mode 100644
index 0000000..382b0e4
--- /dev/null
+++ b/core/res/res/layout/notification_template_part_chronometer.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<Chronometer android:id="@+id/chronometer" xmlns:android="http://schemas.android.com/apk/res/android"
+    android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Time"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:layout_weight="0"
+    android:singleLine="true"
+    android:gravity="center"
+    android:paddingLeft="8dp"
+    />
diff --git a/core/res/res/layout/notification_template_part_time.xml b/core/res/res/layout/notification_template_part_time.xml
new file mode 100644
index 0000000..410fcaf
--- /dev/null
+++ b/core/res/res/layout/notification_template_part_time.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<DateTimeView android:id="@+id/time" xmlns:android="http://schemas.android.com/apk/res/android"
+    android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Time"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:layout_weight="0"
+    android:singleLine="true"
+    android:gravity="center"
+    android:paddingLeft="8dp"
+    />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 438c141..2b27585 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2079,6 +2079,7 @@
             <!-- Locale -->
             <enum name="locale" value="3" />
         </attr>
+
         <!-- Direction of the text. A heuristic is used to determine the resolved text direction
              of paragraphs. -->
          <attr name="textDirection" format="integer">
@@ -2099,6 +2100,29 @@
             <!-- The paragraph direction is coming from the system Locale. -->
             <enum name="locale" value="5" />
         </attr>
+
+        <!-- Alignment of the text. A heuristic is used to determine the resolved
+            text alignment. -->
+        <attr name="textAlignment" format="integer">
+            <!-- Default -->
+            <enum name="inherit" value="0" />
+            <!-- Default for the root view. The gravity determines the alignment, ALIGN_NORMAL,
+                ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s
+                text direction -->
+            <enum name="gravity" value="1" />
+            <!-- Align to the start of the paragraph, e.g. ALIGN_NORMAL. -->
+            <enum name="textStart" value="2" />
+            <!-- Align to the end of the paragraph, e.g. ALIGN_OPPOSITE. -->
+            <enum name="textEnd" value="3" />
+            <!-- Center the paragraph, e.g. ALIGN_CENTER. -->
+            <enum name="center" value="4" />
+            <!-- Align to the start of the view, which is ALIGN_LEFT if the view’s resolved
+                layoutDirection is LTR, and ALIGN_RIGHT otherwise. -->
+            <enum name="viewStart" value="5" />
+            <!-- Align to the end of the view, which is ALIGN_RIGHT if the view’s resolved
+                layoutDirection is LTR, and ALIGN_LEFT otherwise -->
+            <enum name="viewEnd" value="6" />
+        </attr>
     </declare-styleable>
 
     <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 565d7d2..f010a02 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -202,6 +202,7 @@
   <java-symbol type="id" name="action2" />
   <java-symbol type="id" name="big_picture" />
   <java-symbol type="id" name="big_text" />
+  <java-symbol type="id" name="chronometer" />
 
   <java-symbol type="attr" name="actionModeShareDrawable" />
   <java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -1079,6 +1080,8 @@
   <java-symbol type="layout" name="notification_intruder_content" />
   <java-symbol type="layout" name="notification_template_base" />
   <java-symbol type="layout" name="notification_template_big_picture" />
+  <java-symbol type="layout" name="notification_template_part_time" />
+  <java-symbol type="layout" name="notification_template_part_chronometer" />
 
   <java-symbol type="anim" name="slide_in_child_bottom" />
   <java-symbol type="anim" name="slide_in_right" />
@@ -3561,6 +3564,7 @@
   <public type="attr" name="supportsRtl" id="0x010103a8" />
 
   <public type="attr" name="textDirection"/>
+  <public type="attr" name="textAlignment"/>
 
   <public type="attr" name="layoutDirection" />
 
diff --git a/docs/html/guide/developing/testing/testing_otheride.jd b/docs/html/guide/developing/testing/testing_otheride.jd
index 93af979..7745ae7 100644
--- a/docs/html/guide/developing/testing/testing_otheride.jd
+++ b/docs/html/guide/developing/testing/testing_otheride.jd
@@ -209,7 +209,7 @@
 <p>
   To update a test project with the <code>android</code> tool, enter:
 </p>
-<pre>android update-test-project -m &lt;main_path&gt; -p &lt;test_path&gt;</pre>
+<pre>android update test-project -m &lt;main_path&gt; -p &lt;test_path&gt;</pre>
 
 <table>
     <tr>
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index bbbe6fb1..5297c23 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -299,6 +299,31 @@
           </li>
         </ul>
       </li>
+      
+      <li class="toggle-list">
+        <div><a href="<?cs var:toroot ?>training/displaying-bitmaps/index.html">
+            <span class="en">Displaying Bitmaps Efficiently<span class="new">&nbsp;new!</span></span>
+          </a>
+        </div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>training/displaying-bitmaps/load-bitmap.html">
+            <span class="en">Loading Large Bitmaps Efficiently</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/displaying-bitmaps/process-bitmap.html">
+            <span class="en">Processing Bitmaps Off the UI Thread</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/displaying-bitmaps/cache-bitmap.html">
+            <span class="en">Caching Bitmaps</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/displaying-bitmaps/display-bitmap.html">
+            <span class="en">Displaying Bitmaps in Your UI</span>
+          </a>
+          </li>
+        </ul>
+      </li>
 
       <li class="toggle-list">
         <div><a href="<?cs var:toroot ?>training/accessibility/index.html">
diff --git a/docs/html/shareables/training/BitmapFun.zip b/docs/html/shareables/training/BitmapFun.zip
new file mode 100644
index 0000000..e7e71f9
--- /dev/null
+++ b/docs/html/shareables/training/BitmapFun.zip
Binary files differ
diff --git a/docs/html/training/displaying-bitmaps/cache-bitmap.jd b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
new file mode 100644
index 0000000..94abe21
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
@@ -0,0 +1,337 @@
+page.title=Caching Bitmaps
+parent.title=Displaying Bitmaps Efficiently
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Displaying Bitmaps in Your UI
+next.link=display-bitmap.html
+previous.title=Processing Bitmaps Off the UI Thread
+previous.link=process-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#memory-cache">Use a Memory Cache</a></li>
+  <li><a href="#disk-cache">Use a Disk Cache</a></li>
+  <li><a href="#config-changes">Handle Configuration Changes</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+  <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Loading a single bitmap into your user interface (UI) is straightforward, however things get more
+complicated if you need to load a larger set of images at once. In many cases (such as with
+components like {@link android.widget.ListView}, {@link android.widget.GridView} or {@link
+android.support.v4.view.ViewPager }), the total number of images on-screen combined with images that
+might soon scroll onto the screen are essentially unlimited.</p>
+
+<p>Memory usage is kept down with components like this by recycling the child views as they move
+off-screen. The garbage collector also frees up your loaded bitmaps, assuming you don't keep any
+long lived references. This is all good and well, but in order to keep a fluid and fast-loading UI
+you want to avoid continually processing these images each time they come back on-screen. A memory
+and disk cache can often help here, allowing components to quickly reload processed images.</p>
+
+<p>This lesson walks you through using a memory and disk bitmap cache to improve the responsiveness
+and fluidity of your UI when loading multiple bitmaps.</p>
+
+<h2 id="memory-cache">Use a Memory Cache</h2>
+
+<p>A memory cache offers fast access to bitmaps at the cost of taking up valuable application
+memory. The {@link android.util.LruCache} class (also available in the <a
+href="{@docRoot}reference/android/support/v4/util/LruCache.html">Support Library</a> for use back
+to API Level 4) is particularly well suited to the task of caching bitmaps, keeping recently
+referenced objects in a strong referenced {@link java.util.LinkedHashMap} and evicting the least
+recently used member before the cache exceeds its designated size.</p>
+
+<p class="note"><strong>Note:</strong> In the past, a popular memory cache implementation was a
+{@link java.lang.ref.SoftReference} or {@link java.lang.ref.WeakReference} bitmap cache, however
+this is not recommended. Starting from Android 2.3 (API Level 9) the garbage collector is more
+aggressive with collecting soft/weak references which makes them fairly ineffective. In addition,
+prior to Android 3.0 (API Level 11), the backing data of a bitmap was stored in native memory which
+is not released in a predictable manner, potentially causing an application to briefly exceed its
+memory limits and crash.</p>
+
+<p>In order to choose a suitable size for a {@link android.util.LruCache}, a number of factors
+should be taken into consideration, for example:</p>
+
+<ul>
+  <li>How memory intensive is the rest of your activity and/or application?</li>
+  <li>How many images will be on-screen at once? How many need to be available ready to come
+  on-screen?</li>
+  <li>What is the screen size and density of the device? An extra high density screen (xhdpi) device
+  like <a href="http://www.android.com/devices/detail/galaxy-nexus">Galaxy Nexus</a> will need a
+  larger cache to hold the same number of images in memory compared to a device like <a
+  href="http://www.android.com/devices/detail/nexus-s">Nexus S</a> (hdpi).</li>
+  <li>What dimensions and configuration are the bitmaps and therefore how much memory will each take
+  up?</li>
+  <li>How frequently will the images be accessed? Will some be accessed more frequently than others?
+  If so, perhaps you may want to keep certain items always in memory or even have multiple {@link
+  android.util.LruCache} objects for different groups of bitmaps.</li>
+  <li>Can you balance quality against quantity? Sometimes it can be more useful to store a larger
+  number of lower quality bitmaps, potentially loading a higher quality version in another
+  background task.</li>
+</ul>
+
+<p>There is no specific size or formula that suits all applications, it's up to you to analyze your
+usage and come up with a suitable solution. A cache that is too small causes additional overhead with
+no benefit, a cache that is too large can once again cause {@code java.lang.OutOfMemory} exceptions
+and leave the rest of your app little memory to work with.</p>
+
+<p>Here’s an example of setting up a {@link android.util.LruCache} for bitmaps:</p>
+
+<pre>
+private LruCache<String, Bitmap> mMemoryCache;
+
+&#64;Override
+protected void onCreate(Bundle savedInstanceState) {
+    ...
+    // Get memory class of this device, exceeding this amount will throw an
+    // OutOfMemory exception.
+    final int memClass = ((ActivityManager) context.getSystemService(
+            Context.ACTIVITY_SERVICE)).getMemoryClass();
+
+    // Use 1/8th of the available memory for this memory cache.
+    final int cacheSize = 1024 * 1024 * memClass / 8;
+
+    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
+        &#64;Override
+        protected int sizeOf(String key, Bitmap bitmap) {
+            // The cache size will be measured in bytes rather than number of items.
+            return bitmap.getByteCount();
+        }
+    };
+    ...
+}
+
+public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
+    if (getBitmapFromMemCache(key) == null) {
+        mMemoryCache.put(key, bitmap);
+    }
+}
+
+public Bitmap getBitmapFromMemCache(String key) {
+    return mMemoryCache.get(key);
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> In this example, one eighth of the application memory is
+allocated for our cache. On a normal/hdpi device this is a minimum of around 4MB (32/8). A full
+screen {@link android.widget.GridView} filled with images on a device with 800x480 resolution would
+use around 1.5MB (800*480*4 bytes), so this would cache a minimum of around 2.5 pages of images in
+memory.</p>
+
+<p>When loading a bitmap into an {@link android.widget.ImageView}, the {@link android.util.LruCache}
+is checked first. If an entry is found, it is used immediately to update the {@link
+android.widget.ImageView}, otherwise a background thread is spawned to process the image:</p>
+
+<pre>
+public void loadBitmap(int resId, ImageView imageView) {
+    final String imageKey = String.valueOf(resId);
+
+    final Bitmap bitmap = getBitmapFromMemCache(imageKey);
+    if (bitmap != null) {
+        mImageView.setImageBitmap(bitmap);
+    } else {
+        mImageView.setImageResource(R.drawable.image_placeholder);
+        BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
+        task.execute(resId);
+    }
+}
+</pre>
+
+<p>The <a href="process-bitmap.html#BitmapWorkerTask">{@code BitmapWorkerTask}</a> also needs to be
+updated to add entries to the memory cache:</p>
+
+<pre>
+class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+    ...
+    // Decode image in background.
+    &#64;Override
+    protected Bitmap doInBackground(Integer... params) {
+        final Bitmap bitmap = decodeSampledBitmapFromResource(
+                getResources(), params[0], 100, 100));
+        addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
+        return bitmap;
+    }
+    ...
+}
+</pre>
+
+<h2 id="disk-cache">Use a Disk Cache</h2>
+
+<p>A memory cache is useful in speeding up access to recently viewed bitmaps, however you cannot
+rely on images being available in this cache. Components like {@link android.widget.GridView} with
+larger datasets can easily fill up a memory cache. Your application could be interrupted by another
+task like a phone call, and while in the background it might be killed and the memory cache
+destroyed. Once the user resumes, your application it has to process each image again.</p>
+
+<p>A disk cache can be used in these cases to persist processed bitmaps and help decrease loading
+times where images are no longer available in a memory cache. Of course, fetching images from disk
+is slower than loading from memory and should be done in a background thread, as disk read times can
+be unpredictable.</p>
+
+<p class="note"><strong>Note:</strong> A {@link android.content.ContentProvider} might be a more
+appropriate place to store cached images if they are accessed more frequently, for example in an
+image gallery application.</p>
+
+<p>Included in the sample code of this class is a basic {@code DiskLruCache} implementation.
+However, a more robust and recommended {@code DiskLruCache} solution is included in the Android 4.0
+source code ({@code libcore/luni/src/main/java/libcore/io/DiskLruCache.java}). Back-porting this
+class for use on previous Android releases should be fairly straightforward (a <a
+href="http://www.google.com/search?q=disklrucache">quick search</a> shows others who have already
+implemented this solution).</p>
+
+<p>Here’s updated example code that uses the simple {@code DiskLruCache} included in the sample
+application of this class:</p>
+
+<pre>
+private DiskLruCache mDiskCache;
+private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
+private static final String DISK_CACHE_SUBDIR = "thumbnails";
+
+&#64;Override
+protected void onCreate(Bundle savedInstanceState) {
+    ...
+    // Initialize memory cache
+    ...
+    File cacheDir = getCacheDir(this, DISK_CACHE_SUBDIR);
+    mDiskCache = DiskLruCache.openCache(this, cacheDir, DISK_CACHE_SIZE);
+    ...
+}
+
+class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+    ...
+    // Decode image in background.
+    &#64;Override
+    protected Bitmap doInBackground(Integer... params) {
+        final String imageKey = String.valueOf(params[0]);
+
+        // Check disk cache in background thread
+        Bitmap bitmap = getBitmapFromDiskCache(imageKey);
+
+        if (bitmap == null) { // Not found in disk cache
+            // Process as normal
+            final Bitmap bitmap = decodeSampledBitmapFromResource(
+                    getResources(), params[0], 100, 100));
+        }
+
+        // Add final bitmap to caches
+        addBitmapToCache(String.valueOf(imageKey, bitmap);
+
+        return bitmap;
+    }
+    ...
+}
+
+public void addBitmapToCache(String key, Bitmap bitmap) {
+    // Add to memory cache as before
+    if (getBitmapFromMemCache(key) == null) {
+        mMemoryCache.put(key, bitmap);
+    }
+
+    // Also add to disk cache
+    if (!mDiskCache.containsKey(key)) {
+        mDiskCache.put(key, bitmap);
+    }
+}
+
+public Bitmap getBitmapFromDiskCache(String key) {
+    return mDiskCache.get(key);
+}
+
+// Creates a unique subdirectory of the designated app cache directory. Tries to use external
+// but if not mounted, falls back on internal storage.
+public static File getCacheDir(Context context, String uniqueName) {
+    // Check if media is mounted or storage is built-in, if so, try and use external cache dir
+    // otherwise use internal cache dir
+    final String cachePath = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
+            || !Environment.isExternalStorageRemovable() ?
+                    context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();
+
+    return new File(cachePath + File.separator + uniqueName);
+}
+</pre>
+
+<p>While the memory cache is checked in the UI thread, the disk cache is checked in the background
+thread. Disk operations should never take place on the UI thread. When image processing is
+complete, the final bitmap is added to both the memory and disk cache for future use.</p>
+
+<h2 id="config-changes">Handle Configuration Changes</h2>
+
+<p>Runtime configuration changes, such as a screen orientation change, cause Android to destroy and
+restart the running activity with the new configuration (For more information about this behavior,
+see <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a>).
+You want to avoid having to process all your images again so the user has a smooth and fast
+experience when a configuration change occurs.</p>
+
+<p>Luckily, you have a nice memory cache of bitmaps that you built in the <a
+href="#memory-cache">Use a Memory Cache</a> section. This cache can be passed through to the new
+activity instance using a {@link android.app.Fragment} which is preserved by calling {@link
+android.app.Fragment#setRetainInstance setRetainInstance(true)}). After the activity has been
+recreated, this retained {@link android.app.Fragment} is reattached and you gain access to the
+existing cache object, allowing images to be quickly fetched and re-populated into the {@link
+android.widget.ImageView} objects.</p>
+
+<p>Here’s an example of retaining a {@link android.util.LruCache} object across configuration
+changes using a {@link android.app.Fragment}:</p>
+
+<pre>
+private LruCache<String, Bitmap> mMemoryCache;
+
+&#64;Override
+protected void onCreate(Bundle savedInstanceState) {
+    ...
+    RetainFragment mRetainFragment =
+            RetainFragment.findOrCreateRetainFragment(getFragmentManager());
+    mMemoryCache = RetainFragment.mRetainedCache;
+    if (mMemoryCache == null) {
+        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
+            ... // Initialize cache here as usual
+        }
+        mRetainFragment.mRetainedCache = mMemoryCache;
+    }
+    ...
+}
+
+class RetainFragment extends Fragment {
+    private static final String TAG = "RetainFragment";
+    public LruCache<String, Bitmap> mRetainedCache;
+
+    public RetainFragment() {}
+
+    public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
+        RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
+        if (fragment == null) {
+            fragment = new RetainFragment();
+        }
+        return fragment;
+    }
+
+    &#64;Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        <strong>setRetainInstance(true);</strong>
+    }
+}
+</pre>
+
+<p>To test this out, try rotating a device both with and without retaining the {@link
+android.app.Fragment}. You should notice little to no lag as the images populate the activity almost
+instantly from memory when you retain the cache. Any images not found in the memory cache are
+hopefully available in the disk cache, if not, they are processed as usual.</p>
diff --git a/docs/html/training/displaying-bitmaps/display-bitmap.jd b/docs/html/training/displaying-bitmaps/display-bitmap.jd
new file mode 100644
index 0000000..7a93313
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/display-bitmap.jd
@@ -0,0 +1,400 @@
+page.title=Displaying Bitmaps in Your UI
+parent.title=Displaying Bitmaps Efficiently
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Caching Bitmaps
+previous.link=cache-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#viewpager">Load Bitmaps into a ViewPager Implementation</a></li>
+  <li><a href="#gridview">Load Bitmaps into a GridView Implementation</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}design/patterns/swipe-views.html">Android Design: Swipe Views</a></li>
+  <li><a href="{@docRoot}design/building-blocks/grid-lists.html">Android Design: Grid Lists</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+  <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p></p>
+
+<p>This lesson brings together everything from previous lessons, showing you how to load multiple
+bitmaps into {@link android.support.v4.view.ViewPager} and {@link android.widget.GridView}
+components using a background thread and bitmap cache, while dealing with concurrency and
+configuration changes.</p>
+
+<h2 id="viewpager">Load Bitmaps into a ViewPager Implementation</h2>
+
+<p>The <a href="{@docRoot}design/patterns/swipe-views.html">swipe view pattern</a> is an excellent
+way to navigate the detail view of an image gallery. You can implement this pattern using a {@link
+android.support.v4.view.ViewPager} component backed by a {@link
+android.support.v4.view.PagerAdapter}. However, a more suitable backing adapter is the subclass
+{@link android.support.v4.app.FragmentStatePagerAdapter} which automatically destroys and saves
+state of the {@link android.app.Fragment Fragments} in the {@link android.support.v4.view.ViewPager}
+as they disappear off-screen, keeping memory usage down.</p>
+
+<p class="note"><strong>Note:</strong> If you have a smaller number of images and are confident they
+all fit within the application memory limit, then using a regular {@link
+android.support.v4.view.PagerAdapter} or {@link android.support.v4.app.FragmentPagerAdapter} might
+be more appropriate.</p>
+
+<p>Here’s an implementation of a {@link android.support.v4.view.ViewPager} with {@link
+android.widget.ImageView} children. The main activity holds the {@link
+android.support.v4.view.ViewPager} and the adapter:</p>
+
+<pre>
+public class ImageDetailActivity extends FragmentActivity {
+    public static final String EXTRA_IMAGE = "extra_image";
+
+    private ImagePagerAdapter mAdapter;
+    private ViewPager mPager;
+
+    // A static dataset to back the ViewPager adapter
+    public final static Integer[] imageResIds = new Integer[] {
+            R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
+            R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
+            R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};
+
+    &#64;Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.image_detail_pager); // Contains just a ViewPager
+
+        mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), imageResIds.length);
+        mPager = (ViewPager) findViewById(R.id.pager);
+        mPager.setAdapter(mAdapter);
+    }
+
+    public static class ImagePagerAdapter extends FragmentStatePagerAdapter {
+        private final int mSize;
+
+        public ImagePagerAdapter(FragmentManager fm, int size) {
+            super(fm);
+            mSize = size;
+        }
+
+        &#64;Override
+        public int getCount() {
+            return mSize;
+        }
+
+        &#64;Override
+        public Fragment getItem(int position) {
+            return ImageDetailFragment.newInstance(position);
+        }
+    }
+}
+</pre>
+
+<p>The details {@link android.app.Fragment} holds the {@link android.widget.ImageView} children:</p>
+
+<pre>
+public class ImageDetailFragment extends Fragment {
+    private static final String IMAGE_DATA_EXTRA = "resId";
+    private int mImageNum;
+    private ImageView mImageView;
+
+    static ImageDetailFragment newInstance(int imageNum) {
+        final ImageDetailFragment f = new ImageDetailFragment();
+        final Bundle args = new Bundle();
+        args.putInt(IMAGE_DATA_EXTRA, imageNum);
+        f.setArguments(args);
+        return f;
+    }
+
+    // Empty constructor, required as per Fragment docs
+    public ImageDetailFragment() {}
+
+    &#64;Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mImageNum = getArguments() != null ? getArguments().getInt(IMAGE_DATA_EXTRA) : -1;
+    }
+
+    &#64;Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        // image_detail_fragment.xml contains just an ImageView
+        final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
+        mImageView = (ImageView) v.findViewById(R.id.imageView);
+        return v;
+    }
+
+    &#64;Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        final int resId = ImageDetailActivity.imageResIds[mImageNum];
+        <strong>mImageView.setImageResource(resId);</strong> // Load image into ImageView
+    }
+}
+</pre>
+
+<p>Hopefully you noticed the issue with this implementation; The images are being read from
+resources on the UI thread which can lead to an application hanging and being force closed. Using an
+{@link android.os.AsyncTask} as described in the <a href="process-bitmap.html">Processing Bitmaps Off
+the UI Thread</a> lesson, it’s straightforward to move image loading and processing to a background
+thread:</p>
+
+<pre>
+public class ImageDetailActivity extends FragmentActivity {
+    ...
+
+    public void loadBitmap(int resId, ImageView imageView) {
+        mImageView.setImageResource(R.drawable.image_placeholder);
+        BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
+        task.execute(resId);
+    }
+
+    ... // include <a href="process-bitmap.html#BitmapWorkerTask">{@code BitmapWorkerTask}</a> class
+}
+
+public class ImageDetailFragment extends Fragment {
+    ...
+
+    &#64;Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        if (ImageDetailActivity.class.isInstance(getActivity())) {
+            final int resId = ImageDetailActivity.imageResIds[mImageNum];
+            // Call out to ImageDetailActivity to load the bitmap in a background thread
+            ((ImageDetailActivity) getActivity()).loadBitmap(resId, mImageView);
+        }
+    }
+}
+</pre>
+
+<p>Any additional processing (such as resizing or fetching images from the network) can take place
+in the <a href="process-bitmap.html#BitmapWorkerTask">{@code BitmapWorkerTask}</a> without affecting
+responsiveness of the main UI. If the background thread is doing more than just loading an image
+directly from disk, it can also be beneficial to add a memory and/or disk cache as described in the
+lesson <a href="cache-bitmap.html#memory-cache">Caching Bitmaps</a>. Here's the additional
+modifications for a memory cache:</p>
+
+<pre>
+public class ImageDetailActivity extends FragmentActivity {
+    ...
+    private LruCache<String, Bitmap> mMemoryCache;
+
+    &#64;Override
+    public void onCreate(Bundle savedInstanceState) {
+        ...
+        // initialize LruCache as per <a href="cache-bitmap.html#memory-cache">Use a Memory Cache</a> section
+    }
+
+    public void loadBitmap(int resId, ImageView imageView) {
+        final String imageKey = String.valueOf(resId);
+
+        final Bitmap bitmap = mMemoryCache.get(imageKey);
+        if (bitmap != null) {
+            mImageView.setImageBitmap(bitmap);
+        } else {
+            mImageView.setImageResource(R.drawable.image_placeholder);
+            BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
+            task.execute(resId);
+        }
+    }
+
+    ... // include updated BitmapWorkerTask from <a href="cache-bitmap.html#memory-cache">Use a Memory Cache</a> section
+}
+</pre>
+
+<p>Putting all these pieces together gives you a responsive {@link
+android.support.v4.view.ViewPager} implementation with minimal image loading latency and the ability
+to do as much or as little background processing on your images as needed.</p>
+
+<h2 id="gridview">Load Bitmaps into a GridView Implementation</h2>
+
+<p>The <a href="{@docRoot}design/building-blocks/grid-lists.html">grid list building block</a> is
+useful for showing image data sets and can be implemented using a {@link android.widget.GridView}
+component in which many images can be on-screen at any one time and many more need to be ready to
+appear if the user scrolls up or down. When implementing this type of control, you must ensure the
+UI remains fluid, memory usage remains under control and concurrency is handled correctly (due to
+the way {@link android.widget.GridView} recycles its children views).</p>
+
+<p>To start with, here is a standard {@link android.widget.GridView} implementation with {@link
+android.widget.ImageView} children placed inside a {@link android.app.Fragment}:</p>
+
+<pre>
+public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
+    private ImageAdapter mAdapter;
+
+    // A static dataset to back the GridView adapter
+    public final static Integer[] imageResIds = new Integer[] {
+            R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
+            R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
+            R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};
+
+    // Empty constructor as per Fragment docs
+    public ImageGridFragment() {}
+
+    &#64;Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mAdapter = new ImageAdapter(getActivity());
+    }
+
+    &#64;Override
+    public View onCreateView(
+            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        final View v = inflater.inflate(R.layout.image_grid_fragment, container, false);
+        final GridView mGridView = (GridView) v.findViewById(R.id.gridView);
+        mGridView.setAdapter(mAdapter);
+        mGridView.setOnItemClickListener(this);
+        return v;
+    }
+
+    &#64;Override
+    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+        final Intent i = new Intent(getActivity(), ImageDetailActivity.class);
+        i.putExtra(ImageDetailActivity.EXTRA_IMAGE, position);
+        startActivity(i);
+    }
+
+    private class ImageAdapter extends BaseAdapter {
+        private final Context mContext;
+
+        public ImageAdapter(Context context) {
+            super();
+            mContext = context;
+        }
+
+        &#64;Override
+        public int getCount() {
+            return imageResIds.length;
+        }
+
+        &#64;Override
+        public Object getItem(int position) {
+            return imageResIds[position];
+        }
+
+        &#64;Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        &#64;Override
+        public View getView(int position, View convertView, ViewGroup container) {
+            ImageView imageView;
+            if (convertView == null) { // if it's not recycled, initialize some attributes
+                imageView = new ImageView(mContext);
+                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+                imageView.setLayoutParams(new GridView.LayoutParams(
+                        LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+            } else {
+                imageView = (ImageView) convertView;
+            }
+            <strong>imageView.setImageResource(imageResIds[position]);</strong> // Load image into ImageView
+            return imageView;
+        }
+    }
+}
+</pre>
+
+<p>Once again, the problem with this implementation is that the image is being set in the UI thread.
+While this may work for small, simple images (due to system resource loading and caching), if any
+additional processing needs to be done, your UI grinds to a halt.</p>
+
+<p>The same asynchronous processing and caching methods from the previous section can be implemented
+here. However, you also need to wary of concurrency issues as the {@link android.widget.GridView}
+recycles its children views. To handle this, use the techniques discussed in the <a
+href="process-bitmap#concurrency">Processing Bitmaps Off the UI Thread</a> lesson. Here is the updated
+solution:</p>
+
+<pre>
+public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
+    ...
+
+    private class ImageAdapter extends BaseAdapter {
+        ...
+
+        &#64;Override
+        public View getView(int position, View convertView, ViewGroup container) {
+            ...
+            <strong>loadBitmap(imageResIds[position], imageView)</strong>
+            return imageView;
+        }
+    }
+
+    public void loadBitmap(int resId, ImageView imageView) {
+        if (cancelPotentialWork(resId, imageView)) {
+            final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+            final AsyncDrawable asyncDrawable =
+                    new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
+            imageView.setImageDrawable(asyncDrawable);
+            task.execute(resId);
+        }
+    }
+
+    static class AsyncDrawable extends BitmapDrawable {
+        private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+        public AsyncDrawable(Resources res, Bitmap bitmap,
+                BitmapWorkerTask bitmapWorkerTask) {
+            super(res, bitmap);
+            bitmapWorkerTaskReference =
+                new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
+        }
+
+        public BitmapWorkerTask getBitmapWorkerTask() {
+            return bitmapWorkerTaskReference.get();
+        }
+    }
+
+    public static boolean cancelPotentialWork(int data, ImageView imageView) {
+        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+        if (bitmapWorkerTask != null) {
+            final int bitmapData = bitmapWorkerTask.data;
+            if (bitmapData != data) {
+                // Cancel previous task
+                bitmapWorkerTask.cancel(true);
+            } else {
+                // The same work is already in progress
+                return false;
+            }
+        }
+        // No task associated with the ImageView, or an existing task was cancelled
+        return true;
+    }
+
+    private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+       if (imageView != null) {
+           final Drawable drawable = imageView.getDrawable();
+           if (drawable instanceof AsyncDrawable) {
+               final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+               return asyncDrawable.getBitmapWorkerTask();
+           }
+        }
+        return null;
+    }
+
+    ... // include updated <a href="process-bitmap.html#BitmapWorkerTaskUpdated">{@code BitmapWorkerTask}</a> class
+</pre>
+
+<p class="note"><strong>Note:</strong> The same code can easily be adapted to work with {@link
+android.widget.ListView} as well.</p>
+
+<p>This implementation allows for flexibility in how the images are processed and loaded without
+impeding the smoothness of the UI. In the background task you can load images from the network or
+resize large digital camera photos and the images appear as the tasks finish processing.</p>
+
+<p>For a full example of this and other concepts discussed in this lesson, please see the included
+sample application.</p>
diff --git a/docs/html/training/displaying-bitmaps/index.jd b/docs/html/training/displaying-bitmaps/index.jd
new file mode 100644
index 0000000..6755c24
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/index.jd
@@ -0,0 +1,78 @@
+page.title=Displaying Bitmaps Efficiently
+
+trainingnavtop=true
+startpage=true
+next.title=Loading Large Bitmaps Efficiently
+next.link=load-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+<ul>
+  <li>Android 2.1 (API Level 7) or higher</li>
+  <li><a href="{@docRoot}sdk/compatibility-library.html">Support Library</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+  <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>This class covers some common techniques for processing and loading {@link
+android.graphics.Bitmap} objects in a way that keeps your user interface (UI) components responsive
+and avoids exceeding your application memory limit. If you're not careful, bitmaps can quickly
+consume your available memory budget leading to an application crash due to the dreaded
+exception:<br />{@code java.lang.OutofMemoryError: bitmap size exceeds VM budget}.</p>
+
+<p>There are a number of reasons why loading bitmaps in your Android application is tricky:</p>
+
+<ul>
+  <li>Mobile devices typically have constrained system resources. Android devices can have as little
+  as 16MB of memory available to a single application. The <a
+  href="http://source.android.com/compatibility/downloads.html">Android Compatibility Definition
+  Document</a> (CDD), <i>Section 3.7. Virtual Machine Compatibility</i> gives the required minimum
+  application memory for various screen sizes and densities. Applications should be optimized to
+  perform under this minimum memory limit. However, keep in mind many devices are configured with
+  higher limits.</li>
+  <li>Bitmaps take up a lot of memory, especially for rich images like photographs. For example, the
+  camera on the <a href="http://www.google.com/nexus/">Galaxy Nexus</a> takes photos up to 2592x1936
+  pixels (5 megapixels). If the bitmap configuration used is {@link
+  android.graphics.Bitmap.Config ARGB_8888} (the default from the Android 2.3 onward) then loading
+  this image into memory takes about 19MB of memory (2592*1936*4 bytes), immediately exhausting the
+  per-app limit on some devices.</li>
+  <li>Android app UI’s frequently require several bitmaps to be loaded at once. Components such as
+  {@link android.widget.ListView}, {@link android.widget.GridView} and {@link
+  android.support.v4.view.ViewPager} commonly include multiple bitmaps on-screen at once with many
+  more potentially off-screen ready to show at the flick of a finger.</li>
+</ul>
+
+<h2>Lessons</h2>
+
+<dl>
+  <dt><b><a href="load-bitmap.html">Loading Large Bitmaps Efficiently</a></b></dt>
+    <dd>This lesson walks you through decoding large bitmaps without exceeding the per application
+    memory limit.</dd>
+
+  <dt><b><a href="process-bitmap.html">Processing Bitmaps Off the UI Thread</a></b></dt>
+    <dd>Bitmap processing (resizing, downloading from a remote source, etc.) should never take place
+    on the main UI thread. This lesson walks you through processing bitmaps in a background thread
+    using {@link android.os.AsyncTask} and explains how to handle concurrency issues.</dd>
+
+  <dt><b><a href="cache-bitmap.html">Caching Bitmaps</a></b></dt>
+    <dd>This lesson walks you through using a memory and disk bitmap cache to improve the
+    responsiveness and fluidity of your UI when loading multiple bitmaps.</dd>
+
+  <dt><b><a href="display-bitmap.html">Displaying Bitmaps in Your UI</a></b></dt>
+    <dd>This lesson brings everything together, showing you how to load multiple bitmaps into
+    components like {@link android.support.v4.view.ViewPager} and {@link android.widget.GridView}
+    using a background thread and bitmap cache.</dd>
+
+</dl>
\ No newline at end of file
diff --git a/docs/html/training/displaying-bitmaps/load-bitmap.jd b/docs/html/training/displaying-bitmaps/load-bitmap.jd
new file mode 100644
index 0000000..c0a5709
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/load-bitmap.jd
@@ -0,0 +1,165 @@
+page.title=Loading Large Bitmaps Efficiently
+parent.title=Displaying Bitmaps Efficiently
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Processing Bitmaps Off the UI Thread
+next.link=process-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#read-bitmap">Read Bitmap Dimensions and Type</a></li>
+  <li><a href="#load-bitmap">Load a Scaled Down Version into Memory</a></li>
+</ol>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+  <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Images come in all shapes and sizes. In many cases they are larger than required for a typical
+application user interface (UI). For example, the system Gallery application displays photos taken
+using your Android devices's camera which are typically much higher resolution than the screen
+density of your device.</p>
+
+<p>Given that you are working with limited memory, ideally you only want to load a lower resolution
+version in memory. The lower resolution version should match the size of the UI component that
+displays it. An image with a higher resolution does not provide any visible benefit, but still takes
+up precious memory and incurs additional performance overhead due to additional on the fly
+scaling.</p>
+
+<p>This lesson walks you through decoding large bitmaps without exceeding the per application
+memory limit by loading a smaller subsampled version in memory.</p>
+
+<h2 id="read-bitmap">Read Bitmap Dimensions and Type</h2>
+
+<p>The {@link android.graphics.BitmapFactory} class provides several decoding methods ({@link
+android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
+decodeByteArray()}, {@link
+android.graphics.BitmapFactory#decodeFile(java.lang.String,android.graphics.BitmapFactory.Options)
+decodeFile()}, {@link
+android.graphics.BitmapFactory#decodeResource(android.content.res.Resources,int,android.graphics.BitmapFactory.Options)
+decodeResource()}, etc.) for creating a {@link android.graphics.Bitmap} from various sources. Choose
+the most appropriate decode method based on your image data source. These methods attempt to
+allocate memory for the constructed bitmap and therefore can easily result in an {@code OutOfMemory}
+exception. Each type of decode method has additional signatures that let you specify decoding
+options via the {@link android.graphics.BitmapFactory.Options} class. Setting the {@link
+android.graphics.BitmapFactory.Options#inJustDecodeBounds} property to {@code true} while decoding
+avoids memory allocation, returning {@code null} for the bitmap object but setting {@link
+android.graphics.BitmapFactory.Options#outWidth}, {@link
+android.graphics.BitmapFactory.Options#outHeight} and {@link
+android.graphics.BitmapFactory.Options#outMimeType}. This technique allows you to read the
+dimensions and type of the image data prior to construction (and memory allocation) of the
+bitmap.</p>
+
+<pre>
+BitmapFactory.Options options = new BitmapFactory.Options();
+options.inJustDecodeBounds = true;
+BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
+int imageHeight = options.outHeight;
+int imageWidth = options.outWidth;
+String imageType = options.outMimeType;
+</pre>
+
+<p>To avoid {@code java.lang.OutOfMemory} exceptions, check the dimensions of a bitmap before
+decoding it, unless you absolutely trust the source to provide you with predictably sized image data
+that comfortably fits within the available memory.</p>
+
+<h2 id="load-bitmap">Load a Scaled Down Version into Memory</h2>
+
+<p>Now that the image dimensions are known, they can be used to decide if the full image should be
+loaded into memory or if a subsampled version should be loaded instead. Here are some factors to
+consider:</p>
+
+<ul>
+  <li>Estimated memory usage of loading the full image in memory.</li>
+  <li>Amount of memory you are willing to commit to loading this image given any other memory
+  requirements of your application.</li>
+  <li>Dimensions of the target {@link android.widget.ImageView} or UI component that the image
+  is to be loaded into.</li>
+  <li>Screen size and density of the current device.</li>
+</ul>
+
+<p>For example, it’s not worth loading a 1024x768 pixel image into memory if it will eventually be
+displayed in a 128x96 pixel thumbnail in an {@link android.widget.ImageView}.</p>
+
+<p>To tell the decoder to subsample the image, loading a smaller version into memory, set {@link
+android.graphics.BitmapFactory.Options#inSampleSize} to {@code true} in your {@link
+android.graphics.BitmapFactory.Options} object. For example, an image with resolution 2048x1536 that
+is decoded with an {@link android.graphics.BitmapFactory.Options#inSampleSize} of 4 produces a
+bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full
+image (assuming a bitmap configuration of {@link android.graphics.Bitmap.Config ARGB_8888}). Here’s
+a method to calculate a the sample size value based on a target width and height:</p>
+
+<pre>
+public static int calculateInSampleSize(
+            BitmapFactory.Options options, int reqWidth, int reqHeight) {
+    // Raw height and width of image
+    final int height = options.outHeight;
+    final int width = options.outWidth;
+    int inSampleSize = 1;
+
+    if (height > reqHeight || width > reqWidth) {
+        if (width > height) {
+            inSampleSize = Math.round((float)height / (float)reqHeight);
+        } else {
+            inSampleSize = Math.round((float)width / (float)reqWidth);
+        }
+    }
+    return inSampleSize;
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> Using powers of 2 for {@link
+android.graphics.BitmapFactory.Options#inSampleSize} values is faster and more efficient for the
+decoder. However, if you plan to cache the resized versions in memory or on disk, it’s usually still
+worth decoding to the most appropriate image dimensions to save space.</p>
+
+<p>To use this method, first decode with {@link
+android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code true}, pass the options
+through and then decode again using the new {@link
+android.graphics.BitmapFactory.Options#inSampleSize} value and {@link
+android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code false}:</p>
+
+<a name="decodeSampledBitmapFromResource"></a>
+<pre>
+public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
+        int reqWidth, int reqHeight) {
+
+    // First decode with inJustDecodeBounds=true to check dimensions
+    final BitmapFactory.Options options = new BitmapFactory.Options();
+    options.inJustDecodeBounds = true;
+    BitmapFactory.decodeResource(res, resId, options);
+
+    // Calculate inSampleSize
+    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
+
+    // Decode bitmap with inSampleSize set
+    options.inJustDecodeBounds = false;
+    return BitmapFactory.decodeResource(res, resId, options);
+}
+</pre>
+
+<p>This method makes it easy to load a bitmap of arbitrarily large size into an {@link
+android.widget.ImageView} that displays a 100x100 pixel thumbnail, as shown in the following example
+code:</p>
+
+<pre>
+mImageView.setImageBitmap(
+    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
+</pre>
+
+<p>You can follow a similar process to decode bitmaps from other sources, by substituting the
+appropriate {@link
+android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
+BitmapFactory.decode*} method as needed.</p>
\ No newline at end of file
diff --git a/docs/html/training/displaying-bitmaps/process-bitmap.jd b/docs/html/training/displaying-bitmaps/process-bitmap.jd
new file mode 100644
index 0000000..c1450b4
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/process-bitmap.jd
@@ -0,0 +1,239 @@
+page.title=Processing Bitmaps Off the UI Thread
+parent.title=Displaying Bitmaps Efficiently
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Caching Bitmaps
+next.link=cache-bitmap.html
+previous.title=Loading Large Bitmaps Efficiently
+previous.link=load-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#async-task">Use an AsyncTask</a></li>
+  <li><a href="#concurrency">Handle Concurrency</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}guide/practices/design/responsiveness.html">Designing for Responsiveness</a></li>
+  <li><a
+  href="http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html">Multithreading
+  for Performance</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+  <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>The {@link
+android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
+BitmapFactory.decode*} methods, discussed in the <a href="load-bitmap.html">Load Large Bitmaps
+Efficiently</a> lesson, should not be executed on the main UI thread if the source data is read from
+disk or a network location (or really any source other than memory). The time this data takes to
+load is unpredictable and depends on a variety of factors (speed of reading from disk or network,
+size of image, power of CPU, etc.). If one of these tasks blocks the UI thread, the system flags
+your application as non-responsive and the user has the option of closing it (see <a
+href="{@docRoot}guide/practices/design/responsiveness.html">Designing for Responsiveness</a> for
+more information).</p>
+
+<p>This lesson walks you through processing bitmaps in a background thread using
+{@link android.os.AsyncTask} and shows you how to handle concurrency issues.</p>
+
+<h2 id="async-task">Use an AsyncTask</h2>
+
+<p>The {@link android.os.AsyncTask} class provides an easy way to execute some work in a background
+thread and publish the results back on the UI thread. To use it, create a subclass and override the
+provided methods. Here’s an example of loading a large image into an {@link
+android.widget.ImageView} using {@link android.os.AsyncTask} and <a
+href="load-bitmap.html#decodeSampledBitmapFromResource">{@code
+decodeSampledBitmapFromResource()}</a>: </p>
+
+<a name="BitmapWorkerTask"></a>
+<pre>
+class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+    private final WeakReference<ImageView> imageViewReference;
+    private int data = 0;
+
+    public BitmapWorkerTask(ImageView imageView) {
+        // Use a WeakReference to ensure the ImageView can be garbage collected
+        imageViewReference = new WeakReference<ImageView>(imageView);
+    }
+
+    // Decode image in background.
+    &#64;Override
+    protected Bitmap doInBackground(Integer... params) {
+        data = params[0];
+        return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
+    }
+
+    // Once complete, see if ImageView is still around and set bitmap.
+    &#64;Override
+    protected void onPostExecute(Bitmap bitmap) {
+        if (imageViewReference != null && bitmap != null) {
+            final ImageView imageView = imageViewReference.get();
+            if (imageView != null) {
+                imageView.setImageBitmap(bitmap);
+            }
+        }
+    }
+}
+</pre>
+
+<p>The {@link java.lang.ref.WeakReference} to the {@link android.widget.ImageView} ensures that the
+{@link android.os.AsyncTask} does not prevent the {@link android.widget.ImageView} and anything it
+references from being garbage collected. There’s no guarantee the {@link android.widget.ImageView}
+is still around when the task finishes, so you must also check the reference in {@link
+android.os.AsyncTask#onPostExecute(Result) onPostExecute()}. The {@link android.widget.ImageView}
+may no longer exist, if for example, the user navigates away from the activity or if a
+configuration change happens before the task finishes.</p>
+
+<p>To start loading the bitmap asynchronously, simply create a new task and execute it:</p>
+
+<pre>
+public void loadBitmap(int resId, ImageView imageView) {
+    BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+    task.execute(resId);
+}
+</pre>
+
+<h2 id="concurrency">Handle Concurrency</h2>
+
+<p>Common view components such as {@link android.widget.ListView} and {@link
+android.widget.GridView} introduce another issue when used in conjunction with the {@link
+android.os.AsyncTask} as demonstrated in the previous section. In order to be efficient with memory,
+these components recycle child views as the user scrolls. If each child view triggers an {@link
+android.os.AsyncTask}, there is no guarantee that when it completes, the associated view has not
+already been recycled for use in another child view. Furthermore, there is no guarantee that the
+order in which asynchronous tasks are started is the order that they complete.</p>
+
+<p>The blog post <a
+href="http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html">Multithreading
+for Performance</a> further discusses dealing with concurrency, and offers a solution where the
+{@link android.widget.ImageView} stores a reference to the most recent {@link android.os.AsyncTask}
+which can later be checked when the task completes. Using a similar method, the {@link
+android.os.AsyncTask} from the previous section can be extended to follow a similar pattern.</p>
+
+<p>Create a dedicated {@link android.graphics.drawable.Drawable} subclass to store a reference
+back to the worker task. In this case, a {@link android.graphics.drawable.BitmapDrawable} is used so
+that a placeholder image can be displayed in the {@link android.widget.ImageView} while the task
+completes:</p>
+
+<a name="AsyncDrawable"></a>
+<pre>
+static class AsyncDrawable extends BitmapDrawable {
+    private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+    public AsyncDrawable(Resources res, Bitmap bitmap,
+            BitmapWorkerTask bitmapWorkerTask) {
+        super(res, bitmap);
+        bitmapWorkerTaskReference =
+            new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
+    }
+
+    public BitmapWorkerTask getBitmapWorkerTask() {
+        return bitmapWorkerTaskReference.get();
+    }
+}
+</pre>
+
+<p>Before executing the <a href="#BitmapWorkerTask">{@code BitmapWorkerTask}</a>, you create an <a
+href="#AsyncDrawable">{@code AsyncDrawable}</a> and bind it to the target {@link
+android.widget.ImageView}:</p>
+
+<pre>
+public void loadBitmap(int resId, ImageView imageView) {
+    if (cancelPotentialWork(resId, imageView)) {
+        final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+        final AsyncDrawable asyncDrawable =
+                new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
+        imageView.setImageDrawable(asyncDrawable);
+        task.execute(resId);
+    }
+}
+</pre>
+
+<p>The {@code cancelPotentialWork} method referenced in the code sample above checks if another
+running task is already associated with the {@link android.widget.ImageView}. If so, it attempts to
+cancel the previous task by calling {@link android.os.AsyncTask#cancel cancel()}. In a small number
+of cases, the new task data matches the existing task and nothing further needs to happen. Here is
+the implementation of {@code cancelPotentialWork}:</p>
+
+<pre>
+public static boolean cancelPotentialWork(int data, ImageView imageView) {
+    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+    if (bitmapWorkerTask != null) {
+        final int bitmapData = bitmapWorkerTask.data;
+        if (bitmapData != data) {
+            // Cancel previous task
+            bitmapWorkerTask.cancel(true);
+        } else {
+            // The same work is already in progress
+            return false;
+        }
+    }
+    // No task associated with the ImageView, or an existing task was cancelled
+    return true;
+}
+</pre>
+
+<p>A helper method, {@code getBitmapWorkerTask()}, is used above to retrieve the task associated
+with a particular {@link android.widget.ImageView}:</p>
+
+<pre>
+private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+   if (imageView != null) {
+       final Drawable drawable = imageView.getDrawable();
+       if (drawable instanceof AsyncDrawable) {
+           final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+           return asyncDrawable.getBitmapWorkerTask();
+       }
+    }
+    return null;
+}
+</pre>
+
+<p>The last step is updating {@code onPostExecute()} in <a href="#BitmapWorkerTask">{@code
+BitmapWorkerTask}</a> so that it checks if the task is cancelled and if the current task matches the
+one associated with the {@link android.widget.ImageView}:</p>
+
+<a name="BitmapWorkerTaskUpdated"></a>
+<pre>
+class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+    ...
+
+    &#64;Override
+    protected void onPostExecute(Bitmap bitmap) {
+        <strong>if (isCancelled()) {
+            bitmap = null;
+        }</strong>
+
+        if (imageViewReference != null && bitmap != null) {
+            final ImageView imageView = imageViewReference.get();
+            <strong>final BitmapWorkerTask bitmapWorkerTask =
+                    getBitmapWorkerTask(imageView);</strong>
+            if (<strong>this == bitmapWorkerTask &&</strong> imageView != null) {
+                imageView.setImageBitmap(bitmap);
+            }
+        }
+    }
+}
+</pre>
+
+<p>This implementation is now suitable for use in {@link android.widget.ListView} and {@link
+android.widget.GridView} components as well as any other components that recycle their child
+views. Simply call {@code loadBitmap} where you normally set an image to your {@link
+android.widget.ImageView}. For example, in a {@link android.widget.GridView} implementation this
+would be in the {@link android.widget.Adapter#getView getView()} method of the backing adapter.</p>
\ No newline at end of file
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 6f939be..ed5b2f6 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -16,9 +16,12 @@
 
 package android.graphics;
 
+import android.os.Debug;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.DisplayMetrics;
+import android.util.Log;
+
 import java.io.OutputStream;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
@@ -57,6 +60,7 @@
 
     private final boolean mIsMutable;
     private byte[] mNinePatchChunk;   // may be null
+    private int[] mLayoutBounds;   // may be null
     private int mWidth = -1;
     private int mHeight = -1;
     private boolean mRecycled;
@@ -95,6 +99,19 @@
     */
     /*package*/ Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
             int density) {
+        this(nativeBitmap, buffer, isMutable, ninePatchChunk, null, density);
+    }
+
+    /**
+     * @noinspection UnusedDeclaration
+     */
+    /*  Private constructor that must received an already allocated native
+        bitmap int (pointer).
+
+        This can be called from JNI code.
+    */
+    /*package*/ Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
+            int[] layoutBounds, int density) {
         if (nativeBitmap == 0) {
             throw new RuntimeException("internal error: native bitmap is 0");
         }
@@ -106,6 +123,7 @@
 
         mIsMutable = isMutable;
         mNinePatchChunk = ninePatchChunk;
+        mLayoutBounds = layoutBounds;
         if (density >= 0) {
             mDensity = density;
         }
@@ -164,6 +182,16 @@
     }
 
     /**
+     * Sets the layout bounds as an array of left, top, right, bottom integers
+     * @param padding the array containing the padding values
+     *
+     * @hide
+     */
+    public void setLayoutBounds(int[] bounds) {
+        mLayoutBounds = bounds;
+    }
+
+    /**
      * Free the native object associated with this bitmap, and clear the
      * reference to the pixel data. This will not free the pixel data synchronously;
      * it simply allows it to be garbage collected if there are no other references.
@@ -690,6 +718,14 @@
     }
 
     /**
+     * @hide
+     * @return the layout padding [left, right, top, bottom]
+     */
+    public int[] getLayoutBounds() {
+        return mLayoutBounds;
+    }
+
+    /**
      * Specifies the known formats a bitmap can be compressed into
      */
     public enum CompressFormat {
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index c5705f6..1599e40 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -424,6 +424,7 @@
             throw new ArrayIndexOutOfBoundsException();
         }
         Bitmap bm = nativeDecodeByteArray(data, offset, length, opts);
+
         if (bm == null && opts != null && opts.inBitmap != null) {
             throw new IllegalArgumentException("Problem decoding into existing bitmap");
         }
@@ -554,7 +555,6 @@
         if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
             return bm;
         }
-        
         byte[] np = bm.getNinePatchChunk();
         final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
         if (opts.inScaled || isNinePatch) {
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 043adae..86e824b 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -773,7 +773,13 @@
                 np = null;
                 pad = null;
             }
-            return drawableFromBitmap(res, bm, np, pad, srcName);
+            int[] layoutBounds = bm.getLayoutBounds();
+            Rect layoutBoundsRect = null;
+            if (layoutBounds != null) {
+                layoutBoundsRect = new Rect(layoutBounds[0], layoutBounds[1],
+                                             layoutBounds[2], layoutBounds[3]);
+            }
+            return drawableFromBitmap(res, bm, np, pad, layoutBoundsRect, srcName);
         }
         return null;
     }
@@ -875,7 +881,7 @@
 
         Bitmap bm = BitmapFactory.decodeFile(pathName);
         if (bm != null) {
-            return drawableFromBitmap(null, bm, null, null, pathName);
+            return drawableFromBitmap(null, bm, null, null, null, pathName);
         }
 
         return null;
@@ -956,10 +962,12 @@
     }
 
     private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np,
-            Rect pad, String srcName) {
+            Rect pad, Rect layoutBounds, String srcName) {
 
         if (np != null) {
-            return new NinePatchDrawable(res, bm, np, pad, srcName);
+            NinePatchDrawable npd = new NinePatchDrawable(res, bm, np, pad, srcName);
+            npd.setLayoutBounds(layoutBounds);
+            return npd;
         }
 
         return new BitmapDrawable(res, bm);
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 18b8bc7..1272071 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -47,6 +47,7 @@
     private NinePatchState mNinePatchState;
     private NinePatch mNinePatch;
     private Rect mPadding;
+    private Rect mLayoutBounds;
     private Paint mPaint;
     private boolean mMutated;
 
@@ -98,6 +99,13 @@
         mNinePatchState.mTargetDensity = mTargetDensity;
     }
 
+    /**
+     * @hide
+     */
+    void setLayoutBounds(Rect layoutBounds) {
+        mLayoutBounds = layoutBounds;
+    }
+
     private void setNinePatchState(NinePatchState state, Resources res) {
         mNinePatchState = state;
         mNinePatch = state.mNinePatch;
@@ -258,7 +266,7 @@
         }
         options.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
 
-        final Rect padding = new Rect();        
+        final Rect padding = new Rect();
         Bitmap bitmap = null;
 
         try {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 82dd308..c7e71eb 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -903,9 +903,18 @@
      * @hide
      */
     public void setMasterMute(boolean state) {
+        setMasterMute(state, FLAG_SHOW_UI);
+    }
+
+    /**
+     * set master mute state with optional flags.
+     *
+     * @hide
+     */
+    public void setMasterMute(boolean state, int flags) {
         IAudioService service = getService();
         try {
-            service.setMasterMute(state, mICallBack);
+            service.setMasterMute(state, flags, mICallBack);
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in setMasterMute", e);
         }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index c66a03f..2e456f0 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -126,6 +126,7 @@
     private static final int MSG_RCDISPLAY_CLEAR = 13;
     private static final int MSG_RCDISPLAY_UPDATE = 14;
     private static final int MSG_SET_ALL_VOLUMES = 15;
+    private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 16;
 
 
     // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
@@ -501,6 +502,10 @@
                 System.MUTE_STREAMS_AFFECTED,
                 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
 
+        boolean masterMute = System.getInt(cr, System.VOLUME_MASTER_MUTE, 0) == 1;
+        AudioSystem.setMasterMute(masterMute);
+        broadcastMasterMuteStatus(masterMute);
+
         // Each stream will read its own persisted settings
 
         // Broadcast the sticky intent
@@ -740,9 +745,14 @@
     // UI update and Broadcast Intent
     private void sendMasterMuteUpdate(boolean muted, int flags) {
         mVolumePanel.postMasterMuteChanged(flags);
+        broadcastMasterMuteStatus(muted);
+    }
 
+    private void broadcastMasterMuteStatus(boolean muted) {
         Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
         intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
         long origCallerIdentityToken = Binder.clearCallingIdentity();
         mContext.sendStickyBroadcast(intent);
         Binder.restoreCallingIdentity(origCallerIdentityToken);
@@ -817,10 +827,13 @@
     }
 
     /** @see AudioManager#setMasterMute(boolean, IBinder) */
-    public void setMasterMute(boolean state, IBinder cb) {
+    public void setMasterMute(boolean state, int flags, IBinder cb) {
         if (state != AudioSystem.getMasterMute()) {
             AudioSystem.setMasterMute(state);
-            sendMasterMuteUpdate(state, AudioManager.FLAG_SHOW_UI);
+            // Post a persist master volume msg
+            sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1
+                    : 0, 0, null, PERSIST_DELAY);
+            sendMasterMuteUpdate(state, flags);
         }
     }
 
@@ -2551,6 +2564,11 @@
                             (float)msg.arg1 / (float)1000.0);
                     break;
 
+                case MSG_PERSIST_MASTER_VOLUME_MUTE:
+                    Settings.System.putInt(mContentResolver, Settings.System.VOLUME_MASTER_MUTE,
+                            msg.arg1);
+                    break;
+
                 case MSG_PERSIST_RINGER_MODE:
                     // note that the value persisted is the current ringer mode, not the
                     // value of ringer mode as of the time the request was made to persist
@@ -3038,11 +3056,6 @@
                     adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
                                             BluetoothProfile.A2DP);
                 }
-
-                if (mUseMasterVolume) {
-                    // Send sticky broadcast for initial master mute state
-                    sendMasterMuteUpdate(false, 0);
-                }
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                     // a package is being removed, not replaced
diff --git a/media/java/android/media/Crypto.java b/media/java/android/media/Crypto.java
new file mode 100644
index 0000000..43e34fb
--- /dev/null
+++ b/media/java/android/media/Crypto.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * Crypto class can be used in conjunction with MediaCodec to decode
+ * encrypted media data.
+ * @hide
+*/
+public final class Crypto {
+    public static final native boolean isCryptoSchemeSupported(byte[] uuid);
+
+    public Crypto(byte[] uuid, byte[] initData) {
+        native_setup(uuid, initData);
+    }
+
+    public final native boolean requiresSecureDecoderComponent(String mime);
+
+    @Override
+    protected void finalize() {
+        native_finalize();
+    }
+
+    public native final void release();
+    private static native final void native_init();
+    private native final void native_setup(byte[] uuid, byte[] initData);
+    private native final void native_finalize();
+
+    static {
+        System.loadLibrary("media_jni");
+        native_init();
+    }
+
+    private int mNativeContext;
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 17d8e4d..b775095 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -45,7 +45,7 @@
 
     boolean isStreamMute(int streamType);
 
-    void setMasterMute(boolean state, IBinder cb);
+    void setMasterMute(boolean state, int flags, IBinder cb);
 
     boolean isMasterMute();
 
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 7629d60..66cea9d4 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.media.Crypto;
 import android.view.Surface;
 import java.nio.ByteBuffer;
 import java.util.Map;
@@ -25,8 +26,7 @@
  * encoder/decoder components.
  * @hide
 */
-public class MediaCodec
-{
+final public class MediaCodec {
     /** Per buffer metadata includes an offset and size specifying
         the range of valid data in the associated codec buffer.
     */
@@ -113,11 +113,14 @@
      *
      *  @param surface Specify a surface on which to render the output of this
      *                 decoder.
+     *  @param crypto  Specify a crypto object to facilitate secure decryption
+     *                 of the media data.
      *  @param flags   Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the
      *                 component as an encoder.
     */
     public void configure(
-            Map<String, Object> format, Surface surface, int flags) {
+            Map<String, Object> format,
+            Surface surface, Crypto crypto, int flags) {
         String[] keys = null;
         Object[] values = null;
 
@@ -133,11 +136,12 @@
             }
         }
 
-        native_configure(keys, values, surface, flags);
+        native_configure(keys, values, surface, crypto, flags);
     }
 
     private native final void native_configure(
-            String[] keys, Object[] values, Surface surface, int flags);
+            String[] keys, Object[] values,
+            Surface surface, Crypto crypto, int flags);
 
     /** After successfully configuring the component, call start. On return
      *  you can query the component for its input/output buffers.
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 9ea3d0e..9c3b6a7 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -23,8 +23,7 @@
  * MediaExtractor
  * @hide
 */
-public class MediaExtractor
-{
+final public class MediaExtractor {
     public MediaExtractor(String path) {
         native_setup(path);
     }
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 2f4ed89..26089ad 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -609,6 +609,10 @@
                 mCompilation = parseSubstring(value, 0, 0);
             } else if (name.equalsIgnoreCase("isdrm")) {
                 mIsDrm = (parseSubstring(value, 0, 0) == 1);
+            } else if (name.equalsIgnoreCase("width")) {
+                mWidth = parseSubstring(value, 0, 0);
+            } else if (name.equalsIgnoreCase("height")) {
+                mHeight = parseSubstring(value, 0, 0);
             } else {
                 //Log.v(TAG, "unknown tag: " + name + " (" + mProcessGenres + ")");
             }
@@ -734,9 +738,11 @@
             map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
             map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
 
+            String resolution = null;
             if (mWidth > 0 && mHeight > 0) {
                 map.put(MediaStore.MediaColumns.WIDTH, mWidth);
                 map.put(MediaStore.MediaColumns.HEIGHT, mHeight);
+                resolution = mWidth + "x" + mHeight;
             }
 
             if (!mNoMedia) {
@@ -746,7 +752,9 @@
                     map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0
                             ? mAlbum : MediaStore.UNKNOWN_STRING));
                     map.put(Video.Media.DURATION, mDuration);
-                    // FIXME - add RESOLUTION
+                    if (resolution != null) {
+                        map.put(Video.Media.RESOLUTION, resolution);
+                    }
                 } else if (MediaFile.isImageFileType(mFileType)) {
                     // FIXME - add DESCRIPTION
                 } else if (MediaFile.isAudioFileType(mFileType)) {
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index dd1e505..a3361d4 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,6 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
+    android_media_Crypto.cpp \
     android_media_MediaCodec.cpp \
     android_media_MediaCodecList.cpp \
     android_media_MediaExtractor.cpp \
diff --git a/media/jni/android_media_Crypto.cpp b/media/jni/android_media_Crypto.cpp
new file mode 100644
index 0000000..e1a60a1
--- /dev/null
+++ b/media/jni/android_media_Crypto.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Crypto-JNI"
+#include <utils/Log.h>
+
+#include "android_media_Crypto.h"
+
+#include "android_runtime/AndroidRuntime.h"
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <binder/IServiceManager.h>
+#include <media/ICrypto.h>
+#include <media/IMediaPlayerService.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+struct fields_t {
+    jfieldID context;
+};
+
+static fields_t gFields;
+
+static sp<JCrypto> getCrypto(JNIEnv *env, jobject thiz) {
+    return (JCrypto *)env->GetIntField(thiz, gFields.context);
+}
+
+JCrypto::JCrypto(
+        JNIEnv *env, jobject thiz,
+        const uint8_t uuid[16], const void *initData, size_t initSize) {
+    mObject = env->NewWeakGlobalRef(thiz);
+
+    mCrypto = MakeCrypto(uuid, initData, initSize);
+}
+
+JCrypto::~JCrypto() {
+    mCrypto.clear();
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    env->DeleteWeakGlobalRef(mObject);
+    mObject = NULL;
+}
+
+// static
+sp<ICrypto> JCrypto::MakeCrypto() {
+    sp<IServiceManager> sm = defaultServiceManager();
+
+    sp<IBinder> binder =
+        sm->getService(String16("media.player"));
+
+    sp<IMediaPlayerService> service =
+        interface_cast<IMediaPlayerService>(binder);
+
+    if (service == NULL) {
+        return NULL;
+    }
+
+    sp<ICrypto> crypto = service->makeCrypto();
+
+    if (crypto == NULL || crypto->initCheck() != OK) {
+        return NULL;
+    }
+
+    return crypto;
+}
+
+// static
+sp<ICrypto> JCrypto::MakeCrypto(
+        const uint8_t uuid[16], const void *initData, size_t initSize) {
+    sp<ICrypto> crypto = MakeCrypto();
+
+    if (crypto == NULL) {
+        return NULL;
+    }
+
+    status_t err = crypto->createPlugin(uuid, initData, initSize);
+
+    if (err != OK) {
+        return NULL;
+    }
+
+    return crypto;
+}
+
+bool JCrypto::requiresSecureDecoderComponent(const char *mime) const {
+    if (mCrypto == NULL) {
+        return false;
+    }
+
+    return mCrypto->requiresSecureDecoderComponent(mime);
+}
+
+// static
+bool JCrypto::IsCryptoSchemeSupported(const uint8_t uuid[16]) {
+    sp<ICrypto> crypto = MakeCrypto();
+
+    if (crypto == NULL) {
+        return false;
+    }
+
+    return crypto->isCryptoSchemeSupported(uuid);
+}
+
+status_t JCrypto::initCheck() const {
+    return mCrypto == NULL ? NO_INIT : OK;
+}
+
+// static
+sp<ICrypto> JCrypto::GetCrypto(JNIEnv *env, jobject obj) {
+    jclass clazz = env->FindClass("android/media/Crypto");
+    CHECK(clazz != NULL);
+
+    if (!env->IsInstanceOf(obj, clazz)) {
+        return NULL;
+    }
+
+    sp<JCrypto> jcrypto = getCrypto(env, obj);
+
+    if (jcrypto == NULL) {
+        return NULL;
+    }
+
+    return jcrypto->mCrypto;
+}
+
+}  // namespace android
+
+using namespace android;
+
+static sp<JCrypto> setCrypto(
+        JNIEnv *env, jobject thiz, const sp<JCrypto> &crypto) {
+    sp<JCrypto> old = (JCrypto *)env->GetIntField(thiz, gFields.context);
+    if (crypto != NULL) {
+        crypto->incStrong(thiz);
+    }
+    if (old != NULL) {
+        old->decStrong(thiz);
+    }
+    env->SetIntField(thiz, gFields.context, (int)crypto.get());
+
+    return old;
+}
+
+static void android_media_Crypto_release(JNIEnv *env, jobject thiz) {
+    setCrypto(env, thiz, NULL);
+}
+
+static void android_media_Crypto_native_init(JNIEnv *env) {
+    jclass clazz = env->FindClass("android/media/Crypto");
+    CHECK(clazz != NULL);
+
+    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+    CHECK(gFields.context != NULL);
+}
+
+static void android_media_Crypto_native_setup(
+        JNIEnv *env, jobject thiz,
+        jbyteArray uuidObj, jbyteArray initDataObj) {
+    jsize uuidLength = env->GetArrayLength(uuidObj);
+
+    if (uuidLength != 16) {
+        jniThrowException(
+                env,
+                "java/lang/IllegalArgumentException",
+                NULL);
+        return;
+    }
+
+    jboolean isCopy;
+    jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
+
+    jsize initDataLength = env->GetArrayLength(initDataObj);
+    jbyte *initData = env->GetByteArrayElements(initDataObj, &isCopy);
+
+    sp<JCrypto> crypto = new JCrypto(
+            env, thiz, (const uint8_t *)uuid, initData, initDataLength);
+
+    status_t err = crypto->initCheck();
+
+    env->ReleaseByteArrayElements(initDataObj, initData, 0);
+    initData = NULL;
+
+    env->ReleaseByteArrayElements(uuidObj, uuid, 0);
+    uuid = NULL;
+
+    if (err != OK) {
+        jniThrowException(
+                env,
+                "java/io/IOException",
+                "Failed to instantiate crypto object.");
+        return;
+    }
+
+    setCrypto(env,thiz, crypto);
+}
+
+static void android_media_Crypto_native_finalize(
+        JNIEnv *env, jobject thiz) {
+    android_media_Crypto_release(env, thiz);
+}
+
+static jboolean android_media_Crypto_isCryptoSchemeSupported(
+        JNIEnv *env, jobject thiz, jbyteArray uuidObj) {
+    jsize uuidLength = env->GetArrayLength(uuidObj);
+
+    if (uuidLength != 16) {
+        jniThrowException(
+                env,
+                "java/lang/IllegalArgumentException",
+                NULL);
+        return false;
+    }
+
+    jboolean isCopy;
+    jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
+
+    bool result = JCrypto::IsCryptoSchemeSupported((const uint8_t *)uuid);
+
+    env->ReleaseByteArrayElements(uuidObj, uuid, 0);
+    uuid = NULL;
+
+    return result;
+}
+
+static jboolean android_media_Crypto_requiresSecureDecoderComponent(
+        JNIEnv *env, jobject thiz, jstring mimeObj) {
+    if (mimeObj == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return false;
+    }
+
+    sp<JCrypto> crypto = getCrypto(env, thiz);
+
+    if (crypto == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return false;
+    }
+
+    const char *mime = env->GetStringUTFChars(mimeObj, NULL);
+
+    if (mime == NULL) {
+        return false;
+    }
+
+    bool result = crypto->requiresSecureDecoderComponent(mime);
+
+    env->ReleaseStringUTFChars(mimeObj, mime);
+    mime = NULL;
+
+    return result;
+}
+
+static JNINativeMethod gMethods[] = {
+    { "release", "()V", (void *)android_media_Crypto_release },
+    { "native_init", "()V", (void *)android_media_Crypto_native_init },
+
+    { "native_setup", "([B[B)V",
+      (void *)android_media_Crypto_native_setup },
+
+    { "native_finalize", "()V",
+      (void *)android_media_Crypto_native_finalize },
+
+    { "isCryptoSchemeSupported", "([B)Z",
+      (void *)android_media_Crypto_isCryptoSchemeSupported },
+
+    { "requiresSecureDecoderComponent", "(Ljava/lang/String;)Z",
+      (void *)android_media_Crypto_requiresSecureDecoderComponent },
+};
+
+int register_android_media_Crypto(JNIEnv *env) {
+    return AndroidRuntime::registerNativeMethods(env,
+                "android/media/Crypto", gMethods, NELEM(gMethods));
+}
+
diff --git a/media/jni/android_media_Crypto.h b/media/jni/android_media_Crypto.h
new file mode 100644
index 0000000..505725e
--- /dev/null
+++ b/media/jni/android_media_Crypto.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_MEDIA_CRYPTO_H_
+#define _ANDROID_MEDIA_CRYPTO_H_
+
+#include "jni.h"
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ICrypto;
+
+struct JCrypto : public RefBase {
+    static bool IsCryptoSchemeSupported(const uint8_t uuid[16]);
+
+    JCrypto(JNIEnv *env, jobject thiz,
+            const uint8_t uuid[16], const void *initData, size_t initSize);
+
+    status_t initCheck() const;
+
+    bool requiresSecureDecoderComponent(const char *mime) const;
+
+    static sp<ICrypto> GetCrypto(JNIEnv *env, jobject obj);
+
+protected:
+    virtual ~JCrypto();
+
+private:
+    jweak mObject;
+    sp<ICrypto> mCrypto;
+
+    static sp<ICrypto> MakeCrypto();
+
+    static sp<ICrypto> MakeCrypto(
+            const uint8_t uuid[16], const void *initData, size_t initSize);
+
+    DISALLOW_EVIL_CONSTRUCTORS(JCrypto);
+};
+
+}  // namespace android
+
+#endif  // _ANDROID_MEDIA_CRYPTO_H_
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 4b7a811..217216a 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -20,6 +20,7 @@
 
 #include "android_media_MediaCodec.h"
 
+#include "android_media_Crypto.h"
 #include "android_media_Utils.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/android_view_Surface.h"
@@ -98,12 +99,13 @@
 status_t JMediaCodec::configure(
         const sp<AMessage> &format,
         const sp<ISurfaceTexture> &surfaceTexture,
+        const sp<ICrypto> &crypto,
         int flags) {
     sp<SurfaceTextureClient> client;
     if (surfaceTexture != NULL) {
         client = new SurfaceTextureClient(surfaceTexture);
     }
-    return mCodec->configure(format, client, NULL /* crypto */, flags);
+    return mCodec->configure(format, client, crypto, flags);
 }
 
 status_t JMediaCodec::start() {
@@ -256,6 +258,7 @@
         jobject thiz,
         jobjectArray keys, jobjectArray values,
         jobject jsurface,
+        jobject jcrypto,
         jint flags) {
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
@@ -286,7 +289,12 @@
         }
     }
 
-    err = codec->configure(format, surfaceTexture, flags);
+    sp<ICrypto> crypto;
+    if (jcrypto != NULL) {
+        crypto = JCrypto::GetCrypto(env, jcrypto);
+    }
+
+    err = codec->configure(format, surfaceTexture, crypto, flags);
 
     throwExceptionAsNecessary(env, err);
 }
@@ -513,7 +521,8 @@
     { "release", "()V", (void *)android_media_MediaCodec_release },
 
     { "native_configure",
-      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;I)V",
+      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
+      "Landroid/media/Crypto;I)V",
       (void *)android_media_MediaCodec_native_configure },
 
     { "start", "()V", (void *)android_media_MediaCodec_start },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 6b1257d..6bb4071 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -27,6 +27,7 @@
 
 struct ALooper;
 struct AMessage;
+struct ICrypto;
 struct ISurfaceTexture;
 struct MediaCodec;
 
@@ -40,6 +41,7 @@
     status_t configure(
             const sp<AMessage> &format,
             const sp<ISurfaceTexture> &surfaceTexture,
+            const sp<ICrypto> &crypto,
             int flags);
 
     status_t start();
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 3074bb1..2e74ffd 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -879,6 +879,7 @@
                 "android/media/MediaPlayer", gMethods, NELEM(gMethods));
 }
 
+extern int register_android_media_Crypto(JNIEnv *env);
 extern int register_android_media_MediaCodec(JNIEnv *env);
 extern int register_android_media_MediaExtractor(JNIEnv *env);
 extern int register_android_media_MediaCodecList(JNIEnv *env);
@@ -968,6 +969,11 @@
         goto bail;
     }
 
+    if (register_android_media_Crypto(env) < 0) {
+        ALOGE("ERROR: MediaCodec native registration failed");
+        goto bail;
+    }
+
     /* success -- return valid version number */
     result = JNI_VERSION_1_4;
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java
index c501d3f..7be2707 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java
@@ -22,7 +22,7 @@
 import com.android.mediaframeworktest.functional.CodecTest;
 
 import android.content.Context;
-import android.test.ActivityInstrumentationTestCase;
+import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.MediumTest;
@@ -33,25 +33,28 @@
 /**
  * Junit / Instrumentation test case for the media player api
  */
-public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFrameworkTest> {
-   private boolean duratoinWithinTolerence = false;
-   private String TAG = "MediaPlayerApiTest";
-   private boolean isWMAEnable = false;
-   private boolean isWMVEnable = false;
+public class MediaPlayerApiTest extends  ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+    private boolean duratoinWithinTolerence = false;
+    private String TAG = "MediaPlayerApiTest";
+    private boolean isWMAEnable = false;
+    private boolean isWMVEnable = false;
 
-   Context mContext;
+    Context mContext;
 
-   public MediaPlayerApiTest() {
-     super("com.android.mediaframeworktest", MediaFrameworkTest.class);
-     isWMAEnable = MediaProfileReader.getWMAEnable();
-     isWMVEnable = MediaProfileReader.getWMVEnable();
-   }
+    public MediaPlayerApiTest() {
+       super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+       isWMAEnable = MediaProfileReader.getWMAEnable();
+       isWMVEnable = MediaProfileReader.getWMVEnable();
+    }
 
     protected void setUp() throws Exception {
-      super.setUp();
-   
-  }
-    
+       //Insert a 2 second before launching the test activity. This is
+       //the workaround for the race condition of requesting the updated surface.
+       Thread.sleep(2000);
+       getActivity();
+       super.setUp();
+    }
+
     public boolean verifyDuration(int duration, int expectedDuration){
       if ((duration > expectedDuration * 1.1) || (duration < expectedDuration * 0.9))
          return false;
diff --git a/test-runner/src/android/test/AndroidTestRunner.java b/test-runner/src/android/test/AndroidTestRunner.java
index fc9832c..30876d0 100644
--- a/test-runner/src/android/test/AndroidTestRunner.java
+++ b/test-runner/src/android/test/AndroidTestRunner.java
@@ -28,6 +28,7 @@
 import junit.framework.TestSuite;
 import junit.runner.BaseTestRunner;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.util.List;
 
@@ -91,15 +92,35 @@
 
     private TestCase buildSingleTestMethod(Class testClass, String testMethodName) {
         try {
-            TestCase testCase = (TestCase) testClass.newInstance();
+            Constructor c = testClass.getConstructor();
+            return newSingleTestMethod(testClass, testMethodName, c);
+        } catch (NoSuchMethodException e) {
+        }
+
+        try {
+            Constructor c = testClass.getConstructor(String.class);
+            return newSingleTestMethod(testClass, testMethodName, c, testMethodName);
+        } catch (NoSuchMethodException e) {
+        }
+
+        return null;
+    }
+
+    private TestCase newSingleTestMethod(Class testClass, String testMethodName,
+            Constructor constructor, Object... args) {
+        try {
+            TestCase testCase = (TestCase) constructor.newInstance(args);
             testCase.setName(testMethodName);
             return testCase;
         } catch (IllegalAccessException e) {
             runFailed("Could not access test class. Class: " + testClass.getName());
         } catch (InstantiationException e) {
             runFailed("Could not instantiate test class. Class: " + testClass.getName());
+        } catch (IllegalArgumentException e) {
+            runFailed("Illegal argument passed to constructor. Class: " + testClass.getName());
+        } catch (InvocationTargetException e) {
+            runFailed("Constructor thew an exception. Class: " + testClass.getName());
         }
-
         return null;
     }
 
diff --git a/tests/BiDiTests/res/layout/textview_alignment_ltr.xml b/tests/BiDiTests/res/layout/textview_alignment_ltr.xml
new file mode 100644
index 0000000..0e1adba
--- /dev/null
+++ b/tests/BiDiTests/res/layout/textview_alignment_ltr.xml
@@ -0,0 +1,578 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/textview_alignment_ltr"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:layoutDirection="ltr">
+
+        <TableLayout android:orientation="vertical"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content">
+
+            <TableRow>
+                <TextView android:text="(unspecified)"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="gravity (default)"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="gravity"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="gravity"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="gravity left"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="gravity"
+                          android:gravity="left"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="gravity"
+                          android:gravity="left"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="gravity right"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="gravity"
+                          android:gravity="right"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="gravity"
+                          android:gravity="right"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="gravity start"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="gravity"
+                          android:gravity="start"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="gravity"
+                          android:gravity="start"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="gravity end"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="gravity"
+                          android:gravity="end"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="gravity"
+                          android:gravity="end"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="gravity center"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="gravity"
+                          android:gravity="center"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="gravity"
+                          android:gravity="center"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="gravity center_horizontal"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="gravity"
+                          android:gravity="center_horizontal"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="gravity"
+                          android:gravity="center_horizontal"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="center"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="center"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="center"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="textStart"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="textStart"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="textStart"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="textEnd"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="textEnd"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="textEnd"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="viewStart"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginRight="7dip"
+                          android:layout_marginLeft="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="viewStart"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="viewStart"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="viewEnd"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginRight="7dip"
+                          android:layout_marginLeft="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="viewEnd"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="viewEnd"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow android:textAlignment="gravity"
+                      android:gravity="center_horizontal">
+
+                <TextView android:text="inherit gravity (default)"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginRight="7dip"
+                          android:layout_marginLeft="7dip"
+                        />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                        />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                        />
+            </TableRow>
+
+            <TableRow android:textAlignment="center">
+
+                <TextView android:text="inherit gravity center"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginRight="7dip"
+                          android:layout_marginLeft="7dip"
+                        />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                        />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                        />
+            </TableRow>
+
+            <TableRow android:textAlignment="textStart">
+
+                <TextView android:text="inherit textStart"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginRight="7dip"
+                          android:layout_marginLeft="7dip"
+                        />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                        />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                        />
+            </TableRow>
+
+            <TableRow android:textAlignment="textEnd">
+
+                <TextView android:text="inherit textEnd"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginRight="7dip"
+                          android:layout_marginLeft="7dip"
+                        />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                        />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                        />
+            </TableRow>
+
+            <TableRow android:textAlignment="viewStart">
+
+                <TextView android:text="inherit viewStart"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginRight="7dip"
+                          android:layout_marginLeft="7dip"
+                        />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                        />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                        />
+            </TableRow>
+
+            <TableRow android:textAlignment="viewEnd">
+
+                <TextView android:text="inherit viewEnd"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginRight="7dip"
+                          android:layout_marginLeft="7dip"
+                        />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/textview_text"
+                          android:textAlignment="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                        />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="200dip"
+                          android:textSize="24dip"
+                          android:text="@string/hebrew_text"
+                          android:textAlignment="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                        />
+            </TableRow>
+
+        </TableLayout>
+
+</FrameLayout>
diff --git a/tests/BiDiTests/res/layout/textview_alignment_rtl.xml b/tests/BiDiTests/res/layout/textview_alignment_rtl.xml
new file mode 100644
index 0000000..12a90d5
--- /dev/null
+++ b/tests/BiDiTests/res/layout/textview_alignment_rtl.xml
@@ -0,0 +1,578 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:id="@+id/textview_alignment_rtl"
+             android:layout_width="fill_parent"
+             android:layout_height="fill_parent"
+             android:layoutDirection="rtl">
+
+    <TableLayout android:orientation="vertical"
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content">
+
+        <TableRow>
+            <TextView android:text="(unspecified)"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow>
+            <TextView android:text="gravity (default)"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="gravity"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="gravity"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow>
+            <TextView android:text="gravity left"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="gravity"
+                      android:gravity="left"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="gravity"
+                      android:gravity="left"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow>
+            <TextView android:text="gravity right"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="gravity"
+                      android:gravity="right"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="gravity"
+                      android:gravity="right"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow>
+            <TextView android:text="gravity start"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="gravity"
+                      android:gravity="start"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="gravity"
+                      android:gravity="start"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow>
+            <TextView android:text="gravity end"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="gravity"
+                      android:gravity="end"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="gravity"
+                      android:gravity="end"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow>
+            <TextView android:text="gravity center"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="gravity"
+                      android:gravity="center"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="gravity"
+                      android:gravity="center"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow>
+            <TextView android:text="gravity center_horizontal"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="gravity"
+                      android:gravity="center_horizontal"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="gravity"
+                      android:gravity="center_horizontal"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow>
+            <TextView android:text="center"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="center"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="center"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow>
+            <TextView android:text="textStart"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="textStart"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="textStart"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow>
+            <TextView android:text="textEnd"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="textEnd"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="textEnd"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow>
+            <TextView android:text="viewStart"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginRight="7dip"
+                      android:layout_marginLeft="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="viewStart"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="viewStart"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow>
+            <TextView android:text="viewEnd"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginRight="7dip"
+                      android:layout_marginLeft="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="viewEnd"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="viewEnd"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow android:textAlignment="gravity"
+                  android:gravity="center_horizontal">
+
+            <TextView android:text="inherit gravity (default)"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginRight="7dip"
+                      android:layout_marginLeft="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="inherit"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="inherit"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow android:textAlignment="center">
+
+            <TextView android:text="inherit gravity center"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginRight="7dip"
+                      android:layout_marginLeft="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="inherit"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="inherit"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow android:textAlignment="textStart">
+
+            <TextView android:text="inherit textStart"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginRight="7dip"
+                      android:layout_marginLeft="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="inherit"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="inherit"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow android:textAlignment="textEnd">
+
+            <TextView android:text="inherit textEnd"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginRight="7dip"
+                      android:layout_marginLeft="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="inherit"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="inherit"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow android:textAlignment="viewStart">
+
+            <TextView android:text="inherit viewStart"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginRight="7dip"
+                      android:layout_marginLeft="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="inherit"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="inherit"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+        <TableRow android:textAlignment="viewEnd">
+
+            <TextView android:text="inherit viewEnd"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:typeface="serif"
+                      android:layout_marginRight="7dip"
+                      android:layout_marginLeft="7dip"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textAlignment="inherit"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="200dip"
+                      android:textSize="24dip"
+                      android:text="@string/hebrew_text"
+                      android:textAlignment="inherit"
+                      android:layout_marginLeft="7dip"
+                      android:layout_marginRight="7dip"
+                      android:background="#444444"
+                    />
+        </TableRow>
+
+    </TableLayout>
+
+</FrameLayout>
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
index c5a1235..209597e 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -104,6 +104,16 @@
         addItem(result, "Canvas", BiDiTestCanvas.class, R.id.canvas);
         addItem(result, "Canvas2", BiDiTestCanvas2.class, R.id.canvas2);
 
+        addItem(result, "TextView LTR", BiDiTestTextViewLtr.class, R.id.textview_ltr);
+        addItem(result, "TextView RTL", BiDiTestTextViewRtl.class, R.id.textview_rtl);
+        addItem(result, "TextView LOC", BiDiTestTextViewLocale.class, R.id.textview_locale);
+
+        addItem(result, "TextDirection LTR", BiDiTestTextViewDirectionLtr.class, R.id.textview_direction_ltr);
+        addItem(result, "TextDirection RTL", BiDiTestTextViewDirectionRtl.class, R.id.textview_direction_rtl);
+
+        addItem(result, "TextAlignment LTR", BiDiTestTextViewAlignmentLtr.class, R.id.textview_alignment_ltr);
+        addItem(result, "TextAlignment RTL", BiDiTestTextViewAlignmentRtl.class, R.id.textview_alignment_rtl);
+
         addItem(result, "Linear LTR", BiDiTestLinearLayoutLtr.class, R.id.linear_layout_ltr);
         addItem(result, "Linear RTL", BiDiTestLinearLayoutRtl.class, R.id.linear_layout_rtl);
         addItem(result, "Linear LOC", BiDiTestLinearLayoutLocale.class, R.id.linear_layout_locale);
@@ -134,15 +144,9 @@
 
         addItem(result, "Margin MIXED", BiDiTestViewGroupMarginMixed.class, R.id.view_group_margin_mixed);
 
-        addItem(result, "TextView LTR", BiDiTestTextViewLtr.class, R.id.textview_ltr);
-        addItem(result, "TextView RTL", BiDiTestTextViewRtl.class, R.id.textview_rtl);
-        addItem(result, "TextView LOC", BiDiTestTextViewLocale.class, R.id.textview_locale);
-
-        addItem(result, "TextDirection LTR", BiDiTestTextViewDirectionLtr.class, R.id.textview_direction_ltr);
-        addItem(result, "TextDirection RTL", BiDiTestTextViewDirectionRtl.class, R.id.textview_direction_rtl);
-
         addItem(result, "TextView Drawables LTR", BiDiTestTextViewDrawablesLtr.class, R.id.textview_drawables_ltr);
         addItem(result, "TextView Drawables RTL", BiDiTestTextViewDrawablesRtl.class, R.id.textview_drawables_rtl);
+
         addItem(result, "Gallery LTR", BiDiTestGalleryLtr.class, R.id.gallery_ltr);
         addItem(result, "Gallery RTL", BiDiTestGalleryRtl.class, R.id.gallery_rtl);
 
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentLtr.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentLtr.java
new file mode 100644
index 0000000..5ea5d81
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentLtr.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bidi;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class BiDiTestTextViewAlignmentLtr extends Fragment {
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.textview_alignment_ltr, container, false);
+    }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentRtl.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentRtl.java
new file mode 100644
index 0000000..fcc7a5d
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentRtl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bidi;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class BiDiTestTextViewAlignmentRtl extends Fragment {
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.textview_alignment_rtl, container, false);
+    }
+}
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
index 87baf76..7c03313 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
@@ -23,7 +23,6 @@
 import android.webkit.WebSettingsClassic;
 import android.webkit.WebView;
 import android.webkit.WebViewClassic;
-import android.widget.Toast;
 
 import java.util.ArrayList;
 
@@ -72,10 +71,7 @@
             mContext = c;
         }
 
-        /** Show a toast from the web page */
         public void animationComplete() {
-            Toast.makeText(mContext, "Animation complete!", Toast.LENGTH_SHORT).show();
-            //Log.d(LOGTAG, "anim complete");
             mAnimationTime = System.currentTimeMillis();
         }
     }
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 2b9b056..9de685a 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -57,6 +57,13 @@
     bool is9Patch;
     Res_png_9patch info9Patch;
 
+    // Layout padding, if relevant
+    bool haveLayoutBounds;
+    int32_t layoutBoundsLeft;
+    int32_t layoutBoundsTop;
+    int32_t layoutBoundsRight;
+    int32_t layoutBoundsBottom;
+
     png_uint_32 allocHeight;
     png_bytepp allocRows;
 };
@@ -129,33 +136,62 @@
        &interlace_type, &compression_type, NULL);
 }
 
-static bool is_tick(png_bytep p, bool transparent, const char** outError)
+#define COLOR_TRANSPARENT 0
+#define COLOR_WHITE 0xFFFFFFFF
+#define COLOR_TICK  0xFF000000
+#define COLOR_LAYOUT_BOUNDS_TICK 0xFF0000FF
+
+enum {
+    TICK_TYPE_NONE,
+    TICK_TYPE_TICK,
+    TICK_TYPE_LAYOUT_BOUNDS,
+    TICK_TYPE_BOTH
+};
+
+static int tick_type(png_bytep p, bool transparent, const char** outError)
 {
+    png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+
     if (transparent) {
         if (p[3] == 0) {
-            return false;
+            return TICK_TYPE_NONE;
         }
+        if (color == COLOR_LAYOUT_BOUNDS_TICK) {
+            return TICK_TYPE_LAYOUT_BOUNDS;
+        }
+        if (color == COLOR_TICK) {
+            return TICK_TYPE_TICK;
+        }
+
+        // Error cases
         if (p[3] != 0xff) {
             *outError = "Frame pixels must be either solid or transparent (not intermediate alphas)";
-            return false;
+            return TICK_TYPE_NONE;
         }
         if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
-            *outError = "Ticks in transparent frame must be black";
+            *outError = "Ticks in transparent frame must be black or red";
         }
-        return true;
+        return TICK_TYPE_TICK;
     }
 
     if (p[3] != 0xFF) {
         *outError = "White frame must be a solid color (no alpha)";
     }
-    if (p[0] == 0xFF && p[1] == 0xFF && p[2] == 0xFF) {
-        return false;
+    if (color == COLOR_WHITE) {
+        return TICK_TYPE_NONE;
     }
+    if (color == COLOR_TICK) {
+        return TICK_TYPE_TICK;
+    }
+    if (color == COLOR_LAYOUT_BOUNDS_TICK) {
+        return TICK_TYPE_LAYOUT_BOUNDS;
+    }
+
     if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
-        *outError = "Ticks in white frame must be black";
-        return false;
+        *outError = "Ticks in white frame must be black or red";
+        return TICK_TYPE_NONE;
     }
-    return true;
+    return TICK_TYPE_TICK;
 }
 
 enum {
@@ -175,7 +211,7 @@
     bool found = false;
 
     for (i=1; i<width-1; i++) {
-        if (is_tick(row+i*4, transparent, outError)) {
+        if (TICK_TYPE_TICK == tick_type(row+i*4, transparent, outError)) {
             if (state == TICK_START ||
                 (state == TICK_OUTSIDE_1 && multipleAllowed)) {
                 *outLeft = i-1;
@@ -224,7 +260,7 @@
     bool found = false;
 
     for (i=1; i<height-1; i++) {
-        if (is_tick(rows[i]+offset, transparent, outError)) {
+        if (TICK_TYPE_TICK == tick_type(rows[i]+offset, transparent, outError)) {
             if (state == TICK_START ||
                 (state == TICK_OUTSIDE_1 && multipleAllowed)) {
                 *outTop = i-1;
@@ -262,6 +298,83 @@
     return NO_ERROR;
 }
 
+static status_t get_horizontal_layout_bounds_ticks(
+        png_bytep row, int width, bool transparent, bool required,
+        int32_t* outLeft, int32_t* outRight, const char** outError)
+{
+    int i;
+    *outLeft = *outRight = 0;
+
+    // Look for left tick
+    if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + 4, transparent, outError)) {
+        // Starting with a layout padding tick
+        i = 1;
+        while (i < width - 1) {
+            (*outLeft)++;
+            i++;
+            int tick = tick_type(row + i * 4, transparent, outError);
+            if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
+                break;
+            }
+        }
+    }
+
+    // Look for right tick
+    if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + (width - 2) * 4, transparent, outError)) {
+        // Ending with a layout padding tick
+        i = width - 2;
+        while (i > 1) {
+            (*outRight)++;
+            i--;
+            int tick = tick_type(row+i*4, transparent, outError);
+            if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
+                break;
+            }
+        }
+    }
+
+    return NO_ERROR;
+}
+
+static status_t get_vertical_layout_bounds_ticks(
+        png_bytepp rows, int offset, int height, bool transparent, bool required,
+        int32_t* outTop, int32_t* outBottom, const char** outError)
+{
+    int i;
+    *outTop = *outBottom = 0;
+
+    // Look for top tick
+    if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[1] + offset, transparent, outError)) {
+        // Starting with a layout padding tick
+        i = 1;
+        while (i < height - 1) {
+            (*outTop)++;
+            i++;
+            int tick = tick_type(rows[i] + offset, transparent, outError);
+            if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
+                break;
+            }
+        }
+    }
+
+    // Look for bottom tick
+    if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[height - 2] + offset, transparent, outError)) {
+        // Ending with a layout padding tick
+        i = height - 2;
+        while (i > 1) {
+            (*outBottom)++;
+            i--;
+            int tick = tick_type(rows[i] + offset, transparent, outError);
+            if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
+                break;
+            }
+        }
+    }
+
+    return NO_ERROR;
+}
+
+
 static uint32_t get_color(
     png_bytepp rows, int left, int top, int right, int bottom)
 {
@@ -353,6 +466,9 @@
     image->info9Patch.paddingLeft = image->info9Patch.paddingRight =
         image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
 
+    image->layoutBoundsLeft = image->layoutBoundsRight =
+        image->layoutBoundsTop = image->layoutBoundsBottom = 0;
+
     png_bytep p = image->rows[0];
     bool transparent = p[3] == 0;
     bool hasColor = false;
@@ -408,6 +524,25 @@
         goto getout;
     }
 
+    // Find left and right of layout padding...
+    get_horizontal_layout_bounds_ticks(image->rows[H-1], W, transparent, false,
+                                        &image->layoutBoundsLeft,
+                                        &image->layoutBoundsRight, &errorMsg);
+
+    get_vertical_layout_bounds_ticks(image->rows, (W-1)*4, H, transparent, false,
+                                        &image->layoutBoundsTop,
+                                        &image->layoutBoundsBottom, &errorMsg);
+
+    image->haveLayoutBounds = image->layoutBoundsLeft != 0
+                               || image->layoutBoundsRight != 0
+                               || image->layoutBoundsTop != 0
+                               || image->layoutBoundsBottom != 0;
+
+    if (image->haveLayoutBounds) {
+        NOISY(printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
+                image->layoutBoundsRight, image->layoutBoundsBottom));
+    }
+
     // Copy patch data into image
     image->info9Patch.numXDivs = numXDivs;
     image->info9Patch.numYDivs = numYDivs;
@@ -845,8 +980,9 @@
     int bit_depth, interlace_type, compression_type;
     int i;
 
-    png_unknown_chunk unknowns[1];
+    png_unknown_chunk unknowns[2];
     unknowns[0].data = NULL;
+    unknowns[1].data = NULL;
 
     png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * png_sizeof(png_bytep));
     if (outRows == (png_bytepp) 0) {
@@ -916,23 +1052,42 @@
     }
 
     if (imageInfo.is9Patch) {
+        int chunk_count = 1 + (imageInfo.haveLayoutBounds ? 1 : 0);
+        int p_index = imageInfo.haveLayoutBounds ? 1 : 0;
+        int b_index = 0;
+        png_byte *chunk_names = imageInfo.haveLayoutBounds
+                ? (png_byte*)"npLb\0npTc\0"
+                : (png_byte*)"npTc";
         NOISY(printf("Adding 9-patch info...\n"));
-        strcpy((char*)unknowns[0].name, "npTc");
-        unknowns[0].data = (png_byte*)imageInfo.info9Patch.serialize();
-        unknowns[0].size = imageInfo.info9Patch.serializedSize();
+        strcpy((char*)unknowns[p_index].name, "npTc");
+        unknowns[p_index].data = (png_byte*)imageInfo.info9Patch.serialize();
+        unknowns[p_index].size = imageInfo.info9Patch.serializedSize();
         // TODO: remove the check below when everything works
-        checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[0].data);
+        checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data);
+
+        if (imageInfo.haveLayoutBounds) {
+            int chunk_size = sizeof(png_uint_32) * 4;
+            strcpy((char*)unknowns[b_index].name, "npLb");
+            unknowns[b_index].data = (png_byte*) calloc(chunk_size, 1);
+            memcpy(unknowns[b_index].data, &imageInfo.layoutBoundsLeft, chunk_size);
+            unknowns[b_index].size = chunk_size;
+        }
+
         png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
-                                    (png_byte*)"npTc", 1);
-        png_set_unknown_chunks(write_ptr, write_info, unknowns, 1);
+                                    chunk_names, chunk_count);
+        png_set_unknown_chunks(write_ptr, write_info, unknowns, chunk_count);
         // XXX I can't get this to work without forcibly changing
         // the location to what I want...  which apparently is supposed
         // to be a private API, but everything else I have tried results
         // in the location being set to what I -last- wrote so I never
         // get written. :p
         png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE);
+        if (imageInfo.haveLayoutBounds) {
+            png_set_unknown_chunk_location(write_ptr, write_info, 1, PNG_HAVE_PLTE);
+        }
     }
 
+
     png_write_info(write_ptr, write_info);
 
     png_bytepp rows;
@@ -954,6 +1109,7 @@
     }
     free(outRows);
     free(unknowns[0].data);
+    free(unknowns[1].data);
 
     png_get_IHDR(write_ptr, write_info, &width, &height,
        &bit_depth, &color_type, &interlace_type,
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index eadec02..b76b8cf 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -524,7 +524,8 @@
         int nativeInt = sManager.addNewDelegate(delegate);
 
         // and create/return a new Bitmap with it
-        return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, density);
+        return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, 
+                density);
     }
 
     /**