Merge "update javadoc for Settings.Secure.ANDROID_ID"
diff --git a/api/current.xml b/api/current.xml
index cd200c1..e8a8481 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -3184,7 +3184,7 @@
  value="16843395"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -3261,7 +3261,7 @@
  value="16843362"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -5131,7 +5131,7 @@
  value="16842997"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -5252,7 +5252,7 @@
  value="16842996"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index a8e217e..81d60dc 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -1191,14 +1191,6 @@
 
     CameraParameters p(params);
 
-    // The orientation parameter is actually for CameraService, not for the camera driver.
-    if (p.getOrientation() == CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
-        LOGV("portrait mode");
-        mOrientation = ISurface::BufferHeap::ROT_90;
-    } else {
-        mOrientation = 0;
-    }
-
     return mHardware->setParameters(p);
 }
 
@@ -1224,6 +1216,30 @@
     status_t result = checkPid();
     if (result != NO_ERROR) return result;
 
+    if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) {
+        // The orientation cannot be set during preview.
+        if (mHardware->previewEnabled()) {
+            return INVALID_OPERATION;
+        }
+        switch (arg1) {
+            case 0:
+                mOrientation = ISurface::BufferHeap::ROT_0;
+                break;
+            case 90:
+                mOrientation = ISurface::BufferHeap::ROT_90;
+                break;
+            case 180:
+                mOrientation = ISurface::BufferHeap::ROT_180;
+                break;
+            case 270:
+                mOrientation = ISurface::BufferHeap::ROT_270;
+                break;
+            default:
+                return BAD_VALUE;
+        }
+        return OK;
+    }
+
     if (mHardware == 0) {
         LOGE("mHardware is NULL, returning.");
         return INVALID_OPERATION;
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 9300915..78f90a1 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -129,7 +129,7 @@
     dump_file("LAST PANIC CONSOLE", "/data/dontpanic/apanic_console");
     dump_file("LAST PANIC THREADS", "/data/dontpanic/apanic_threads");
 
-    printf("----- BACKLIGHTS -----\n");
+    printf("------ BACKLIGHTS ------\n");
     printf("LCD brightness=");
     dump_file(NULL, "/sys/class/leds/lcd-backlight/brightness");
     printf("Button brightness=");
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
index fda618c..39e14e4 100644
--- a/cmds/dumpstate/utils.c
+++ b/cmds/dumpstate/utils.c
@@ -43,13 +43,13 @@
     int fd = open(path, O_RDONLY);
     if (fd < 0) {
         int err = errno;
-        if (title) printf("----- %s (%s) -----\n", title, path);
+        if (title) printf("------ %s (%s) ------\n", title, path);
         printf("*** %s: %s\n", path, strerror(err));
         if (title) printf("\n");
         return -1;
     }
 
-    if (title) printf("----- %s (%s", title, path);
+    if (title) printf("------ %s (%s", title, path);
 
     if (title) {
         struct stat st;
@@ -59,7 +59,7 @@
             strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
             printf(": %s", stamp);
         }
-        printf(") -----\n");
+        printf(") ------\n");
     }
 
     int newline = 0;
@@ -97,13 +97,13 @@
 
         va_list ap;
         va_start(ap, command);
-        if (title) printf("----- %s (%s", title, command);
+        if (title) printf("------ %s (%s", title, command);
         for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
             args[arg] = va_arg(ap, const char *);
             if (args[arg] == NULL) break;
             if (title) printf(" %s", args[arg]);
         }
-        if (title) printf(") -----\n");
+        if (title) printf(") ------\n");
         fflush(stdout);
 
         execvp(command, (char**) args);
@@ -159,7 +159,7 @@
     property_list(print_prop, NULL);
     qsort(&props, num_props, sizeof(props[0]), compare_prop);
 
-    printf("----- SYSTEM PROPERTIES -----\n");
+    printf("------ SYSTEM PROPERTIES ------\n");
     for (i = 0; i < num_props; ++i) {
         fputs(props[i], stdout);
         free(props[i]);
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index d90536c..4c4455a 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -543,6 +543,21 @@
     public native final void stopSmoothZoom();
 
     /**
+     * Set the display orientation. This affects the preview frames and the
+     * picture displayed after snapshot. This method is useful for portrait
+     * mode applications.
+     *
+     * This does not affect the order of byte array passed in
+     * {@link PreviewCallback#onPreviewFrame}. This method is not allowed to
+     * be called during preview.
+     *
+     * @param degrees the angle that the picture will be rotated clockwise.
+     *                Valid values are 0, 90, 180, and 270.
+     * @hide
+     */
+    public native final void setDisplayOrientation(int degrees);
+
+    /**
      * Handles the zoom callback.
      *
      * @hide
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 6bf09b5..08ab166 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -517,6 +517,8 @@
     /**
      * This download couldn't be completed because of a storage issue.
      * Typically, that's because the filesystem is missing or full.
+     * Use the more specific {@link #STATUS_INSUFFICIENT_SPACE_ERROR}
+     * and {@link #STATUS_DEVICE_NOT_FOUND_ERROR} when appropriate.
      * @hide
      */
     public static final int STATUS_FILE_ERROR = 492;
@@ -558,6 +560,21 @@
     public static final int STATUS_TOO_MANY_REDIRECTS = 497;
 
     /**
+     * This download couldn't be completed due to insufficient storage
+     * space.  Typically, this is because the SD card is full.
+     * @hide
+     */
+    public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498;
+
+    /**
+     * This download couldn't be completed because no external storage
+     * device was found.  Typically, this is because the SD card is not
+     * mounted.
+     * @hide
+     */
+    public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499;
+
+    /**
      * This download is visible but only shows in the notifications
      * while it's in progress.
      * @hide
@@ -1019,6 +1036,8 @@
         /**
          * This download couldn't be completed because of a storage issue.
          * Typically, that's because the filesystem is missing or full.
+         * Use the more specific {@link #STATUS_INSUFFICIENT_SPACE_ERROR}
+         * and {@link #STATUS_DEVICE_NOT_FOUND_ERROR} when appropriate.
          */
         public static final int STATUS_FILE_ERROR = 492;
 
@@ -1054,6 +1073,19 @@
         public static final int STATUS_TOO_MANY_REDIRECTS = 497;
 
         /**
+         * This download couldn't be completed due to insufficient storage
+         * space.  Typically, this is because the SD card is full.
+         */
+        public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498;
+
+        /**
+         * This download couldn't be completed because no external storage
+         * device was found.  Typically, this is because the SD card is not
+         * mounted.
+         */
+        public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499;
+
+        /**
          * This download is visible but only shows in the notifications
          * while it's in progress.
          */
diff --git a/core/java/com/android/internal/util/HanziToPinyin.java b/core/java/com/android/internal/util/HanziToPinyin.java
index 4368e98..6a4adaa 100644
--- a/core/java/com/android/internal/util/HanziToPinyin.java
+++ b/core/java/com/android/internal/util/HanziToPinyin.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.util;
 
-import com.google.android.util.AbstractMessageParser.Token;
-
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -298,8 +296,10 @@
         };
 
     /** First and last Chinese character with known Pinyin according to zh collation */
-    private static final String FIRST_UNIHAN =  "\u5416";
-    private static final String LAST_UNIHAN =  "\u5497";
+    private static final String FIRST_PINYIN_UNIHAN =  "\u5416";
+    private static final String LAST_PINYIN_UNIHAN =  "\u5497";
+    /** The first Chinese character in Unicode block */
+    private static final char FIRST_UNIHAN = '\u3400';
     private static final Collator COLLATOR = Collator.getInstance(Locale.CHINA);
 
     private static HanziToPinyin sInstance;
@@ -311,10 +311,18 @@
          */
         public static final String SEPARATOR = " ";
 
-        public static final int ASCII = 1;
+        public static final int LATIN = 1;
         public static final int PINYIN = 2;
         public static final int UNKNOWN = 3;
 
+        public Token() {
+        }
+
+        public Token(int type, String source, String target) {
+            this.type = type;
+            this.source = source;
+            this.target = target;
+        }
         /**
          * Type of this token, ASCII, PINYIN or UNKNOWN.
          */
@@ -347,6 +355,7 @@
                     return sInstance;
                 }
             }
+            Log.w(TAG, "There is no Chinese collator, HanziToPinyin is disabled");
             sInstance = new HanziToPinyin(false);
             return sInstance;
         }
@@ -359,11 +368,15 @@
         int offset = -1;
         int cmp;
         if (character < 256) {
-            token.type = Token.ASCII;
+            token.type = Token.LATIN;
+            token.target = letter;
+            return token;
+        } else if (character < FIRST_UNIHAN) {
+            token.type = Token.UNKNOWN;
             token.target = letter;
             return token;
         } else {
-            cmp = COLLATOR.compare(letter, FIRST_UNIHAN);
+            cmp = COLLATOR.compare(letter, FIRST_PINYIN_UNIHAN);
             if (cmp < 0) {
                 token.type = Token.UNKNOWN;
                 token.target = letter;
@@ -372,7 +385,7 @@
                 token.type = Token.PINYIN;
                 offset = 0;
             } else {
-                cmp = COLLATOR.compare(letter, LAST_UNIHAN);
+                cmp = COLLATOR.compare(letter, LAST_PINYIN_UNIHAN);
                 if (cmp > 0) {
                     token.type = Token.UNKNOWN;
                     token.target = letter;
@@ -412,44 +425,71 @@
         return token;
     }
 
+    /**
+     * Convert the input to a array of tokens. The sequence of ASCII or Unknown
+     * characters without space will be put into a Token, One Hanzi character 
+     * which has pinyin will be treated as a Token.
+     * If these is no China collator, the empty token array is returned.
+     */
     public ArrayList<Token> get(final String input) {
-        if (!mHasChinaCollator || TextUtils.isEmpty(input)) {
-            return null;
-        }
-
         ArrayList<Token> tokens = new ArrayList<Token>();
-        Token currentToken;
-
+        if (!mHasChinaCollator || TextUtils.isEmpty(input)) {
+            // return empty tokens.
+            return tokens;
+        }
         final int inputLength = input.length();
-
-        currentToken = getToken(input.charAt(0));
-
-        for (int i = 1; i < inputLength; i++) {
+        final StringBuilder sb = new StringBuilder();
+        int tokenType = Token.LATIN;
+        // Go through the input, create a new token when
+        // a. Token type changed
+        // b. Get the Pinyin of current charater.
+        // c. current character is space.
+        for (int i = 0; i < inputLength; i++) {
             final char character = input.charAt(i);
-            Token token = getToken(character);
-
-            if (token.type != currentToken.type) {
-                currentToken.target = currentToken.target.trim();
-                tokens.add(currentToken);
-                currentToken = token;
+            if (character == ' ') {
+                if (sb.length() > 0) {
+                    addToken(sb, tokens, tokenType);
+                }
+            } else if (character < 256) {
+                if (tokenType != Token.LATIN && sb.length() > 0) {
+                    addToken(sb, tokens, tokenType);
+                }
+                tokenType = Token.LATIN;
+                sb.append(character);
+            } else if (character < FIRST_UNIHAN) {
+                if (tokenType != Token.UNKNOWN && sb.length() > 0) {
+                    addToken(sb, tokens, tokenType);
+                }
+                tokenType = Token.UNKNOWN;
+                sb.append(character);
             } else {
-                switch (token.type) {
-                    case Token.ASCII:
-                    case Token.UNKNOWN:
-                        currentToken.source += token.source;
-                        currentToken.target += token.target;
-                        break;
-                    case Token.PINYIN:
-                        currentToken.source += token.source;
-                        currentToken.target += " " + token.target;
-                        break;
+                Token t = getToken(character);
+                if (t.type == Token.PINYIN) {
+                    if (sb.length() > 0) {
+                        addToken(sb, tokens, tokenType);
+                    }
+                    tokens.add(t);
+                    tokenType = Token.PINYIN;
+                } else {
+                    if (tokenType != t.type && sb.length() > 0) {
+                        addToken(sb, tokens, tokenType);
+                    }
+                    tokenType = t.type;
+                    sb.append(character);
                 }
             }
         }
-
-        currentToken.target = currentToken.target.trim();
-        tokens.add(currentToken);
-
+        if (sb.length() > 0) {
+            addToken(sb, tokens, tokenType);
+        }
         return tokens;
     }
+
+    private void addToken(final StringBuilder sb, final ArrayList<Token> tokens,
+            final int tokenType) {
+        String str = sb.toString();
+        tokens.add(new Token(tokenType, str, str));
+        sb.setLength(0);
+    }
+
 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 67a0bda..7fd58e8 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -99,6 +99,7 @@
 	android/graphics/Shader.cpp \
 	android/graphics/Typeface.cpp \
 	android/graphics/Xfermode.cpp \
+	android/graphics/YuvToJpegEncoder.cpp \
 	android_media_AudioRecord.cpp \
 	android_media_AudioSystem.cpp \
 	android_media_AudioTrack.cpp \
@@ -148,6 +149,7 @@
 	external/tremor/Tremor \
 	external/icu4c/i18n \
 	external/icu4c/common \
+	external/jpeg \
 	frameworks/opt/emoji
 
 LOCAL_SHARED_LIBRARIES := \
@@ -175,7 +177,8 @@
 	libicui18n \
 	libicudata \
 	libmedia \
-	libwpa_client
+	libwpa_client \
+	libjpeg
 
 ifeq ($(BOARD_HAVE_BLUETOOTH),true)
 LOCAL_C_INCLUDES += \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 8364838..fa1ee0d 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -64,6 +64,7 @@
 extern int register_android_graphics_Region(JNIEnv* env);
 extern int register_android_graphics_Shader(JNIEnv* env);
 extern int register_android_graphics_Typeface(JNIEnv* env);
+extern int register_android_graphics_YuvImage(JNIEnv* env);
 
 extern int register_com_google_android_gles_jni_EGLImpl(JNIEnv* env);
 extern int register_com_google_android_gles_jni_GLImpl(JNIEnv* env);
@@ -1215,6 +1216,7 @@
     REG_JNI(register_android_graphics_Shader),
     REG_JNI(register_android_graphics_Typeface),
     REG_JNI(register_android_graphics_Xfermode),
+    REG_JNI(register_android_graphics_YuvImage),
     REG_JNI(register_com_android_internal_graphics_NativeUtils),
 
     REG_JNI(register_android_database_CursorWindow),
diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp
new file mode 100644
index 0000000..ef5c9ae
--- /dev/null
+++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp
@@ -0,0 +1,252 @@
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "SkJpegUtility.h"
+#include "YuvToJpegEncoder.h"
+#include "ui/PixelFormat.h"
+
+#include <jni.h>
+
+YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) {
+    // Only PIXEL_FORMAT_YCbCr_420_SP and PIXEl_FOMAT_YCbCr_422_I are supported
+    // for now.
+    if (format == android::PIXEL_FORMAT_YCbCr_420_SP) {
+        return new Yuv420SpToJpegEncoder(strides);
+    } else if (format == android::PIXEL_FORMAT_YCbCr_422_I) {
+        return new Yuv422IToJpegEncoder(strides);
+    } else {
+      return NULL;
+    }
+}
+
+YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) {
+}
+
+bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width,
+        int height, int* offsets, int jpegQuality) {
+    jpeg_compress_struct    cinfo;
+    skjpeg_error_mgr        sk_err;
+    skjpeg_destination_mgr  sk_wstream(stream);
+
+    cinfo.err = jpeg_std_error(&sk_err);
+    sk_err.error_exit = skjpeg_error_exit;
+    if (setjmp(sk_err.fJmpBuf)) {
+        return false;
+    }
+    jpeg_create_compress(&cinfo);
+
+    cinfo.dest = &sk_wstream;
+
+    setJpegCompressStruct(&cinfo, width, height, jpegQuality);
+
+    jpeg_start_compress(&cinfo, TRUE);
+
+    compress(&cinfo, (uint8_t*) inYuv, offsets);
+
+    jpeg_finish_compress(&cinfo);
+
+    return true;
+}
+
+void YuvToJpegEncoder::setJpegCompressStruct(jpeg_compress_struct* cinfo,
+        int width, int height, int quality) {
+    jpeg_set_quality(cinfo, quality, TRUE);
+
+    cinfo->image_width = width;
+    cinfo->image_height = height;
+
+    cinfo->input_components = 3;
+    cinfo->in_color_space = JCS_YCbCr;
+    jpeg_set_defaults(cinfo);
+    jpeg_set_colorspace(cinfo, JCS_YCbCr);
+    cinfo->raw_data_in = TRUE;
+
+    cinfo->dct_method = JDCT_IFAST;
+
+    configSamplingFactors(cinfo);
+}
+
+///////////////////////////////////////////////////////////////////
+Yuv420SpToJpegEncoder::Yuv420SpToJpegEncoder(int* strides) :
+        YuvToJpegEncoder(strides) {
+    fNumPlanes = 2;
+}
+
+void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo,
+        uint8_t* yuv, int* offsets) {
+    SkDebugf("onFlyCompress");
+    JSAMPROW y[16];
+    JSAMPROW cb[8];
+    JSAMPROW cr[8];
+    JSAMPARRAY planes[3];
+    planes[0] = y;
+    planes[1] = cb;
+    planes[2] = cr;
+
+    int width = cinfo->image_width;
+    int height = cinfo->image_height;
+    uint8_t* yPlanar = yuv + offsets[0];
+    uint8_t* vuPlanar = yuv + offsets[1]; //width * height;
+    uint8_t* uRows = new uint8_t [8 * (width >> 1)];
+    uint8_t* vRows = new uint8_t [8 * (width >> 1)];
+
+
+    // process 16 lines of Y and 8 lines of U/V each time.
+    while (cinfo->next_scanline < cinfo->image_height) {
+        //deitnerleave u and v
+        deinterleave(vuPlanar, uRows, vRows, cinfo->next_scanline, width);
+
+        for (int i = 0; i < 16; i++) {
+            // y row
+            y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0];
+
+            // construct u row and v row
+            if ((i & 1) == 0) {
+                // height and width are both halved because of downsampling
+                int offset = (i >> 1) * (width >> 1);
+                cb[i/2] = uRows + offset;
+                cr[i/2] = vRows + offset;
+            }
+          }
+        jpeg_write_raw_data(cinfo, planes, 16);
+    }
+    delete [] uRows;
+    delete [] vRows;
+
+}
+
+void Yuv420SpToJpegEncoder::deinterleave(uint8_t* vuPlanar, uint8_t* uRows,
+        uint8_t* vRows, int rowIndex, int width) {
+    for (int row = 0; row < 8; ++row) {
+        int offset = ((rowIndex >> 1) + row) * fStrides[1];
+        uint8_t* vu = vuPlanar + offset;
+        for (int i = 0; i < (width >> 1); ++i) {
+            int index = row * (width >> 1) + i;
+            uRows[index] = vu[1];
+            vRows[index] = vu[0];
+            vu += 2;
+        }
+    }
+}
+
+void Yuv420SpToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) {
+    // cb and cr are horizontally downsampled and vertically downsampled as well.
+    cinfo->comp_info[0].h_samp_factor = 2;
+    cinfo->comp_info[0].v_samp_factor = 2;
+    cinfo->comp_info[1].h_samp_factor = 1;
+    cinfo->comp_info[1].v_samp_factor = 1;
+    cinfo->comp_info[2].h_samp_factor = 1;
+    cinfo->comp_info[2].v_samp_factor = 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+Yuv422IToJpegEncoder::Yuv422IToJpegEncoder(int* strides) :
+        YuvToJpegEncoder(strides) {
+    fNumPlanes = 1;
+}
+
+void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo,
+        uint8_t* yuv, int* offsets) {
+    SkDebugf("onFlyCompress_422");
+    JSAMPROW y[16];
+    JSAMPROW cb[16];
+    JSAMPROW cr[16];
+    JSAMPARRAY planes[3];
+    planes[0] = y;
+    planes[1] = cb;
+    planes[2] = cr;
+
+    int width = cinfo->image_width;
+    int height = cinfo->image_height;
+    uint8_t* yRows = new uint8_t [16 * width];
+    uint8_t* uRows = new uint8_t [16 * (width >> 1)];
+    uint8_t* vRows = new uint8_t [16 * (width >> 1)];
+
+    uint8_t* yuvOffset = yuv + offsets[0];
+
+    // process 16 lines of Y and 16 lines of U/V each time.
+    while (cinfo->next_scanline < cinfo->image_height) {
+        deinterleave(yuvOffset, yRows, uRows, vRows, cinfo->next_scanline, width, height);
+
+        for (int i = 0; i < 16; i++) {
+            // y row
+            y[i] = yRows + i * width;
+
+            // construct u row and v row
+            // width is halved because of downsampling
+            int offset = i * (width >> 1);
+            cb[i] = uRows + offset;
+            cr[i] = vRows + offset;
+        }
+
+        jpeg_write_raw_data(cinfo, planes, 16);
+    }
+    delete [] yRows;
+    delete [] uRows;
+    delete [] vRows;
+}
+
+
+void Yuv422IToJpegEncoder::deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows,
+        uint8_t* vRows, int rowIndex, int width, int height) {
+    for (int row = 0; row < 16; ++row) {
+        uint8_t* yuvSeg = yuv + (rowIndex + row) * fStrides[0];
+        for (int i = 0; i < (width >> 1); ++i) {
+            int indexY = row * width + (i << 1);
+            int indexU = row * (width >> 1) + i;
+            yRows[indexY] = yuvSeg[0];
+            yRows[indexY + 1] = yuvSeg[2];
+            uRows[indexU] = yuvSeg[1];
+            vRows[indexU] = yuvSeg[3];
+            yuvSeg += 4;
+        }
+    }
+}
+
+void Yuv422IToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) {
+    // cb and cr are horizontally downsampled and vertically downsampled as well.
+    cinfo->comp_info[0].h_samp_factor = 2;
+    cinfo->comp_info[0].v_samp_factor = 2;
+    cinfo->comp_info[1].h_samp_factor = 1;
+    cinfo->comp_info[1].v_samp_factor = 2;
+    cinfo->comp_info[2].h_samp_factor = 1;
+    cinfo->comp_info[2].v_samp_factor = 2;
+}
+///////////////////////////////////////////////////////////////////////////////
+
+static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv,
+        int format, int width, int height, jintArray offsets,
+        jintArray strides, int jpegQuality, jobject jstream,
+        jbyteArray jstorage) {
+    jbyte* yuv = env->GetByteArrayElements(inYuv, NULL);
+    SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
+
+    jint* imgOffsets = env->GetIntArrayElements(offsets, NULL);
+    jint* imgStrides = env->GetIntArrayElements(strides, NULL);
+    YuvToJpegEncoder* encoder = YuvToJpegEncoder::create(format, imgStrides);
+    if (encoder == NULL) {
+        return false;
+    }
+    encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality);
+
+    delete encoder;
+    env->ReleaseByteArrayElements(inYuv, yuv, 0);
+    env->ReleaseIntArrayElements(offsets, imgOffsets, 0);
+    env->ReleaseIntArrayElements(strides, imgStrides, 0);
+    return true;
+}
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gYuvImageMethods[] = {
+    {   "nativeCompressToJpeg",  "([BIII[I[IILjava/io/OutputStream;[B)Z",
+        (void*)YuvImage_compressToJpeg }
+};
+
+#define kClassPathName  "android/graphics/YuvImage"
+
+int register_android_graphics_YuvImage(JNIEnv* env);
+int register_android_graphics_YuvImage(JNIEnv* env)
+{
+    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+            gYuvImageMethods, SK_ARRAY_COUNT(gYuvImageMethods));
+}
diff --git a/core/jni/android/graphics/YuvToJpegEncoder.h b/core/jni/android/graphics/YuvToJpegEncoder.h
new file mode 100644
index 0000000..97106ce
--- /dev/null
+++ b/core/jni/android/graphics/YuvToJpegEncoder.h
@@ -0,0 +1,74 @@
+#ifndef YuvToJpegEncoder_DEFINED
+#define YuvToJpegEncoder_DEFINED
+
+#include "SkTypes.h"
+#include "SkStream.h"
+extern "C" {
+    #include "jpeglib.h"
+    #include "jerror.h"
+}
+
+class YuvToJpegEncoder {
+public:
+    /** Create an encoder based on the YUV format.
+     *
+     *  @param pixelFormat The yuv pixel format as defined in ui/PixelFormat.h.
+     *  @param strides The number of row bytes in each image plane.
+     *  @return an encoder based on the pixelFormat.
+     */
+    static YuvToJpegEncoder* create(int pixelFormat, int* strides);
+
+    YuvToJpegEncoder(int* strides);
+
+    /** Encode YUV data to jpeg,  which is output to a stream.
+     *
+     *  @param stream The jpeg output stream.
+     *  @param inYuv The input yuv data.
+     *  @param width Width of the the Yuv data in terms of pixels.
+     *  @param height Height of the Yuv data in terms of pixels.
+     *  @param offsets The offsets in each image plane with respect to inYuv.
+     *  @param jpegQuality Picture quality in [0, 100].
+     *  @return true if successfully compressed the stream.
+     */
+    bool encode(SkWStream* stream,  void* inYuv, int width,
+           int height, int* offsets, int jpegQuality);
+
+    virtual ~YuvToJpegEncoder() {}
+
+protected:
+    int fNumPlanes;
+    int* fStrides;
+    void setJpegCompressStruct(jpeg_compress_struct* cinfo, int width,
+            int height, int quality);
+    virtual void configSamplingFactors(jpeg_compress_struct* cinfo) = 0;
+    virtual void compress(jpeg_compress_struct* cinfo,
+            uint8_t* yuv, int* offsets) = 0;
+};
+
+class Yuv420SpToJpegEncoder : public YuvToJpegEncoder {
+public:
+     Yuv420SpToJpegEncoder(int* strides);
+     virtual ~Yuv420SpToJpegEncoder() {}
+
+private:
+     void configSamplingFactors(jpeg_compress_struct* cinfo);
+     void deinterleaveYuv(uint8_t* yuv, int width, int height,
+            uint8_t*& yPlanar, uint8_t*& uPlanar, uint8_t*& vPlanar);
+     void deinterleave(uint8_t* vuPlanar, uint8_t* uRows, uint8_t* vRows,
+             int rowIndex, int width);
+     void compress(jpeg_compress_struct* cinfo, uint8_t* yuv, int* offsets);
+};
+
+class Yuv422IToJpegEncoder : public YuvToJpegEncoder {
+public:
+    Yuv422IToJpegEncoder(int* strides);
+    virtual ~Yuv422IToJpegEncoder() {}
+
+private:
+    void configSamplingFactors(jpeg_compress_struct* cinfo);
+    void compress(jpeg_compress_struct* cinfo, uint8_t* yuv, int* offsets);
+    void deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows,
+            uint8_t* vRows, int rowIndex, int width, int height);
+};
+
+#endif
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 64ee4f0..1a5987c 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -546,6 +546,18 @@
     }
 }
 
+static void android_hardware_Camera_setDisplayOrientation(JNIEnv *env, jobject thiz,
+        jint value)
+{
+    LOGV("setDisplayOrientation");
+    sp<Camera> camera = get_native_camera(env, thiz, NULL);
+    if (camera == 0) return;
+
+    if (camera->sendCommand(CAMERA_CMD_SET_DISPLAY_ORIENTATION, value, 0) != NO_ERROR) {
+        jniThrowException(env, "java/lang/RuntimeException", "set display orientation failed");
+    }
+}
+
 //-------------------------------------------------
 
 static JNINativeMethod camMethods[] = {
@@ -603,6 +615,9 @@
   { "stopSmoothZoom",
     "()V",
     (void *)android_hardware_Camera_stopSmoothZoom },
+  { "setDisplayOrientation",
+    "(I)V",
+    (void *)android_hardware_Camera_setDisplayOrientation },
 };
 
 struct field {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d81476a..ac6467d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -700,6 +700,46 @@
         android:label="@string/permlab_mount_format_filesystems"
         android:description="@string/permdesc_mount_format_filesystems" />
 
+    <!-- Allows access to ASEC non-destructive API calls
+         @hide  -->
+    <permission android:name="android.permission.ASEC_ACCESS"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_asec_access"
+        android:description="@string/permdesc_asec_access" />
+
+    <!-- Allows creation of ASEC volumes
+         @hide  -->
+    <permission android:name="android.permission.ASEC_CREATE"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_asec_create"
+        android:description="@string/permdesc_asec_create" />
+
+    <!-- Allows destruction of ASEC volumes
+         @hide  -->
+    <permission android:name="android.permission.ASEC_DESTROY"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_asec_destroy"
+        android:description="@string/permdesc_asec_destroy" />
+
+    <!-- Allows mount / unmount of ASEC volumes
+         @hide  -->
+    <permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_asec_mount_unmount"
+        android:description="@string/permdesc_asec_mount_unmount" />
+
+    <!-- Allows rename of ASEC volumes
+         @hide  -->
+    <permission android:name="android.permission.ASEC_RENAME"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_asec_rename"
+        android:description="@string/permdesc_asec_rename" />
+
     <!-- Allows applications to disable the keyguard -->
     <permission android:name="android.permission.DISABLE_KEYGUARD"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 287e3a0..794a9f0 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1394,7 +1394,7 @@
              be a dimension (such as "12dip") for a constant width or one of
              the special constants. -->
         <attr name="layout_width" format="dimension">
-            <!-- {@deprecated Use match_parent instead.} -->
+            <!-- <strong>Deprecated.</strong> Use {@code match_parent} instead. -->
             <enum name="fill_parent" value="-1" />
             <!-- The view should be as big as its parent (minus padding). -->
             <enum name="match_parent" value="-1" />
@@ -1407,7 +1407,7 @@
              be a dimension (such as "12dip") for a constant height or one of
              the special constants. -->
         <attr name="layout_height" format="dimension">
-            <!-- {@deprecated Use match_parent instead.} -->
+            <!-- <strong>Deprecated.</strong> Use {@code match_parent} instead. -->
             <enum name="fill_parent" value="-1" />
             <!-- The view should be as big as its parent (minus padding). -->
             <enum name="match_parent" value="-1" />
@@ -2122,7 +2122,7 @@
              to match the width of the screen, or wrap_content to match the width
              of the anchored view. -->
         <attr name="dropDownWidth" format="dimension">
-            <!-- {@deprecated Use match_parent instead.} -->
+            <!-- <strong>Deprecated.</strong> Use {@code match_parent} instead. -->
             <enum name="fill_parent" value="-1" />
             <!-- The dropdown should fit the width of the screen. -->
             <enum name="match_parent" value="-1" />
@@ -2134,7 +2134,7 @@
              to fill the width of the screen, or wrap_content to match the height of
              the content of the drop down. -->
         <attr name="dropDownHeight" format="dimension">
-            <!-- {@deprecated Use match_parent instead.} -->
+            <!-- <strong>Deprecated.</strong> Use {@code match_parent} instead. -->
             <enum name="fill_parent" value="-1" />
             <!-- The dropdown should fill the width of the screen. -->
             <enum name="match_parent" value="-1" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0ef07ff..8c46f58 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -877,6 +877,31 @@
     <string name="permdesc_mount_format_filesystems">Allows the application to format removable storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_asec_access">get information on secure storage</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_asec_access">Allows the application to get information on secure storage.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_asec_create">create secure storage</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_asec_create">Allows the application to create secure storage.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_asec_destroy">destroy secure storage</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_asec_destroy">Allows the application to destroy secure storage.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_asec_mount_unmount">mount / unmount secure storage</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_asec_mount_unmount">Allows the application to mount / unmount secure storage.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_asec_rename">rename secure storage</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_asec_rename">Allows the application to rename secure storage.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_vibrate">control vibrator</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_vibrate">Allows the application to control
diff --git a/graphics/java/android/graphics/YuvImage.java b/graphics/java/android/graphics/YuvImage.java
new file mode 100644
index 0000000..4a3bd47
--- /dev/null
+++ b/graphics/java/android/graphics/YuvImage.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2010 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.graphics;
+
+import java.io.OutputStream;
+
+/**
+ * @hide pending API council approval
+ *
+ * YuvImage contains YUV data and provides a method that compresses a region of
+ * the YUV data to a Jpeg. The YUV data should be provided as a single byte
+ * array irrespective of the number of image planes in it. The stride of each
+ * image plane should be provided as well.
+ *
+ * To compress a rectangle region in the YUV data, users have to specify a
+ * region by width, height and offsets, where each image plane has a
+ * corresponding offset. All offsets are measured as a displacement in bytes
+ * from yuv[0], where yuv[0] is the beginning of the yuv data.
+ */
+public class YuvImage {
+
+    /**
+     * Number of bytes of temp storage we use for communicating between the
+     * native compressor and the java OutputStream.
+     */
+    private final static int WORKING_COMPRESS_STORAGE = 4096;
+
+   /**
+     * The YUV format as defined in {@link PixelFormat}.
+     */
+    private int mFormat;
+
+    /**
+     * The raw YUV data.
+     * In the case of more than one image plane, the image planes must be
+     * concatenated into a single byte array.
+     */
+    private byte[] mData;
+
+    /**
+     * The number of row bytes in each image plane.
+     */
+    private int[] mStrides;
+
+    /**
+     * Construct an YuvImage.
+     *
+     * @param yuv The YUV data. In the case of more than one image plane, all the planes must be
+     *            concatenated into a single byte array.
+     * @param format The YUV data format as defined in {@link PixelFormat}.
+     * @param strides Row bytes of each image plane.
+     */
+    public YuvImage(byte[] yuv, int format, int[] strides) {
+        if ((yuv == null) || (strides == null)) {
+            throw new IllegalArgumentException(
+                    "yuv or strides cannot be null");
+        }
+        mData = yuv;
+        mFormat = format;
+        mStrides = strides;
+    }
+
+    /**
+     * Compress a rectangle region in the YuvImage to a jpeg.
+     * Only PixelFormat.YCbCr_420_SP and PixelFormat.YCbCr_422_I
+     * are supported for now.
+     *
+     * @param width The width of the rectangle region.
+     * @param height The height of the rectangle region.
+     * @param offsets The offsets of the rectangle region in each image plane.
+     *                The offsets are measured as a displacement in bytes from
+     *                yuv[0], where yuv[0] is the beginning of the yuv data.
+     * @param quality  Hint to the compressor, 0-100. 0 meaning compress for
+     *                 small size, 100 meaning compress for max quality.
+     * @param stream   The outputstream to write the compressed data.
+     *
+     * @return true if successfully compressed to the specified stream.
+     *
+     */
+    public boolean compressToJpeg(int width, int height, int[] offsets, int quality,
+            OutputStream stream) {
+        if (!validate(mFormat, width, height, offsets)) {
+            return false;
+        }
+
+        if (quality < 0 || quality > 100) {
+            throw new IllegalArgumentException("quality must be 0..100");
+        }
+
+        if (stream == null) {
+            throw new NullPointerException();
+        }
+
+        return nativeCompressToJpeg(mData, mFormat, width, height, offsets,
+                mStrides, quality, stream, new byte[WORKING_COMPRESS_STORAGE]);
+    }
+
+    /**
+     * @return the YUV data.
+     */
+    public byte[] getYuvData() {
+        return mData;
+    }
+
+    /**
+     * @return the YUV format as defined in {@link PixelFormat}.
+     */
+    public int getYuvFormat() {
+        return mFormat;
+    }
+
+    /**
+     * @return the number of row bytes in each image plane.
+     */
+    public int[] getStrides() {
+        return mStrides;
+    }
+
+    protected boolean validate(int format, int width, int height, int[] offsets) {
+        if (format != PixelFormat.YCbCr_420_SP &&
+                format != PixelFormat.YCbCr_422_I) {
+            throw new IllegalArgumentException(
+                    "only support PixelFormat.YCbCr_420_SP " +
+                    "and PixelFormat.YCbCr_422_I for now");
+        }
+
+        if (offsets.length != mStrides.length) {
+            throw new IllegalArgumentException(
+                    "the number of image planes are mismatched");
+        }
+
+        if (width <= 0  || height <= 0) {
+            throw new IllegalArgumentException(
+                    "width and height must large than 0");
+        }
+
+        int requiredSize;
+        if (format == PixelFormat.YCbCr_420_SP) {
+            requiredSize = height * mStrides[0] +(height >> 1) * mStrides[1];
+        } else {
+            requiredSize = height * mStrides[0];
+        }
+
+        if (requiredSize > mData.length) {
+            throw new IllegalArgumentException(
+                    "width or/and height is larger than the yuv data");
+        }
+
+        return true;
+    }
+
+    //////////// native methods
+
+    private static native boolean nativeCompressToJpeg(byte[] oriYuv,
+            int format, int width, int height, int[] offsets, int[] strides,
+            int quality, OutputStream stream, byte[] tempStorage);
+}
diff --git a/include/media/stagefright/CachingDataSource.h b/include/media/stagefright/CachingDataSource.h
index b0fc4b2..30b7ad9 100644
--- a/include/media/stagefright/CachingDataSource.h
+++ b/include/media/stagefright/CachingDataSource.h
@@ -33,6 +33,8 @@
 
     virtual ssize_t readAt(off_t offset, void *data, size_t size);
 
+    virtual uint32_t flags();
+
 protected:
     virtual ~CachingDataSource();
 
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index f88666a..0c0ace0 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -31,6 +31,10 @@
 
 class DataSource : public RefBase {
 public:
+    enum Flags {
+        kWantsPrefetching = 1,
+    };
+
     static sp<DataSource> CreateFromURI(const char *uri);
 
     DataSource() {}
@@ -45,6 +49,10 @@
     // May return ERROR_UNSUPPORTED.
     virtual status_t getSize(off_t *size);
 
+    virtual uint32_t flags() {
+        return 0;
+    }
+
     ////////////////////////////////////////////////////////////////////////////
 
     bool sniff(String8 *mimeType, float *confidence);
diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h
index d5dc9e6..3075f1c 100644
--- a/include/media/stagefright/HTTPDataSource.h
+++ b/include/media/stagefright/HTTPDataSource.h
@@ -33,6 +33,10 @@
 
     virtual ssize_t readAt(off_t offset, void *data, size_t size);
 
+    virtual uint32_t flags() {
+        return kWantsPrefetching;
+    }
+
 protected:
     virtual ~HTTPDataSource();
 
@@ -52,6 +56,8 @@
 
     status_t mInitCheck;
 
+    ssize_t sendRangeRequest(size_t offset);
+
     HTTPDataSource(const HTTPDataSource &);
     HTTPDataSource &operator=(const HTTPDataSource &);
 };
diff --git a/include/ui/Camera.h b/include/ui/Camera.h
index 5219772..c506fb8 100644
--- a/include/ui/Camera.h
+++ b/include/ui/Camera.h
@@ -82,6 +82,7 @@
 enum {
     CAMERA_CMD_START_SMOOTH_ZOOM     = 1,
     CAMERA_CMD_STOP_SMOOTH_ZOOM      = 2,
+    CAMERA_CMD_SET_DISPLAY_ORIENTATION = 3,
 };
 
 // camera fatal errors
@@ -209,4 +210,3 @@
 }; // namespace android
 
 #endif
-
diff --git a/include/ui/CameraParameters.h b/include/ui/CameraParameters.h
index a5ea133..cae0676 100644
--- a/include/ui/CameraParameters.h
+++ b/include/ui/CameraParameters.h
@@ -29,12 +29,6 @@
     CameraParameters(const String8 &params) { unflatten(params); }
     ~CameraParameters();
 
-    enum {
-        CAMERA_ORIENTATION_UNKNOWN = 0,
-        CAMERA_ORIENTATION_PORTRAIT = 1,
-        CAMERA_ORIENTATION_LANDSCAPE = 2,
-    };
-
     String8 flatten() const;
     void unflatten(const String8 &params);
 
@@ -63,9 +57,6 @@
     void setPictureFormat(const char *format);
     const char *getPictureFormat() const;
 
-    int getOrientation() const;
-    void setOrientation(int orientation);
-
     void dump() const;
     status_t dump(int fd, const Vector<String16>& args) const;
 
diff --git a/libs/ui/CameraParameters.cpp b/libs/ui/CameraParameters.cpp
index 2e0409b..a94f6b9 100644
--- a/libs/ui/CameraParameters.cpp
+++ b/libs/ui/CameraParameters.cpp
@@ -121,9 +121,6 @@
 const char CameraParameters::FOCUS_MODE_MACRO[] = "macro";
 const char CameraParameters::FOCUS_MODE_FIXED[] = "fixed";
 
-static const char* portrait = "portrait";
-static const char* landscape = "landscape";
-
 CameraParameters::CameraParameters()
                 : mMap()
 {
@@ -282,23 +279,6 @@
     set(KEY_PREVIEW_FORMAT, format);
 }
 
-int CameraParameters::getOrientation() const
-{
-    const char* orientation = get("orientation");
-    if (orientation && !strcmp(orientation, portrait))
-        return CAMERA_ORIENTATION_PORTRAIT;
-    return CAMERA_ORIENTATION_LANDSCAPE;
-}
-
-void CameraParameters::setOrientation(int orientation)
-{
-    if (orientation == CAMERA_ORIENTATION_PORTRAIT) {
-        set("orientation", portrait);
-    } else {
-        set("orientation", landscape);
-    }
-}
-
 const char *CameraParameters::getPreviewFormat() const
 {
     return get(KEY_PREVIEW_FORMAT);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 3813907..dbb52c6 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -31,6 +31,7 @@
         MPEG4Extractor.cpp        \
         MPEG4Writer.cpp           \
         MediaExtractor.cpp        \
+        Prefetcher.cpp            \
         SampleIterator.cpp        \
         SampleTable.cpp           \
         ShoutcastSource.cpp       \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index f6cd46a..42b9acc 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -19,6 +19,7 @@
 #include <utils/Log.h>
 
 #include "include/AwesomePlayer.h"
+#include "include/Prefetcher.h"
 #include "include/SoftwareRenderer.h"
 
 #include <binder/IPCThreadState.h>
@@ -118,6 +119,8 @@
     mVideoEventPending = false;
     mStreamDoneEvent = new AwesomeEvent(this, 1);
     mStreamDoneEventPending = false;
+    mBufferingEvent = new AwesomeEvent(this, 2);
+    mBufferingEventPending = false;
 
     mQueue.start();
 
@@ -132,11 +135,16 @@
     mClient.disconnect();
 }
 
-void AwesomePlayer::cancelPlayerEvents() {
+void AwesomePlayer::cancelPlayerEvents(bool keepBufferingGoing) {
     mQueue.cancelEvent(mVideoEvent->eventID());
     mVideoEventPending = false;
     mQueue.cancelEvent(mStreamDoneEvent->eventID());
     mStreamDoneEventPending = false;
+
+    if (!keepBufferingGoing) {
+        mQueue.cancelEvent(mBufferingEvent->eventID());
+        mBufferingEventPending = false;
+    }
 }
 
 void AwesomePlayer::setListener(const wp<MediaPlayerBase> &listener) {
@@ -149,12 +157,22 @@
 
     reset_l();
 
-    sp<MediaExtractor> extractor = MediaExtractor::CreateFromURI(uri);
+    sp<DataSource> dataSource = DataSource::CreateFromURI(uri);
+
+    if (dataSource == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
 
     if (extractor == NULL) {
         return UNKNOWN_ERROR;
     }
 
+    if (dataSource->flags() & DataSource::kWantsPrefetching) {
+        mPrefetcher = new Prefetcher;
+    }
+
     return setDataSource_l(extractor);
 }
 
@@ -182,8 +200,6 @@
 }
 
 status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
-    reset_l();
-
     bool haveAudio = false;
     bool haveVideo = false;
     for (size_t i = 0; i < extractor->countTracks(); ++i) {
@@ -253,6 +269,8 @@
 
     mSeeking = false;
     mSeekTimeUs = 0;
+
+    mPrefetcher.clear();
 }
 
 // static
@@ -278,16 +296,38 @@
     }
 }
 
-void AwesomePlayer::notifyListener_l(int msg) {
+void AwesomePlayer::notifyListener_l(int msg, int ext1) {
     if (mListener != NULL) {
         sp<MediaPlayerBase> listener = mListener.promote();
 
         if (listener != NULL) {
-            listener->sendEvent(msg);
+            listener->sendEvent(msg, ext1);
         }
     }
 }
 
+void AwesomePlayer::onBufferingUpdate() {
+    Mutex::Autolock autoLock(mLock);
+    mBufferingEventPending = false;
+
+    if (mDurationUs >= 0) {
+        int64_t cachedDurationUs = mPrefetcher->getCachedDurationUs();
+        int64_t positionUs = 0;
+        if (mVideoRenderer != NULL) {
+            positionUs = mVideoTimeUs;
+        } else if (mAudioPlayer != NULL) {
+            positionUs = mAudioPlayer->getMediaTimeUs();
+        }
+
+        cachedDurationUs += positionUs;
+
+        double percentage = (double)cachedDurationUs / mDurationUs;
+        notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage * 100.0);
+
+        postBufferingEvent_l();
+    }
+}
+
 void AwesomePlayer::onStreamDone() {
     // Posted whenever any stream finishes playing.
 
@@ -361,6 +401,8 @@
         seekAudioIfNecessary_l();
     }
 
+    postBufferingEvent_l();
+
     return OK;
 }
 
@@ -414,7 +456,7 @@
         return OK;
     }
 
-    cancelPlayerEvents();
+    cancelPlayerEvents(true /* keepBufferingGoing */);
 
     if (mAudioPlayer != NULL) {
         mAudioPlayer->pause();
@@ -518,11 +560,15 @@
     return OK;
 }
 
-status_t AwesomePlayer::setAudioSource(const sp<MediaSource> &source) {
+status_t AwesomePlayer::setAudioSource(sp<MediaSource> source) {
     if (source == NULL) {
         return UNKNOWN_ERROR;
     }
 
+    if (mPrefetcher != NULL) {
+        source = mPrefetcher->addSource(source);
+    }
+
     sp<MetaData> meta = source->getFormat();
 
     const char *mime;
@@ -549,11 +595,15 @@
     return mAudioSource != NULL ? OK : UNKNOWN_ERROR;
 }
 
-status_t AwesomePlayer::setVideoSource(const sp<MediaSource> &source) {
+status_t AwesomePlayer::setVideoSource(sp<MediaSource> source) {
     if (source == NULL) {
         return UNKNOWN_ERROR;
     }
 
+    if (mPrefetcher != NULL) {
+        source = mPrefetcher->addSource(source);
+    }
+
     mVideoSource = OMXCodec::Create(
             mClient.interface(), source->getFormat(),
             false, // createEncoder
@@ -580,9 +630,13 @@
     if (code == 1) {
         onStreamDone();
         return;
+    } else if (code == 2) {
+        onBufferingUpdate();
+        return;
     }
 
     Mutex::Autolock autoLock(mLock);
+
     mVideoEventPending = false;
 
     if (mSeeking) {
@@ -718,5 +772,17 @@
     mQueue.postEvent(mStreamDoneEvent);
 }
 
+void AwesomePlayer::postBufferingEvent_l() {
+    if (mPrefetcher == NULL) {
+        return;
+    }
+
+    if (mBufferingEventPending) {
+        return;
+    }
+    mBufferingEventPending = true;
+    mQueue.postEventWithDelay(mBufferingEvent, 1000000ll);
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/CachingDataSource.cpp b/media/libstagefright/CachingDataSource.cpp
index 23f4897..8d04ead 100644
--- a/media/libstagefright/CachingDataSource.cpp
+++ b/media/libstagefright/CachingDataSource.cpp
@@ -65,6 +65,10 @@
     return mSource->initCheck();
 }
 
+uint32_t CachingDataSource::flags() {
+    return mSource->flags();
+}
+
 ssize_t CachingDataSource::readAt(off_t offset, void *data, size_t size) {
     Mutex::Autolock autoLock(mLock);
 
diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp
index 7e8bbc6..135a044 100644
--- a/media/libstagefright/HTTPDataSource.cpp
+++ b/media/libstagefright/HTTPDataSource.cpp
@@ -190,30 +190,12 @@
     mHttp = NULL;
 }
 
-ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) {
-    if (offset >= mBufferOffset
-            && offset < (off_t)(mBufferOffset + mBufferLength)) {
-        size_t num_bytes_available = mBufferLength - (offset - mBufferOffset);
-
-        size_t copy = num_bytes_available;
-        if (copy > size) {
-            copy = size;
-        }
-
-        memcpy(data, (const char *)mBuffer + (offset - mBufferOffset), copy);
-
-        return copy;
-    }
-
-    mBufferOffset = offset;
-    mBufferLength = 0;
-
+ssize_t HTTPDataSource::sendRangeRequest(size_t offset) {
     char host[128];
     sprintf(host, "Host: %s\r\n", mHost);
 
     char range[128];
-    sprintf(range, "Range: bytes=%ld-%ld\r\n\r\n",
-            mBufferOffset, mBufferOffset + kBufferSize - 1);
+    sprintf(range, "Range: bytes=%d-\r\n\r\n", offset);
 
     int http_status;
 
@@ -251,12 +233,44 @@
     char *end;
     unsigned long contentLength = strtoul(value.c_str(), &end, 10);
 
-    ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength);
+    return contentLength;
+}
 
-    if (num_bytes_received <= 0) {
-        return num_bytes_received;
+ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) {
+    if (offset >= mBufferOffset
+            && offset < (off_t)(mBufferOffset + mBufferLength)) {
+        size_t num_bytes_available = mBufferLength - (offset - mBufferOffset);
+
+        size_t copy = num_bytes_available;
+        if (copy > size) {
+            copy = size;
+        }
+
+        memcpy(data, (const char *)mBuffer + (offset - mBufferOffset), copy);
+
+        return copy;
     }
 
+    ssize_t contentLength = 0;
+    if (mBufferLength <= 0 || offset != mBufferOffset + mBufferLength) {
+        mHttp->disconnect();
+        contentLength = sendRangeRequest(offset);
+
+        if (contentLength > kBufferSize) {
+            contentLength = kBufferSize;
+        }
+    } else {
+        contentLength = kBufferSize;
+    }
+
+    mBufferOffset = offset;
+
+    if (contentLength <= 0) {
+        return contentLength;
+    }
+
+    ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength);
+
     mBufferLength = (size_t)num_bytes_received;
 
     size_t copy = mBufferLength;
diff --git a/media/libstagefright/Prefetcher.cpp b/media/libstagefright/Prefetcher.cpp
new file mode 100644
index 0000000..93e3fdc
--- /dev/null
+++ b/media/libstagefright/Prefetcher.cpp
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2010 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_TAG "Prefetcher"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "include/Prefetcher.h"
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/List.h>
+
+namespace android {
+
+struct PrefetchedSource : public MediaSource {
+    PrefetchedSource(
+            const sp<Prefetcher> &prefetcher,
+            size_t index,
+            const sp<MediaSource> &source);
+
+    virtual status_t start(MetaData *params);
+    virtual status_t stop();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options);
+
+    virtual sp<MetaData> getFormat();
+
+protected:
+    virtual ~PrefetchedSource();
+
+private:
+    friend struct Prefetcher;
+
+    Mutex mLock;
+    Condition mCondition;
+
+    sp<Prefetcher> mPrefetcher;
+    sp<MediaSource> mSource;
+    size_t mIndex;
+    bool mStarted;
+    bool mReachedEOS;
+    int64_t mSeekTimeUs;
+    int64_t mCacheDurationUs;
+
+    List<MediaBuffer *> mCachedBuffers;
+
+    // Returns true iff source is currently caching.
+    bool getCacheDurationUs(int64_t *durationUs);
+
+    void updateCacheDuration_l();
+    void clearCache_l();
+
+    void cacheMore();
+
+    PrefetchedSource(const PrefetchedSource &);
+    PrefetchedSource &operator=(const PrefetchedSource &);
+};
+
+Prefetcher::Prefetcher()
+    : mDone(false),
+      mThreadExited(false) {
+    startThread();
+}
+
+Prefetcher::~Prefetcher() {
+    stopThread();
+}
+
+sp<MediaSource> Prefetcher::addSource(const sp<MediaSource> &source) {
+    Mutex::Autolock autoLock(mLock);
+
+    sp<PrefetchedSource> psource =
+        new PrefetchedSource(this, mSources.size(), source);
+
+    mSources.add(psource);
+
+    return psource;
+}
+
+void Prefetcher::startThread() {
+    mThreadExited = false;
+    mDone = false;
+
+    int res = androidCreateThreadEtc(
+            ThreadWrapper, this, "Prefetcher",
+            ANDROID_PRIORITY_DEFAULT, 0, &mThread);
+
+    CHECK_EQ(res, 1);
+}
+
+void Prefetcher::stopThread() {
+    Mutex::Autolock autoLock(mLock);
+
+    while (!mThreadExited) {
+        mDone = true;
+        mCondition.signal();
+        mCondition.wait(mLock);
+    }
+}
+
+// static
+int Prefetcher::ThreadWrapper(void *me) {
+    static_cast<Prefetcher *>(me)->threadFunc();
+
+    return 0;
+}
+
+// Cache about 10secs for each source.
+static int64_t kMaxCacheDurationUs = 10000000ll;
+
+void Prefetcher::threadFunc() {
+    for (;;) {
+        Mutex::Autolock autoLock(mLock);
+        if (mDone) {
+            mThreadExited = true;
+            mCondition.signal();
+            break;
+        }
+        mCondition.waitRelative(mLock, 10000000ll);
+
+        int64_t minCacheDurationUs = -1;
+        ssize_t minIndex = -1;
+        for (size_t i = 0; i < mSources.size(); ++i) {
+            sp<PrefetchedSource> source = mSources[i].promote();
+
+            if (source == NULL) {
+                continue;
+            }
+
+            int64_t cacheDurationUs;
+            if (!source->getCacheDurationUs(&cacheDurationUs)) {
+                continue;
+            }
+
+            if (cacheDurationUs >= kMaxCacheDurationUs) {
+                continue;
+            }
+
+            if (minIndex < 0 || cacheDurationUs < minCacheDurationUs) {
+                minCacheDurationUs = cacheDurationUs;
+                minIndex = i;
+            }
+        }
+
+        if (minIndex < 0) {
+            continue;
+        }
+
+        sp<PrefetchedSource> source = mSources[minIndex].promote();
+        if (source != NULL) {
+            source->cacheMore();
+        }
+    }
+}
+
+int64_t Prefetcher::getCachedDurationUs() {
+    Mutex::Autolock autoLock(mLock);
+
+    int64_t minCacheDurationUs = -1;
+    ssize_t minIndex = -1;
+    for (size_t i = 0; i < mSources.size(); ++i) {
+        int64_t cacheDurationUs;
+        sp<PrefetchedSource> source = mSources[i].promote();
+        if (source == NULL) {
+            continue;
+        }
+
+        if (!source->getCacheDurationUs(&cacheDurationUs)) {
+            continue;
+        }
+
+        if (cacheDurationUs >= kMaxCacheDurationUs) {
+            continue;
+        }
+
+        if (minIndex < 0 || cacheDurationUs < minCacheDurationUs) {
+            minCacheDurationUs = cacheDurationUs;
+            minIndex = i;
+        }
+    }
+
+    return minCacheDurationUs < 0 ? 0 : minCacheDurationUs;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+PrefetchedSource::PrefetchedSource(
+        const sp<Prefetcher> &prefetcher,
+        size_t index,
+        const sp<MediaSource> &source)
+    : mPrefetcher(prefetcher),
+      mSource(source),
+      mIndex(index),
+      mStarted(false),
+      mReachedEOS(false),
+      mSeekTimeUs(0),
+      mCacheDurationUs(0) {
+}
+
+PrefetchedSource::~PrefetchedSource() {
+    if (mStarted) {
+        stop();
+    }
+}
+
+status_t PrefetchedSource::start(MetaData *params) {
+    Mutex::Autolock autoLock(mLock);
+
+    status_t err = mSource->start(params);
+
+    if (err != OK) {
+        return err;
+    }
+
+    mStarted = true;
+
+    for (;;) {
+        // Buffer 2 secs on startup.
+        if (mReachedEOS || mCacheDurationUs > 2000000) {
+            break;
+        }
+
+        mCondition.wait(mLock);
+    }
+
+    return OK;
+}
+
+status_t PrefetchedSource::stop() {
+    Mutex::Autolock autoLock(mLock);
+
+    clearCache_l();
+
+    status_t err = mSource->stop();
+
+    mStarted = false;
+
+    return err;
+}
+
+status_t PrefetchedSource::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    *out = NULL;
+
+    Mutex::Autolock autoLock(mLock);
+
+    CHECK(mStarted);
+
+    int64_t seekTimeUs;
+    if (options && options->getSeekTo(&seekTimeUs)) {
+        CHECK(seekTimeUs >= 0);
+
+        clearCache_l();
+
+        mReachedEOS = false;
+        mSeekTimeUs = seekTimeUs;
+    }
+
+    while (!mReachedEOS && mCachedBuffers.empty()) {
+        mCondition.wait(mLock);
+    }
+
+    if (mCachedBuffers.empty()) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    *out = *mCachedBuffers.begin();
+    mCachedBuffers.erase(mCachedBuffers.begin());
+    updateCacheDuration_l();
+
+    return OK;
+}
+
+sp<MetaData> PrefetchedSource::getFormat() {
+    return mSource->getFormat();
+}
+
+bool PrefetchedSource::getCacheDurationUs(int64_t *durationUs) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (!mStarted || mReachedEOS) {
+        *durationUs = 0;
+
+        return false;
+    }
+
+    *durationUs = mCacheDurationUs;
+
+    return true;
+}
+
+void PrefetchedSource::cacheMore() {
+    Mutex::Autolock autoLock(mLock);
+
+    if (!mStarted) {
+        return;
+    }
+
+    MediaBuffer *buffer;
+    MediaSource::ReadOptions options;
+    if (mSeekTimeUs >= 0) {
+        options.setSeekTo(mSeekTimeUs);
+        mSeekTimeUs = -1;
+    }
+
+    status_t err = mSource->read(&buffer, &options);
+
+    if (err != OK) {
+        mReachedEOS = true;
+        mCondition.signal();
+
+        return;
+    }
+
+    CHECK(buffer != NULL);
+
+    MediaBuffer *copy = new MediaBuffer(buffer->range_length());
+    memcpy(copy->data(),
+           (const uint8_t *)buffer->data() + buffer->range_offset(),
+           buffer->range_length());
+
+    sp<MetaData> from = buffer->meta_data();
+    sp<MetaData> to = copy->meta_data();
+
+    int64_t timeUs;
+    if (from->findInt64(kKeyTime, &timeUs)) {
+        to->setInt64(kKeyTime, timeUs);
+    }
+
+    buffer->release();
+    buffer = NULL;
+
+    mCachedBuffers.push_back(copy);
+    updateCacheDuration_l();
+    mCondition.signal();
+}
+
+void PrefetchedSource::updateCacheDuration_l() {
+    if (mCachedBuffers.size() < 2) {
+        mCacheDurationUs = 0;
+    } else {
+        int64_t firstTimeUs, lastTimeUs;
+        CHECK((*mCachedBuffers.begin())->meta_data()->findInt64(
+                    kKeyTime, &firstTimeUs));
+        CHECK((*--mCachedBuffers.end())->meta_data()->findInt64(
+                    kKeyTime, &lastTimeUs));
+
+        mCacheDurationUs = lastTimeUs - firstTimeUs;
+    }
+}
+
+void PrefetchedSource::clearCache_l() {
+    List<MediaBuffer *>::iterator it = mCachedBuffers.begin();
+    while (it != mCachedBuffers.end()) {
+        (*it)->release();
+
+        it = mCachedBuffers.erase(it);
+    }
+
+    updateCacheDuration_l();
+}
+
+}  // namespace android
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index b28a12c..c2e46c0 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -26,10 +26,11 @@
 
 namespace android {
 
+struct AudioPlayer;
 struct MediaBuffer;
 struct MediaExtractor;
 struct MediaSource;
-struct AudioPlayer;
+struct Prefetcher;
 struct TimeSource;
 
 struct AwesomeRenderer : public RefBase {
@@ -109,13 +110,18 @@
     bool mVideoEventPending;
     sp<TimedEventQueue::Event> mStreamDoneEvent;
     bool mStreamDoneEventPending;
+    sp<TimedEventQueue::Event> mBufferingEvent;
+    bool mBufferingEventPending;
 
     void postVideoEvent_l(int64_t delayUs = -1);
+    void postBufferingEvent_l();
     void postStreamDoneEvent_l();
 
     MediaBuffer *mLastVideoBuffer;
     MediaBuffer *mVideoBuffer;
 
+    sp<Prefetcher> mPrefetcher;
+
     status_t setDataSource_l(const sp<MediaExtractor> &extractor);
     void reset_l();
     status_t seekTo_l(int64_t timeUs);
@@ -123,17 +129,19 @@
     void initRenderer_l();
     void seekAudioIfNecessary_l();
 
-    void cancelPlayerEvents();
+    void cancelPlayerEvents(bool keepBufferingGoing = false);
 
-    status_t setAudioSource(const sp<MediaSource> &source);
-    status_t setVideoSource(const sp<MediaSource> &source);
+    status_t setAudioSource(sp<MediaSource> source);
+    status_t setVideoSource(sp<MediaSource> source);
 
     void onEvent(int32_t code);
 
     static void AudioNotify(void *me, int what);
     void onStreamDone();
 
-    void notifyListener_l(int msg);
+    void notifyListener_l(int msg, int ext1 = 0);
+
+    void onBufferingUpdate();
 
     AwesomePlayer(const AwesomePlayer &);
     AwesomePlayer &operator=(const AwesomePlayer &);
diff --git a/media/libstagefright/include/Prefetcher.h b/media/libstagefright/include/Prefetcher.h
new file mode 100644
index 0000000..7a97785
--- /dev/null
+++ b/media/libstagefright/include/Prefetcher.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 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 PREFETCHER_H_
+
+#define PREFETCHER_H_
+
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct MediaSource;
+struct PrefetchedSource;
+
+struct Prefetcher : public RefBase {
+    Prefetcher();
+
+    // Given an existing MediaSource returns a new MediaSource
+    // that will benefit from prefetching/caching the original one.
+    sp<MediaSource> addSource(const sp<MediaSource> &source);
+
+    int64_t getCachedDurationUs();
+
+protected:
+    virtual ~Prefetcher();
+
+private:
+    Mutex mLock;
+    Condition mCondition;
+
+    Vector<wp<PrefetchedSource> > mSources;
+    android_thread_id_t mThread;
+    bool mDone;
+    bool mThreadExited;
+
+    void startThread();
+    void stopThread();
+
+    static int ThreadWrapper(void *me);
+    void threadFunc();
+
+    Prefetcher(const Prefetcher &);
+    Prefetcher &operator=(const Prefetcher &);
+};
+
+}  // namespace android
+
+#endif  // PREFETCHER_H_
diff --git a/preloaded-classes b/preloaded-classes
index 90bbb37..34fcbb9 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -695,7 +695,6 @@
 com.ibm.icu4jni.charset.CharsetICU
 com.ibm.icu4jni.text.CollationAttribute
 com.ibm.icu4jni.text.DecimalFormat
-com.ibm.icu4jni.text.DecimalFormatSymbols
 com.ibm.icu4jni.text.NativeDecimalFormat$UNumberFormatAttribute
 com.ibm.icu4jni.text.RuleBasedCollator
 com.ibm.icu4jni.util.Resources$DefaultTimeZones
diff --git a/services/java/com/android/server/BootReceiver.java b/services/java/com/android/server/BootReceiver.java
index debbbb4..fbb4411 100644
--- a/services/java/com/android/server/BootReceiver.java
+++ b/services/java/com/android/server/BootReceiver.java
@@ -89,14 +89,14 @@
         }
 
         ContentResolver cr = context.getContentResolver();
-        logBootFile(cr, db, "/cache/recovery/log", "SYSTEM_RECOVERY_LOG");
-        logBootFile(cr, db, "/proc/last_kmsg", "SYSTEM_LAST_KMSG");
-        logBootFile(cr, db, "/data/dontpanic/apanic_console", "APANIC_CONSOLE");
-        logBootFile(cr, db, "/data/dontpanic/apanic_threads", "APANIC_THREADS");
+        logBootFile(cr, db, props, "/cache/recovery/log", "SYSTEM_RECOVERY_LOG");
+        logBootFile(cr, db, props, "/proc/last_kmsg", "SYSTEM_LAST_KMSG");
+        logBootFile(cr, db, props, "/data/dontpanic/apanic_console", "APANIC_CONSOLE");
+        logBootFile(cr, db, props, "/data/dontpanic/apanic_threads", "APANIC_THREADS");
     }
 
-    private void logBootFile(ContentResolver cr, DropBoxManager db, String filename, String tag)
-            throws IOException {
+    private void logBootFile(ContentResolver cr, DropBoxManager db,
+            CharSequence headers, String filename, String tag) throws IOException {
         if (cr == null || db == null || !db.isTagEnabled(tag)) return;  // Logging disabled
 
         File file = new File(filename);
@@ -108,10 +108,8 @@
         if (lastTime == fileTime) return;  // Already logged this particular file
         Settings.Secure.putLong(cr, setting, fileTime);
 
-        StringBuilder report = new StringBuilder();
-        report.append("Build: ").append(Build.FINGERPRINT).append("\n");
-        report.append("Kernel: ");
-        report.append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"));
+        StringBuilder report = new StringBuilder(headers);
+        report.append("\n");
         report.append(FileUtils.readTextFile(new File(filename), LOG_SIZE, "[[TRUNCATED]]\n"));
         db.addText(tag, report.toString());
     }
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 85a2e0b..0b7cfae 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -553,7 +553,7 @@
                         if (st == VolumeState.NoMedia) {
                             state = Environment.MEDIA_REMOVED;
                         } else if (st == VolumeState.Idle) {
-                            state = Environment.MEDIA_UNMOUNTED;
+                            state = null;
                             try {
                                 mountVolume(path);
                             } catch (Exception ex) {
@@ -569,7 +569,9 @@
                             throw new Exception(String.format("Unexpected state %d", st));
                         }
                     }
-                    updatePublicVolumeState(path, state);
+                    if (state != null) {
+                        updatePublicVolumeState(path, state);
+                    }
                 } catch (Exception e) {
                     Log.e(TAG, "Error processing initial volume state", e);
                     updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
@@ -1056,11 +1058,21 @@
     }
 
     public String[] getSecureContainerList() throws IllegalStateException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.ASEC_ACCESS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires ASEC_ACCESS permission");
+        }
         return mConnector.doListCommand("list_asec", VoldResponseCode.AsecListResult);
     }
 
     public String createSecureContainer(String id, int sizeMb, String fstype,
                                     String key, int ownerUid) throws IllegalStateException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.ASEC_CREATE)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires ASEC_CREATE permission");
+        }
         String cmd = String.format("create_asec %s %d %s %s %d",
                                    id, sizeMb, fstype, key, ownerUid);
         mConnector.doCommand(cmd);
@@ -1068,15 +1080,31 @@
     }
 
     public void finalizeSecureContainer(String id) throws IllegalStateException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.ASEC_CREATE)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires ASEC_CREATE permission");
+        }
         mConnector.doCommand(String.format("finalize_asec %s", id));
     }
 
     public void destroySecureContainer(String id) throws IllegalStateException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.ASEC_DESTROY)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires ASEC_DESTROY permission");
+        }
         mConnector.doCommand(String.format("destroy_asec %s", id));
     }
    
     public String mountSecureContainer(String id, String key,
                                        int ownerUid) throws IllegalStateException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.ASEC_MOUNT_UNMOUNT)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires ASEC_MOUNT_UNMOUNT permission");
+        }
+        mConnector.doCommand(String.format("destroy_asec %s", id));
         String cmd = String.format("mount_asec %s %s %d",
                                    id, key, ownerUid);
         mConnector.doCommand(cmd);
@@ -1084,16 +1112,31 @@
     }
 
     public void unmountSecureContainer(String id) throws IllegalStateException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.ASEC_MOUNT_UNMOUNT)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires ASEC_MOUNT_UNMOUNT permission");
+        }
         String cmd = String.format("unmount_asec %s", id);
         mConnector.doCommand(cmd);
     }
 
     public void renameSecureContainer(String oldId, String newId) throws IllegalStateException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.ASEC_RENAME)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires ASEC_RENAME permission");
+        }
         String cmd = String.format("rename_asec %s %s", oldId, newId);
         mConnector.doCommand(cmd);
     }
 
     public String getSecureContainerPath(String id) throws IllegalStateException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.ASEC_ACCESS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires ASEC_ACCESS permission");
+        }
         ArrayList<String> rsp = mConnector.doCommand("asec_path " + id);
 
         for (String line : rsp) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 44b6624..c2c6e76 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -61,6 +61,7 @@
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageManager;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PathPermission;
 import android.content.pm.ProviderInfo;
@@ -8845,7 +8846,26 @@
                 sb.append("Process: system_server\n");
             } else {
                 sb.append("Process: ").append(process.processName).append("\n");
-                sb.append("Flags: 0x").append(Integer.toString(process.info.flags, 16)).append("\n");
+            }
+            if (process != null) {
+                int flags = process.info.flags;
+                IPackageManager pm = ActivityThread.getPackageManager();
+                sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n");
+                for (String pkg : process.pkgList) {
+                    sb.append("Package: ").append(pkg);
+                    try {
+                        PackageInfo pi = pm.getPackageInfo(pkg, 0);
+                        if (pi != null) {
+                            sb.append(" v").append(pi.versionCode);
+                            if (pi.versionName != null) {
+                                sb.append(" (").append(pi.versionName).append(")");
+                            }
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error getting package info: " + pkg, e);
+                    }
+                    sb.append("\n");
+                }
             }
             if (activity != null) {
                 sb.append("Activity: ").append(activity.shortComponentName).append("\n");
diff --git a/tests/AndroidTests/src/com/android/unit_tests/internal/util/HanziToPinyinTest.java b/tests/AndroidTests/src/com/android/unit_tests/internal/util/HanziToPinyinTest.java
new file mode 100644
index 0000000..8e1ff0b
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/internal/util/HanziToPinyinTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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.unit_tests.internal.util;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import com.android.internal.util.HanziToPinyin;
+import com.android.internal.util.HanziToPinyin.Token;
+
+import junit.framework.TestCase;
+
+public class HanziToPinyinTest extends TestCase {
+    private final static String ONE_HANZI = "\u675C";
+    private final static String TWO_HANZI = "\u675C\u9D51";
+    private final static String ASSIC = "test";
+    private final static String ONE_UNKNOWN = "\uFF71";
+    private final static String MISC = "test\u675C   Test with space\uFF71\uFF71\u675C";
+
+    @SmallTest
+    public void testGetToken() throws Exception {
+        ArrayList<Token> tokens = HanziToPinyin.getInstance().get(ONE_HANZI);
+        assertEquals(tokens.size(), 1);
+        assertEquals(tokens.get(0).type, Token.PINYIN);
+        assertTrue(tokens.get(0).target.equalsIgnoreCase("DU"));
+
+        tokens = HanziToPinyin.getInstance().get(TWO_HANZI);
+        assertEquals(tokens.size(), 2);
+        assertEquals(tokens.get(0).type, Token.PINYIN);
+        assertEquals(tokens.get(1).type, Token.PINYIN);
+        assertTrue(tokens.get(0).target.equalsIgnoreCase("DU"));
+        assertTrue(tokens.get(1).target.equalsIgnoreCase("JUAN"));
+
+        tokens = HanziToPinyin.getInstance().get(ASSIC);
+        assertEquals(tokens.size(), 1);
+        assertEquals(tokens.get(0).type, Token.LATIN);
+
+        tokens = HanziToPinyin.getInstance().get(ONE_UNKNOWN);
+        assertEquals(tokens.size(), 1);
+        assertEquals(tokens.get(0).type, Token.UNKNOWN);
+
+        tokens = HanziToPinyin.getInstance().get(MISC);
+        assertEquals(tokens.size(), 7);
+        assertEquals(tokens.get(0).type, Token.LATIN);
+        assertEquals(tokens.get(1).type, Token.PINYIN);
+        assertEquals(tokens.get(2).type, Token.LATIN);
+        assertEquals(tokens.get(3).type, Token.LATIN);
+        assertEquals(tokens.get(4).type, Token.LATIN);
+        assertEquals(tokens.get(5).type, Token.UNKNOWN);
+        assertEquals(tokens.get(6).type, Token.PINYIN);
+    }
+}