Merge "Fix issue #3194697: default orientation for portrait-mode apps on stingray is backward?"
diff --git a/api/current.xml b/api/current.xml
index e185858..a30426a 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -26765,6 +26765,21 @@
 <parameter name="day" type="int">
 </parameter>
 </method>
+<method name="setRange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startYear" type="int">
+</parameter>
+<parameter name="endYear" type="int">
+</parameter>
+</method>
 <method name="updateDate"
  return="void"
  abstract="false"
@@ -185102,6 +185117,25 @@
 <parameter name="event" type="android.view.KeyEvent">
 </parameter>
 </method>
+<method name="forwardDelete"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+<parameter name="content" type="android.text.Editable">
+</parameter>
+<parameter name="keyCode" type="int">
+</parameter>
+<parameter name="event" type="android.view.KeyEvent">
+</parameter>
+</method>
 <method name="onKeyOther"
  return="boolean"
  abstract="false"
@@ -185324,25 +185358,6 @@
 <parameter name="event" type="android.view.KeyEvent">
 </parameter>
 </method>
-<method name="forwardDelete"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="view" type="android.view.View">
-</parameter>
-<parameter name="content" type="android.text.Editable">
-</parameter>
-<parameter name="keyCode" type="int">
-</parameter>
-<parameter name="event" type="android.view.KeyEvent">
-</parameter>
-</method>
 <method name="onKeyOther"
  return="boolean"
  abstract="false"
@@ -235118,6 +235133,21 @@
 <parameter name="onDateChangedListener" type="android.widget.DatePicker.OnDateChangedListener">
 </parameter>
 </method>
+<method name="setRange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startYear" type="int">
+</parameter>
+<parameter name="endYear" type="int">
+</parameter>
+</method>
 <method name="updateDate"
  return="void"
  abstract="false"
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 448b3ef..e6e55ee 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
-import android.os.Build;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -118,6 +117,28 @@
         mDatePicker.init(year, month, day, null);
     }
 
+    /**
+     * Sets the range of years in which dates can be selected.
+     * <p>
+     * Note: If the range is set to a value that does not include the currently
+     * selected date the value of the picker shown by this dialog will be
+     * updated to the closest date in the range.
+     * </p>
+     *
+     * @param startYear The start year of the range.
+     * @param endYear The end year of the range.
+     */
+    public void setRange(int startYear, int endYear) {
+        mDatePicker.setRange(startYear, endYear);
+    }
+
+    /**
+     * Sets the current date.
+     *
+     * @param year The date year.
+     * @param monthOfYear The date month.
+     * @param dayOfMonth The date day of month.
+     */
     public void updateDate(int year, int monthOfYear, int dayOfMonth) {
         mDatePicker.updateDate(year, monthOfYear, dayOfMonth);
     }
diff --git a/core/java/android/nfc/technology/Ndef.java b/core/java/android/nfc/technology/Ndef.java
index cd12249..53db0c5 100644
--- a/core/java/android/nfc/technology/Ndef.java
+++ b/core/java/android/nfc/technology/Ndef.java
@@ -140,6 +140,7 @@
             return null;
         }
     }
+
     /**
      * Overwrite the primary NDEF message
      * @throws IOException
diff --git a/core/java/android/nfc/technology/NdefFormatable.java b/core/java/android/nfc/technology/NdefFormatable.java
index 899b95f..222c558 100644
--- a/core/java/android/nfc/technology/NdefFormatable.java
+++ b/core/java/android/nfc/technology/NdefFormatable.java
@@ -45,16 +45,6 @@
     }
 
     /**
-     * Returns whether a tag can be formatted with {@link
-     * NdefFormatable#format(NdefMessage)}
-     */
-    public boolean canBeFormatted() throws IOException {
-        checkConnected();
-
-        throw new UnsupportedOperationException();
-    }
-
-    /**
      * Formats a tag as NDEF, if possible. You may supply a first
      * NdefMessage to be written on the tag.
      */
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 55d3b16..a4ad97b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5830,6 +5830,7 @@
                 mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
                 invalidate();
             }
+            mBackgroundSizeChanged = true;
         }
     }
 
@@ -5882,6 +5883,7 @@
                 mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
                 invalidate();
             }
+            mBackgroundSizeChanged = true;
         }
     }
 
@@ -5937,7 +5939,7 @@
                 mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
                 invalidate();
             }
-
+            mBackgroundSizeChanged = true;
         }
     }
 
@@ -5990,6 +5992,7 @@
                 mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
                 invalidate();
             }
+            mBackgroundSizeChanged = true;
         }
     }
 
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index e98c0bd..668490d 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -104,6 +104,14 @@
             }
         };
 
+        // mini-month day-picker
+        mMiniMonthDayPicker = (DayPicker) findViewById(R.id.mini_month_day_picker);
+        mMiniMonthDayPicker.setOnDateChangeListener(new DayPicker.OnSelectedDayChangeListener() {
+            public void onSelectedDayChange(DayPicker view, int year, int month, int monthDay) {
+                updateDateUnchecked(year, month, monthDay);
+            }
+        });
+
         // day
         mDayPicker = (NumberPicker) findViewById(R.id.day);
         mDayPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
@@ -121,24 +129,11 @@
         mYearPicker.setOnLongPressUpdateInterval(100);
         mYearPicker.setOnChangeListener(onChangeListener);
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker);
-        int mStartYear = a.getInt(R.styleable.DatePicker_startYear, DEFAULT_START_YEAR);
-        int mEndYear = a.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
-        mYearPicker.setRange(mStartYear, mEndYear);
+        int startYear = a.getInt(R.styleable.DatePicker_startYear, DEFAULT_START_YEAR);
+        int endYear = a.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
+        setRange(startYear, endYear);
         a.recycle();
 
-        // mini-month day-picker
-        mMiniMonthDayPicker = (DayPicker) findViewById(R.id.mini_month_day_picker);
-        mTempCalendar.clear();
-        mTempCalendar.set(mStartYear, 0, 1);
-        Calendar endRangeDate = (Calendar) mTempCalendar.clone();
-        endRangeDate.set(mEndYear, 11, 31);
-        mMiniMonthDayPicker.setRange(mTempCalendar, endRangeDate);
-        mMiniMonthDayPicker.setOnDateChangeListener(new DayPicker.OnSelectedDayChangeListener() {
-            public void onSelectedDayChange(DayPicker view, int year, int month, int monthDay) {
-                updateDateUnchecked(year, month, monthDay);
-            }
-        });
-        
         // initialize to current date
         mTempCalendar.setTimeInMillis(System.currentTimeMillis());
         init(mTempCalendar.get(Calendar.YEAR), mTempCalendar.get(Calendar.MONTH),
@@ -148,6 +143,40 @@
         reorderPickers();
     }
 
+    /**
+     * Sets the range of years in which dates can be selected.
+     * <p>
+     * Note: If the range is set to a value that does not include the currently
+     * selected date the value of this picker will be updated to the closest
+     * date in the range.
+     * </p>
+     *
+     * @param startYear The start year of the range.
+     * @param endYear The end year of the range.
+     */
+    public void setRange(int startYear, int endYear) {
+        // set ranges of the widgets
+        mYearPicker.setRange(startYear, endYear);
+        mTempCalendar.clear();
+        Calendar startRangeDate = (Calendar) mTempCalendar.clone();
+        startRangeDate.set(startYear, 0, 1);
+        Calendar endRangeDate = (Calendar) mTempCalendar.clone();
+        endRangeDate.set(endYear, 11, 31);
+        mMiniMonthDayPicker.setRange(startRangeDate, endRangeDate);
+
+        // update state if current date is outside of the range
+        mTempCalendar.set(Calendar.YEAR, getYear());
+        mTempCalendar.set(Calendar.MONTH, getMonth());
+        mTempCalendar.set(Calendar.DAY_OF_MONTH, getDayOfMonth());
+        if (mTempCalendar.before(startRangeDate)) {
+            updateDate(startRangeDate.get(Calendar.YEAR), startRangeDate.get(Calendar.MONTH),
+                    startRangeDate.get(Calendar.DAY_OF_MONTH));
+        } else if (mTempCalendar.after(endRangeDate)) {
+            updateDate(endRangeDate.get(Calendar.YEAR), endRangeDate.get(Calendar.MONTH),
+                    endRangeDate.get(Calendar.DAY_OF_MONTH));
+        }
+    }
+
     @Override
     public void setEnabled(boolean enabled) {
         super.setEnabled(enabled);
diff --git a/core/java/android/widget/DayPicker.java b/core/java/android/widget/DayPicker.java
index cdf51f7..02805be 100644
--- a/core/java/android/widget/DayPicker.java
+++ b/core/java/android/widget/DayPicker.java
@@ -366,22 +366,38 @@
      * @param endRangeDate The end date.
      */
     public void setRange(Calendar startRangeDate, Calendar endRangeDate) {
-        boolean doSetupAdapter = false;
+        boolean rangeChanged = false;
         if (mRangeStartDate.get(Calendar.DAY_OF_YEAR) != startRangeDate.get(Calendar.DAY_OF_YEAR)
                 || mRangeStartDate.get(Calendar.YEAR) != startRangeDate.get(Calendar.YEAR)) {
             mRangeStartDate.setTimeInMillis(startRangeDate.getTimeInMillis());
             mRangeStartDate.setTimeZone(startRangeDate.getTimeZone());
-            doSetupAdapter = true;
+            rangeChanged = true;
         }
         if (mRangeEndDate.get(Calendar.DAY_OF_YEAR) != endRangeDate.get(Calendar.DAY_OF_YEAR)
                 || mRangeEndDate.get(Calendar.YEAR) != endRangeDate.get(Calendar.YEAR)) {
             mRangeEndDate.setTimeInMillis(endRangeDate.getTimeInMillis());
             mRangeEndDate.setTimeZone(endRangeDate.getTimeZone());
-            doSetupAdapter = true;
-            
+            rangeChanged = true;
         }
-        if (doSetupAdapter) {
-            setUpAdapter();
+
+        if (!rangeChanged) {
+            return;
+        }
+
+        // now recreate the adapter since we have a new range to handle
+        mAdapter = null;
+        setUpAdapter();
+
+        // set the current date to today if in the range
+        // otherwise to the closest end of the range
+        mTempCalendar.clear();
+        mTempCalendar.setTimeInMillis(System.currentTimeMillis());
+        if (mTempCalendar.before(mRangeStartDate)) {
+            goTo(mRangeStartDate, false, true, true);
+        } else if (mTempCalendar.after(mRangeEndDate)) {
+            goTo(mRangeEndDate, false, true, true);
+        } else {
+            goTo(mTempCalendar, false, true, true);
         }
     }
 
@@ -629,7 +645,6 @@
         }
 
         // Figure out where we are
-        int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
         long currScroll = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
 
         // If we have moved since our last call update the direction
@@ -645,6 +660,7 @@
         // causes the month to transition when two full weeks of a month are
         // visible when scrolling up, and when the first day in a month reaches
         // the top of the screen when scrolling down.
+        int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
         if (mIsScrollingUp) {
             child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
         } else if (offset != 0) {
@@ -712,8 +728,9 @@
             throw new IllegalArgumentException("fromDate: " + mRangeStartDate.getTime()
                     + " does not precede toDate: " + toDate.getTime());
         }
+
         int fromDateDayOfWeek = mRangeStartDate.get(Calendar.DAY_OF_WEEK);
-        long diff = (fromDateDayOfWeek - toDate.getFirstDayOfWeek()) * MILLIS_IN_DAY;
+        long diff = (fromDateDayOfWeek - mFirstDayOfWeek) * MILLIS_IN_DAY;
         if (diff < 0) {
             diff = diff + MILLIS_IN_WEEK;
         }
@@ -874,7 +891,16 @@
         protected void init() {
             mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener());
             mSelectedWeek = getWeeksDelta(mSelectedDay);
-            mTotalWeekCount = getWeeksDelta(mRangeEndDate);
+
+            // make adjustment to fit the range last week with needed overflow
+            mTempCalendar.setTimeInMillis(mRangeEndDate.getTimeInMillis());
+            mTempCalendar.setTimeZone(mRangeEndDate.getTimeZone());
+            int diff = mFirstDayOfWeek - mRangeEndDate.get(Calendar.DAY_OF_WEEK);
+            if (diff < 0) {
+                diff += DAYS_PER_WEEK;
+            }
+            mTempCalendar.add(Calendar.DAY_OF_WEEK, diff);
+            mTotalWeekCount = getWeeksDelta(mTempCalendar);
         }
 
         /**
@@ -892,7 +918,6 @@
             mSelectedWeek = getWeeksDelta(mSelectedDay);
             mFocusMonth = mSelectedDay.get(Calendar.MONTH);
             notifyDataSetChanged();
-            invalidate();  // Test
         }
 
         /**
@@ -1004,9 +1029,12 @@
             if (mGestureDetector.onTouchEvent(event)) {
                 WeekView weekView = (WeekView) v;
                 weekView.getDayFromLocation(event.getX(), mTempCalendar);
-                if (mTempCalendar.get(Calendar.YEAR) != 0) {
-                    onDayTapped(mTempCalendar);
+                // it is possible that the touched day is outside the valid range
+                // we draw whole weeks but range end can fall not on the week end
+                if (mTempCalendar.before(mRangeStartDate) || mTempCalendar.after(mRangeEndDate)) {
+                    return true;
                 }
+                onDayTapped(mTempCalendar);
                 return true;
             }
             return false;
@@ -1019,6 +1047,7 @@
          */
         protected void onDayTapped(Calendar day) {
             setSelectedDay(day);
+            setMonthDisplayed(day);
         }
 
         /**
@@ -1244,8 +1273,8 @@
                 mNumCells = mShowWeekNumber ? mWeekDayCount + 1 : mWeekDayCount;
             }
             mWeek = ((int[]) params.get(VIEW_PARAMS_WEEK))[0];
-            mTempCalendar.clear();
-            mTempCalendar.set(1900, 0, 1);
+            mTempCalendar.setTimeInMillis(mRangeStartDate.getTimeInMillis());
+            mTempCalendar.setTimeZone(mRangeStartDate.getTimeZone());
             mTempCalendar.add(Calendar.WEEK_OF_YEAR, mWeek);
             if (params.containsKey(VIEW_PARAMS_WEEK_START)) {
                 mTempCalendar.setFirstDayOfWeek(((int[]) params.get(VIEW_PARAMS_WEEK_START))[0]);
@@ -1277,7 +1306,12 @@
 
             for (; i < mNumCells; i++) {
                 mFocusDay[i] = (mTempCalendar.get(Calendar.MONTH) == focusMonth);
-                mDayNumbers[i] = Integer.toString(mTempCalendar.get(Calendar.DAY_OF_MONTH));
+                // do not draw dates outside the valid range to avoid user confusion
+                if (mTempCalendar.before(mRangeStartDate) || mTempCalendar.after(mRangeEndDate)) {
+                    mDayNumbers[i] = "";
+                } else {
+                    mDayNumbers[i] = Integer.toString(mTempCalendar.get(Calendar.DAY_OF_MONTH));
+                }
                 mTempCalendar.add(Calendar.DAY_OF_MONTH, 1);
             }
             // We do one extra add at the end of the loop, if that pushed us to
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 8c22f97..a236d27 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1189,9 +1189,9 @@
      */
     private int getWrappedSelectorIndex(int selectorIndex) {
         if (selectorIndex > mEnd) {
-            return mStart + (selectorIndex - mEnd) % (mEnd - mStart);
+            return mStart + (selectorIndex - mEnd) % (mEnd - mStart) - 1;
         } else if (selectorIndex < mStart) {
-            return mEnd - (mStart - selectorIndex) % (mEnd - mStart);
+            return mEnd - (mStart - selectorIndex) % (mEnd - mStart) + 1;
         }
         return selectorIndex;
     }
diff --git a/core/res/assets/webkit/youtube.html b/core/res/assets/webkit/youtube.html
index 289f8cf..d808bcf 100644
--- a/core/res/assets/webkit/youtube.html
+++ b/core/res/assets/webkit/youtube.html
@@ -38,19 +38,29 @@
                 // All images are loaded, so display them.
                 // (Note that the images are loaded from javascript, so might load
                 // after document.onload fires)
-                ctx.drawImage(background, 0, 0, width, height);
+
                 playWidth = play.width;
                 playHeight = play.height;
                 logoWidth = logo.width;
                 logoHeight = logo.height;
                 var ratio = 1;
                 // If the page is smaller than it 'should' be in either dimension
-                // we scale the play button and logo according to the dimension that
-                // has been shrunk the most.
+                // we scale the background, play button and logo according to the
+                // dimension that has been shrunk the most.
                 if (width / height > defWidth / defHeight && height < defHeight) {
                     ratio = height / defHeight;
+                    // Stretch the background in this dimension only.
+                    backgroundHeight = background.height / ratio;
+                    ctx.drawImage(background, 0, 0, background.width, background.height,
+                        0, (height - backgroundHeight) / 2, width, backgroundHeight);
                 } else if (width / height < defWidth / defHeight && width < defWidth) {
                     ratio = width / defWidth;
+                    backgroundWidth = background.width / ratio;
+                    ctx.drawImage(background, 0, 0, background.width, background.height,
+                        (width - backgroundWidth) / 2, 0, backgroundWidth, height);
+                } else {
+                    // In this case stretch the background in both dimensions to fill the space.
+                    ctx.drawImage(background, 0, 0, width, height);
                 }
                 playWidth *= ratio;
                 playHeight *= ratio;
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 9662817..4ccdd9a 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -22,6 +22,20 @@
 #include <utils/Log.h>
 #include <utils/threads.h>
 #include <core/SkBitmap.h>
+
+// Please do not enable "USE_PRIVATE_NATIVE_BITMAP_CONSTUCTOR"
+// This mode will be removed and it is kept here for
+// convenient comparsion just in case. Will be removed soon.
+// Tests show that this mode is also ~30ms slower,
+// when rotation is involved.
+#define USE_PRIVATE_NATIVE_BITMAP_CONSTUCTOR 0
+
+#if (!USE_PRIVATE_NATIVE_BITMAP_CONSTUCTOR)
+#include <core/SkCanvas.h>
+#include <core/SkDevice.h>
+#include <core/SkScalar.h>
+#endif
+
 #include <media/mediametadataretriever.h>
 #include <private/media/VideoFrame.h>
 
@@ -35,8 +49,15 @@
 struct fields_t {
     jfieldID context;
     jclass bitmapClazz;
+#if USE_PRIVATE_NATIVE_BITMAP_CONSTUCTOR
     jmethodID bitmapConstructor;
+    jmethodID createBitmapRotationMethod;
+#else
+    jfieldID nativeBitmap;
     jmethodID createBitmapMethod;
+    jclass configClazz;
+    jmethodID createConfigMethod;
+#endif
 };
 
 static fields_t fields;
@@ -162,6 +183,12 @@
         return NULL;
     }
 
+    LOGV("Dimension = %dx%d and bytes = %d",
+            videoFrame->mDisplayWidth,
+            videoFrame->mDisplayHeight,
+            videoFrame->mSize);
+
+#if USE_PRIVATE_NATIVE_BITMAP_CONSTUCTOR
     jobject matrix = NULL;
     if (videoFrame->mRotationAngle != 0) {
         LOGD("Create a rotation matrix: %d degrees", videoFrame->mRotationAngle);
@@ -217,9 +244,9 @@
     jobject jSrcBitmap = env->NewObject(fields.bitmapClazz,
             fields.bitmapConstructor, (int) bitmap, NULL, true, NULL, -1);
 
-    LOGV("Return a new bitmap constructed with the rotation matrix");
-    return env->CallStaticObjectMethod(
-                fields.bitmapClazz, fields.createBitmapMethod,
+    jobject jBitmap = env->CallStaticObjectMethod(
+                fields.bitmapClazz,
+                fields.createBitmapRotationMethod,
                 jSrcBitmap,                     // source Bitmap
                 0,                              // x
                 0,                              // y
@@ -227,6 +254,39 @@
                 videoFrame->mDisplayHeight,     // height
                 matrix,                         // transform matrix
                 false);                         // filter
+
+#else
+
+    jobject config = env->CallStaticObjectMethod(
+                        fields.configClazz,
+                        fields.createConfigMethod,
+                        SkBitmap::kRGB_565_Config);
+
+    jobject jBitmap = env->CallStaticObjectMethod(
+                            fields.bitmapClazz,
+                            fields.createBitmapMethod,
+                            videoFrame->mDisplayWidth,
+                            videoFrame->mDisplayHeight,
+                            config);
+    SkBitmap *bitmap =
+            (SkBitmap *) env->GetIntField(jBitmap, fields.nativeBitmap);
+
+    bitmap->lockPixels();
+
+    memcpy((uint8_t*)bitmap->getPixels(),
+            (uint8_t*)videoFrame + sizeof(VideoFrame), videoFrame->mSize);
+
+    bitmap->unlockPixels();
+
+    if (videoFrame->mRotationAngle != 0) {
+        SkDevice device(*bitmap);
+        SkCanvas canvas(&device);
+        canvas.rotate((SkScalar) (videoFrame->mRotationAngle * 1.0));
+        canvas.drawBitmap(*bitmap, 0, 0);
+    }
+#endif
+    LOGV("Return a new bitmap constructed with the rotation matrix");
+    return jBitmap;
 }
 
 static jbyteArray android_media_MediaMetadataRetriever_extractAlbumArt(JNIEnv *env, jobject thiz)
@@ -293,7 +353,6 @@
 static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jobject thiz)
 {
     LOGV("native_finalize");
-    
     // No lock is needed, since android_media_MediaMetadataRetriever_release() is protected
     android_media_MediaMetadataRetriever_release(env, thiz);
 }
@@ -320,21 +379,52 @@
         jniThrowException(env, "java/lang/RuntimeException", "Can't find android/graphics/Bitmap");
         return;
     }
-
+#if USE_PRIVATE_NATIVE_BITMAP_CONSTUCTOR
     fields.bitmapConstructor = env->GetMethodID(fields.bitmapClazz, "<init>", "(I[BZ[BI)V");
     if (fields.bitmapConstructor == NULL) {
         jniThrowException(env, "java/lang/RuntimeException", "Can't find Bitmap constructor");
         return;
     }
-    fields.createBitmapMethod =
+    fields.createBitmapRotationMethod =
             env->GetStaticMethodID(fields.bitmapClazz, "createBitmap",
                     "(Landroid/graphics/Bitmap;IIIILandroid/graphics/Matrix;Z)"
                     "Landroid/graphics/Bitmap;");
-    if (fields.createBitmapMethod == NULL) {
+    if (fields.createBitmapRotationMethod == NULL) {
         jniThrowException(env, "java/lang/RuntimeException",
                 "Can't find Bitmap.createBitmap method");
         return;
     }
+#else
+    fields.createBitmapMethod =
+            env->GetStaticMethodID(fields.bitmapClazz, "createBitmap",
+                    "(IILandroid/graphics/Bitmap$Config;)"
+                    "Landroid/graphics/Bitmap;");
+    if (fields.createBitmapMethod == NULL) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "Can't find Bitmap.createBitmap(int, int, Config)  method");
+        return;
+    }
+    fields.nativeBitmap = env->GetFieldID(fields.bitmapClazz, "mNativeBitmap", "I");
+    if (fields.nativeBitmap == NULL) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "Can't find Bitmap.mNativeBitmap field");
+    }
+
+    fields.configClazz = env->FindClass("android/graphics/Bitmap$Config");
+    if (fields.configClazz == NULL) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                               "Can't find Bitmap$Config class");
+        return;
+    }
+    fields.createConfigMethod =
+            env->GetStaticMethodID(fields.configClazz, "nativeToConfig",
+                    "(I)Landroid/graphics/Bitmap$Config;");
+    if (fields.createConfigMethod == NULL) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "Can't find Bitmap$Config.nativeToConfig(int)  method");
+        return;
+    }
+#endif
 }
 
 static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz)
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index d3dab13..967fa49 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -32,8 +32,6 @@
 #include <media/stagefright/MetaData.h>
 #include <surfaceflinger/Surface.h>
 
-#define SHUTDOWN_ON_DISCONTINUITY       0
-
 namespace android {
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -78,6 +76,26 @@
     (new AMessage(kWhatStart, id()))->post();
 }
 
+// static
+bool NuPlayer::IsFlushingState(FlushStatus state, bool *formatChange) {
+    switch (state) {
+        case FLUSHING_DECODER:
+            if (formatChange != NULL) {
+                *formatChange = false;
+            }
+            return true;
+
+        case FLUSHING_DECODER_FORMATCHANGE:
+            if (formatChange != NULL) {
+                *formatChange = true;
+            }
+            return true;
+
+        default:
+            return false;
+    }
+}
+
 void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
         case kWhatSetDataSource:
@@ -181,28 +199,30 @@
             } else if (what == ACodec::kWhatEOS) {
                 mRenderer->queueEOS(audio, ERROR_END_OF_STREAM);
             } else if (what == ACodec::kWhatFlushCompleted) {
+                bool formatChange;
+
                 if (audio) {
-                    CHECK_EQ((int)mFlushingAudio, (int)FLUSHING_DECODER);
+                    CHECK(IsFlushingState(mFlushingAudio, &formatChange));
                     mFlushingAudio = FLUSHED;
                 } else {
-                    CHECK_EQ((int)mFlushingVideo, (int)FLUSHING_DECODER);
+                    CHECK(IsFlushingState(mFlushingVideo, &formatChange));
                     mFlushingVideo = FLUSHED;
                 }
 
                 LOGI("decoder %s flush completed", audio ? "audio" : "video");
 
-#if SHUTDOWN_ON_DISCONTINUITY
-                LOGI("initiating %s decoder shutdown",
-                     audio ? "audio" : "video");
+                if (formatChange) {
+                    LOGI("initiating %s decoder shutdown",
+                         audio ? "audio" : "video");
 
-                (audio ? mAudioDecoder : mVideoDecoder)->initiateShutdown();
+                    (audio ? mAudioDecoder : mVideoDecoder)->initiateShutdown();
 
-                if (audio) {
-                    mFlushingAudio = SHUTTING_DOWN_DECODER;
-                } else {
-                    mFlushingVideo = SHUTTING_DOWN_DECODER;
+                    if (audio) {
+                        mFlushingAudio = SHUTTING_DOWN_DECODER;
+                    } else {
+                        mFlushingVideo = SHUTTING_DOWN_DECODER;
+                    }
                 }
-#endif
 
                 finishFlushIfPossible();
             } else if (what == ACodec::kWhatOutputFormatChanged) {
@@ -451,8 +471,8 @@
     sp<AMessage> reply;
     CHECK(msg->findMessage("reply", &reply));
 
-    if ((audio && mFlushingAudio == FLUSHING_DECODER)
-            || (!audio && mFlushingVideo == FLUSHING_DECODER)) {
+    if ((audio && IsFlushingState(mFlushingAudio))
+            || (!audio && IsFlushingState(mFlushingVideo))) {
         reply->setInt32("err", INFO_DISCONTINUITY);
         reply->post();
         return OK;
@@ -467,14 +487,25 @@
         return err;
     } else if (err != OK) {
         if (err == INFO_DISCONTINUITY) {
-            LOGI("%s discontinuity", audio ? "audio" : "video");
+            int32_t formatChange;
+            if (!accessUnit->meta()->findInt32(
+                        "format-change", &formatChange)) {
+                formatChange = 0;
+            }
+
+            LOGI("%s discontinuity (formatChange=%d)",
+                 audio ? "audio" : "video", formatChange);
+
             (audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
             mRenderer->flush(audio);
 
             if (audio) {
                 CHECK(mFlushingAudio == NONE
                         || mFlushingAudio == AWAITING_DISCONTINUITY);
-                mFlushingAudio = FLUSHING_DECODER;
+
+                mFlushingAudio = formatChange
+                    ? FLUSHING_DECODER_FORMATCHANGE : FLUSHING_DECODER;
+
                 if (mFlushingVideo == NONE) {
                     mFlushingVideo = (mVideoDecoder != NULL)
                         ? AWAITING_DISCONTINUITY
@@ -483,7 +514,10 @@
             } else {
                 CHECK(mFlushingVideo == NONE
                         || mFlushingVideo == AWAITING_DISCONTINUITY);
-                mFlushingVideo = FLUSHING_DECODER;
+
+                mFlushingVideo = formatChange
+                    ? FLUSHING_DECODER_FORMATCHANGE : FLUSHING_DECODER;
+
                 if (mFlushingAudio == NONE) {
                     mFlushingAudio = (mAudioDecoder != NULL)
                         ? AWAITING_DISCONTINUITY
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index fad1ce1..d4e7428 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -79,6 +79,7 @@
         NONE,
         AWAITING_DISCONTINUITY,
         FLUSHING_DECODER,
+        FLUSHING_DECODER_FORMATCHANGE,
         SHUTTING_DOWN_DECODER,
         FLUSHED,
         SHUT_DOWN,
@@ -104,6 +105,8 @@
 
     void finishFlushIfPossible();
 
+    static bool IsFlushingState(FlushStatus state, bool *formatChange = NULL);
+
     DISALLOW_EVIL_CONSTRUCTORS(NuPlayer);
 };
 
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index afacb2e..ee9b5739 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -334,7 +334,7 @@
 
             if (mStreamType == 0x1b && mSource != NULL) {
                 // Don't signal discontinuities on audio streams.
-                mSource->queueDiscontinuity();
+                mSource->queueDiscontinuity(true /* formatChange */);
             }
             break;
         }
@@ -348,7 +348,7 @@
 
             if (mSource != NULL) {
                 mSource->clear();
-                mSource->queueDiscontinuity();
+                mSource->queueDiscontinuity(!isASeek);
             }
             break;
         }
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 7a1d5b0..a8fe2c1 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -63,8 +63,6 @@
         int32_t discontinuity;
         if ((*buffer)->meta()->findInt32("discontinuity", &discontinuity)
                 && discontinuity) {
-            buffer->clear();
-
             return INFO_DISCONTINUITY;
         }
 
@@ -125,10 +123,14 @@
     mCondition.signal();
 }
 
-void AnotherPacketSource::queueDiscontinuity() {
+void AnotherPacketSource::queueDiscontinuity(bool formatChange) {
     sp<ABuffer> buffer = new ABuffer(0);
     buffer->meta()->setInt32("discontinuity", true);
 
+    if (formatChange) {
+        buffer->meta()->setInt32("format-change", true);
+    }
+
     Mutex::Autolock autoLock(mLock);
 
     mBuffers.push_back(buffer);
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 2bc7404..f25a067 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -42,7 +42,7 @@
     status_t nextBufferTime(int64_t *timeUs);
 
     void queueAccessUnit(const sp<ABuffer> &buffer);
-    void queueDiscontinuity();
+    void queueDiscontinuity(bool formatChange);
     void signalEOS(status_t result);
 
     void clear();
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index f82a243..3e2eac2 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1097,6 +1097,7 @@
                     // gotten a positive report we don't want to overwrite, but if not we need to
                     // clear this now to turn our cellular sig strength white
                     mDefaultInetConditionPublished = 0;
+                    intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
                 }
                 intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
             } else {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 748f2e9..94ed813 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -6814,6 +6814,11 @@
         // Input channel
         InputChannel mInputChannel;
         
+        // Used to improve performance of toString()
+        String mStringNameCache;
+        CharSequence mLastTitle;
+        boolean mWasPaused;
+
         WindowState(Session s, IWindow c, WindowToken token,
                WindowState attachedWindow, WindowManager.LayoutParams a,
                int viewVisibility) {
@@ -8131,9 +8136,14 @@
 
         @Override
         public String toString() {
-            return "Window{"
-                + Integer.toHexString(System.identityHashCode(this))
-                + " " + mAttrs.getTitle() + " paused=" + mToken.paused + "}";
+            if (mStringNameCache == null || mLastTitle != mAttrs.getTitle()
+                    || mWasPaused != mToken.paused) {
+                mLastTitle = mAttrs.getTitle();
+                mWasPaused = mToken.paused;
+                mStringNameCache = "Window{" + Integer.toHexString(System.identityHashCode(this))
+                        + " " + mLastTitle + " paused=" + mWasPaused + "}";
+            }
+            return mStringNameCache;
         }
     }
 
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index 51236fe9..ce18ec5 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -519,10 +519,15 @@
      * @param session the session that receives the incoming call
      * @param sessionDescription the session description of the incoming call
      * @throws SipException if the SIP service fails to attach this object to
-     *        the session
+     *        the session or VOIP API is not supported by the device
+     * @see SipManager#isVoipSupported
      */
     public void attachCall(SipSession session, String sessionDescription)
             throws SipException {
+        if (!SipManager.isVoipSupported(mContext)) {
+            throw new SipException("VOIP API is not supported");
+        }
+
         synchronized (this) {
             mSipSession = session;
             mPeerSd = sessionDescription;
@@ -548,10 +553,15 @@
      *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener#onError
      * @throws SipException if the SIP service fails to create a session for the
-     *        call
+     *        call or VOIP API is not supported by the device
+     * @see SipManager#isVoipSupported
      */
     public void makeCall(SipProfile peerProfile, SipSession sipSession,
             int timeout) throws SipException {
+        if (!SipManager.isVoipSupported(mContext)) {
+            throw new SipException("VOIP API is not supported");
+        }
+
         synchronized (this) {
             mSipSession = sipSession;
             try {
@@ -595,6 +605,9 @@
     public void holdCall(int timeout) throws SipException {
         synchronized (this) {
             if (mHold) return;
+            if (mSipSession == null) {
+                throw new SipException("Not in a call to hold call");
+            }
             mSipSession.changeCall(createHoldOffer().encode(), timeout);
             mHold = true;
             setAudioGroupMode();
@@ -614,6 +627,9 @@
      */
     public void answerCall(int timeout) throws SipException {
         synchronized (this) {
+            if (mSipSession == null) {
+                throw new SipException("No call to answer");
+            }
             try {
                 mAudioStream = new AudioStream(InetAddress.getByName(
                         getLocalIp()));
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 2e386620..dce46fe 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -133,7 +133,7 @@
     }
 
     /**
-     * Returns true if the system supports SIP-based VoIP.
+     * Returns true if the system supports SIP-based VOIP API.
      */
     public static boolean isVoipSupported(Context context) {
         return context.getPackageManager().hasSystemFeature(
@@ -305,12 +305,17 @@
      * @param timeout the timeout value in seconds. Default value (defined by
      *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @return a {@link SipAudioCall} object
-     * @throws SipException if calling the SIP service results in an error
+     * @throws SipException if calling the SIP service results in an error or
+     *      VOIP API is not supported by the device
      * @see SipAudioCall.Listener#onError
+     * @see #isVoipSupported
      */
     public SipAudioCall makeAudioCall(SipProfile localProfile,
             SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
             throws SipException {
+        if (!isVoipSupported(mContext)) {
+            throw new SipException("VOIP API is not supported");
+        }
         SipAudioCall call = new SipAudioCall(mContext, localProfile);
         call.setListener(listener);
         SipSession s = createSipSession(localProfile, null);
@@ -332,12 +337,17 @@
      * @param timeout the timeout value in seconds. Default value (defined by
      *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @return a {@link SipAudioCall} object
-     * @throws SipException if calling the SIP service results in an error
+     * @throws SipException if calling the SIP service results in an error or
+     *      VOIP API is not supported by the device
      * @see SipAudioCall.Listener#onError
+     * @see #isVoipSupported
      */
     public SipAudioCall makeAudioCall(String localProfileUri,
             String peerProfileUri, SipAudioCall.Listener listener, int timeout)
             throws SipException {
+        if (!isVoipSupported(mContext)) {
+            throw new SipException("VOIP API is not supported");
+        }
         try {
             return makeAudioCall(
                     new SipProfile.Builder(localProfileUri).build(),