Merge "Refactor Stagefright::StartMPEG4Recording()" into gingerbread
diff --git a/api/current.xml b/api/current.xml
index 9145462..5d71cad 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -87294,6 +87294,21 @@
 <parameter name="quality" type="int">
 </parameter>
 </method>
+<method name="get"
+ return="android.media.CamcorderProfile"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cameraId" type="int">
+</parameter>
+<parameter name="quality" type="int">
+</parameter>
+</method>
 <field name="QUALITY_HIGH"
  type="int"
  transient="false"
@@ -87466,6 +87481,21 @@
 <parameter name="quality" type="int">
 </parameter>
 </method>
+<method name="getJpegEncodingQualityParameter"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cameraId" type="int">
+</parameter>
+<parameter name="quality" type="int">
+</parameter>
+</method>
 <field name="QUALITY_HIGH"
  type="int"
  transient="false"
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 33696f4..9a97284 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -7,7 +7,7 @@
 	SineSource.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libstagefright libmedia libutils libbinder
+	libstagefright libmedia libutils libbinder libstagefright_foundation
 
 LOCAL_C_INCLUDES:= \
 	$(JNI_H_INCLUDE) \
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 877b908..b7a3f99 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -38,6 +38,9 @@
 #include <media/stagefright/OMXCodec.h>
 #include <media/mediametadataretriever.h>
 
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MPEG4Writer.h>
+
 using namespace android;
 
 static long gNumRepetitions;
@@ -45,6 +48,8 @@
 static long gReproduceBug;  // if not -1.
 static bool gPreferSoftwareCodec;
 static bool gPlaybackAudio;
+static bool gWriteMP4;
+static String8 gWriteMP4Filename;
 
 static int64_t getNowUs() {
     struct timeval tv;
@@ -258,6 +263,21 @@
     }
 }
 
+static void writeSourceToMP4(const sp<MediaSource> &source) {
+    sp<MPEG4Writer> writer =
+        new MPEG4Writer(gWriteMP4Filename.string());
+
+    CHECK_EQ(writer->addSource(source), OK);
+
+    sp<MetaData> params = new MetaData;
+    CHECK_EQ(writer->start(), OK);
+
+    while (!writer->reachedEOS()) {
+        usleep(100000);
+    }
+    writer->stop();
+}
+
 static void usage(const char *me) {
     fprintf(stderr, "usage: %s\n", me);
     fprintf(stderr, "       -h(elp)\n");
@@ -270,6 +290,7 @@
     fprintf(stderr, "       -t(humbnail) extract video thumbnail or album art\n");
     fprintf(stderr, "       -s(oftware) prefer software codec\n");
     fprintf(stderr, "       -o playback audio\n");
+    fprintf(stderr, "       -w(rite) filename (write to .mp4 file)\n");
 }
 
 int main(int argc, char **argv) {
@@ -284,9 +305,10 @@
     gReproduceBug = -1;
     gPreferSoftwareCodec = false;
     gPlaybackAudio = false;
+    gWriteMP4 = false;
 
     int res;
-    while ((res = getopt(argc, argv, "han:lm:b:ptso")) >= 0) {
+    while ((res = getopt(argc, argv, "han:lm:b:ptsow:")) >= 0) {
         switch (res) {
             case 'a':
             {
@@ -322,6 +344,13 @@
                 break;
             }
 
+            case 'w':
+            {
+                gWriteMP4 = true;
+                gWriteMP4Filename.setTo(optarg);
+                break;
+            }
+
             case 'p':
             {
                 dumpProfiles = true;
@@ -554,7 +583,11 @@
             mediaSource = extractor->getTrack(i);
         }
 
-        playSource(&client, mediaSource);
+        if (gWriteMP4) {
+            writeSourceToMP4(mediaSource);
+        } else {
+            playSource(&client, mediaSource);
+        }
     }
 
     client.disconnect();
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 5640a06..f695dbb 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -626,6 +626,15 @@
             throws IllegalArgumentException, SecurityException;
 
     /**
+     * Call with 'false' to cause future calls to {@link #setThreadPriority(int)} to
+     * throw an exception if passed a background-level thread priority.  This is only
+     * effective if the JNI layer is built with GUARD_THREAD_PRIORITY defined to 1.
+     *
+     * @hide
+     */
+    public static final native void setCanSelfBackground(boolean backgroundOk);
+
+    /**
      * Sets the scheduling group for a thread.
      * @hide
      * @param tid The indentifier of the thread/process to change.
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 2369d25..127ed68 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -126,6 +126,11 @@
 
     public static final String POWER_CPU_SPEEDS = "cpu.speeds";
 
+    /**
+     * Battery capacity in milliAmpHour (mAh).
+     */
+    public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
+
     static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>();
 
     private static final String TAG_DEVICE = "device";
@@ -243,6 +248,19 @@
         }
     }
 
+    /**
+     * Returns the battery capacity, if available, in milli Amp Hours. If not available,
+     * it returns zero.
+     * @return the battery capacity in mAh
+     */
+    public double getBatteryCapacity() {
+        return getAveragePower(POWER_BATTERY_CAPACITY);
+    }
+
+    /**
+     * Returns the number of speeds that the CPU can be run at.
+     * @return
+     */
     public int getNumSpeedSteps() {
         Object value = sPowerMap.get(POWER_CPU_SPEEDS);
         if (value != null && value instanceof Double[]) {
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 68be741..7c99271 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -52,9 +52,15 @@
 #endif
 
 #define POLICY_DEBUG 0
+#define GUARD_THREAD_PRIORITY 0
 
 using namespace android;
 
+#if GUARD_THREAD_PRIORITY
+Mutex gKeyCreateMutex;
+static pthread_key_t gBgKey = -1;
+#endif
+
 static void signalExceptionForPriorityError(JNIEnv* env, jobject obj, int err)
 {
     switch (err) {
@@ -264,9 +270,41 @@
     closedir(d);
 }
 
+static void android_os_Process_setCanSelfBackground(JNIEnv* env, jobject clazz, jboolean bgOk) {
+    // Establishes the calling thread as illegal to put into the background.
+    // Typically used only for the system process's main looper.
+#if GUARD_THREAD_PRIORITY
+    LOGV("Process.setCanSelfBackground(%d) : tid=%d", bgOk, androidGetTid());
+    {
+        Mutex::Autolock _l(gKeyCreateMutex);
+        if (gBgKey == -1) {
+            pthread_key_create(&gBgKey, NULL);
+        }
+    }
+
+    // inverted:  not-okay, we set a sentinel value
+    pthread_setspecific(gBgKey, (void*)(bgOk ? 0 : 0xbaad));
+#endif
+}
+
 void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
                                               jint pid, jint pri)
 {
+#if GUARD_THREAD_PRIORITY
+    // if we're putting the current thread into the background, check the TLS
+    // to make sure this thread isn't guarded.  If it is, raise an exception.
+    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
+        if (pid == androidGetTid()) {
+            void* bgOk = pthread_getspecific(gBgKey);
+            if (bgOk == ((void*)0xbaad)) {
+                LOGE("Thread marked fg-only put self in background!");
+                jniThrowException(env, "java/lang/SecurityException", "May not put this thread into background");
+                return;
+            }
+        }
+    }
+#endif
+
     int rc = androidSetThreadPriority(pid, pri);
     if (rc != 0) {
         if (rc == INVALID_OPERATION) {
@@ -852,6 +890,7 @@
     {"getUidForName",       "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName},
     {"getGidForName",       "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName},
     {"setThreadPriority",   "(II)V", (void*)android_os_Process_setThreadPriority},
+    {"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground},
     {"setThreadPriority",   "(I)V", (void*)android_os_Process_setCallingThreadPriority},
     {"getThreadPriority",   "(I)I", (void*)android_os_Process_getThreadPriority},
     {"setThreadGroup",      "(II)V", (void*)android_os_Process_setThreadGroup},
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index ce623e8..30312b3 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -18,6 +18,7 @@
 -->
 
 <device name="Android">
+  <!-- All values are in mAh except as noted -->
   <item name="none">0</item>
   <item name="screen.on">0.1</item>
   <item name="bluetooth.active">0.1</item>
@@ -48,4 +49,6 @@
   <array name="cpu.active">
       <value>0.2</value>
   </array>
+  <!-- This is the battery capacity in mAh -->
+  <item name="battery.capacity">1000</item>
 </device>
diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h
index a4eea2a..c3cd361 100644
--- a/include/media/MediaProfiles.h
+++ b/include/media/MediaProfiles.h
@@ -48,8 +48,8 @@
     static MediaProfiles* getInstance();
 
     /**
-     * Returns the value for the given param name at the given quality level,
-     * or -1 if error.
+     * Returns the value for the given param name for the given camera at
+     * the given quality level, or -1 if error.
      *
      * Supported param name are:
      * duration - the recording duration.
@@ -64,7 +64,8 @@
      * aud.hz - audio sample rate
      * aud.ch - number of audio channels
      */
-    int getCamcorderProfileParamByName(const char *name, camcorder_quality quality) const;
+    int getCamcorderProfileParamByName(const char *name, int cameraId,
+                                       camcorder_quality quality) const;
 
     /**
      * Returns the output file formats supported.
@@ -124,12 +125,7 @@
     /**
      * Returns the number of image encoding quality levels supported.
      */
-    Vector<int> getImageEncodingQualityLevels() const;
-
-    /**
-     * Returns the maximum amount of memory in bytes we can use for decoding a JPEG file.
-     */
-    int getImageDecodingMaxMemory() const;
+    Vector<int> getImageEncodingQualityLevels(int cameraId) const;
 
 private:
     MediaProfiles& operator=(const MediaProfiles&);  // Don't call me
@@ -171,7 +167,8 @@
 
     struct CamcorderProfile {
         CamcorderProfile()
-            : mFileFormat(OUTPUT_FORMAT_THREE_GPP),
+            : mCameraId(0),
+              mFileFormat(OUTPUT_FORMAT_THREE_GPP),
               mQuality(CAMCORDER_QUALITY_HIGH),
               mDuration(0),
               mVideoCodec(0),
@@ -182,6 +179,7 @@
             delete mAudioCodec;
         }
 
+        int mCameraId;
         output_format mFileFormat;
         camcorder_quality mQuality;
         int mDuration;
@@ -249,6 +247,11 @@
         int tag;
     };
 
+    struct ImageEncodingQualityLevels {
+        int mCameraId;
+        Vector<int> mLevels;
+    };
+
     // Debug
     static void logVideoCodec(const VideoCodec& codec);
     static void logAudioCodec(const AudioCodec& codec);
@@ -267,9 +270,11 @@
     static VideoDecoderCap* createVideoDecoderCap(const char **atts);
     static VideoEncoderCap* createVideoEncoderCap(const char **atts);
     static AudioEncoderCap* createAudioEncoderCap(const char **atts);
-    static CamcorderProfile* createCamcorderProfile(const char **atts);
-    static int getImageEncodingQualityLevel(const char **atts);
-    static int getImageDecodingMaxMemory(const char **atts);
+    static CamcorderProfile* createCamcorderProfile(int cameraId, const char **atts);
+    static int getCameraId(const char **atts);
+
+    ImageEncodingQualityLevels* findImageEncodingQualityLevels(int cameraId) const;
+    void addImageEncodingQualityLevel(int cameraId, const char** atts);
 
     // Customized element tag handler for parsing the xml configuration file.
     static void startElementHandler(void *userData, const char *name, const char **atts);
@@ -303,6 +308,7 @@
     static bool sIsInitialized;
     static MediaProfiles *sInstance;
     static Mutex sLock;
+    int mCurrentCameraId;
 
     Vector<CamcorderProfile*> mCamcorderProfiles;
     Vector<AudioEncoderCap*>  mAudioEncoders;
@@ -310,8 +316,7 @@
     Vector<AudioDecoderCap*>  mAudioDecoders;
     Vector<VideoDecoderCap*>  mVideoDecoders;
     Vector<output_format>     mEncoderOutputFileFormats;
-    Vector<int>               mImageEncodingQualityLevels;
-    int                       mImageDecodingMaxMemory;
+    Vector<ImageEncodingQualityLevels *>  mImageEncodingQualityLevels;
 };
 
 }; // namespace android
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index e91d066..8d3a9df 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -27,7 +27,10 @@
 struct MetaData;
 
 struct MediaWriter : public RefBase {
-    MediaWriter() {}
+    MediaWriter()
+        : mMaxFileSizeLimitBytes(0),
+          mMaxFileDurationLimitUs(0) {
+    }
 
     virtual status_t addSource(const sp<MediaSource> &source) = 0;
     virtual bool reachedEOS() = 0;
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 50f0674..9212708 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import java.util.NoSuchElementException;
 import android.app.ActivityManagerNative;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -1016,7 +1017,11 @@
                 } else {
                     mStartcount--;
                     if (mStartcount == 0) {
-                        mCb.unlinkToDeath(this, 0);
+                        try {
+                            mCb.unlinkToDeath(this, 0);
+                        } catch (NoSuchElementException e) {
+                            Log.w(TAG, "decCount() going to 0 but not registered to binder");
+                        }
                     }
                     requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
                 }
@@ -1025,8 +1030,14 @@
 
         public void clearCount(boolean stopSco) {
             synchronized(mScoClients) {
+                if (mStartcount != 0) {
+                    try {
+                        mCb.unlinkToDeath(this, 0);
+                    } catch (NoSuchElementException e) {
+                        Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
+                    }
+                }
                 mStartcount = 0;
-                mCb.unlinkToDeath(this, 0);
                 if (stopSco) {
                     requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
                 }
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index 64d6460..a27df57 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -119,15 +119,26 @@
     public int audioChannels;
 
     /**
-     * Returns the camcorder profile for the given quality level.
+     * Returns the camcorder profile for the default camera at the given
+     * quality level.
      * @param quality the target quality level for the camcorder profile
      */
     public static CamcorderProfile get(int quality) {
+        return get(0, quality);
+    }
+
+    /**
+     * Returns the camcorder profile for the given camera at the given
+     * quality level.
+     * @param cameraId the id for the camera
+     * @param quality the target quality level for the camcorder profile
+     */
+    public static CamcorderProfile get(int cameraId, int quality) {
         if (quality < QUALITY_LOW || quality > QUALITY_HIGH) {
             String errMessage = "Unsupported quality level: " + quality;
             throw new IllegalArgumentException(errMessage);
         }
-        return native_get_camcorder_profile(quality);
+        return native_get_camcorder_profile(cameraId, quality);
     }
 
     static {
@@ -165,5 +176,6 @@
 
     // Methods implemented by JNI
     private static native final void native_init();
-    private static native final CamcorderProfile native_get_camcorder_profile(int quality);
+    private static native final CamcorderProfile native_get_camcorder_profile(
+            int cameraId, int quality);
 }
diff --git a/media/java/android/media/CameraProfile.java b/media/java/android/media/CameraProfile.java
index f8d3935..6a0be08 100644
--- a/media/java/android/media/CameraProfile.java
+++ b/media/java/android/media/CameraProfile.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import java.util.Arrays;
+import java.util.HashMap;
 
 /**
  * The CameraProfile class is used to retrieve the pre-defined still image
@@ -40,36 +41,55 @@
     /*
      * Cache the Jpeg encoding quality parameters
      */
-    private static final int[] sJpegEncodingQualityParameters;
+    private static final HashMap<Integer, int[]> sCache = new HashMap<Integer, int[]>();
 
     /**
      * Returns a pre-defined still image capture (jpeg) quality level
-     * used for the given quality level in the Camera application.
+     * used for the given quality level in the Camera application for
+     * the default camera.
      *
      * @param quality The target quality level
      */
     public static int getJpegEncodingQualityParameter(int quality) {
+        return getJpegEncodingQualityParameter(0, quality);
+    }
+
+    /**
+     * Returns a pre-defined still image capture (jpeg) quality level
+     * used for the given quality level in the Camera application for
+     * the specified camera.
+     *
+     * @param cameraId The id of the camera
+     * @param quality The target quality level
+     */
+    public static int getJpegEncodingQualityParameter(int cameraId, int quality) {
         if (quality < QUALITY_LOW || quality > QUALITY_HIGH) {
             throw new IllegalArgumentException("Unsupported quality level: " + quality);
         }
-        return sJpegEncodingQualityParameters[quality];
+        synchronized (sCache) {
+            int[] levels = sCache.get(cameraId);
+            if (levels == null) {
+                levels = getImageEncodingQualityLevels(cameraId);
+                sCache.put(cameraId, levels);
+            }
+            return levels[quality];
+        }
     }
 
     static {
         System.loadLibrary("media_jni");
         native_init();
-        sJpegEncodingQualityParameters = getImageEncodingQualityLevels();
     }
 
-    private static int[] getImageEncodingQualityLevels() {
-        int nLevels = native_get_num_image_encoding_quality_levels();
+    private static int[] getImageEncodingQualityLevels(int cameraId) {
+        int nLevels = native_get_num_image_encoding_quality_levels(cameraId);
         if (nLevels != QUALITY_HIGH + 1) {
             throw new RuntimeException("Unexpected Jpeg encoding quality levels " + nLevels);
         }
 
         int[] levels = new int[nLevels];
         for (int i = 0; i < nLevels; ++i) {
-            levels[i] = native_get_image_encoding_quality_level(i);
+            levels[i] = native_get_image_encoding_quality_level(cameraId, i);
         }
         Arrays.sort(levels);  // Lower quality level ALWAYS comes before higher one
         return levels;
@@ -77,6 +97,6 @@
 
     // Methods implemented by JNI
     private static native final void native_init();
-    private static native final int native_get_num_image_encoding_quality_levels();
-    private static native final int native_get_image_encoding_quality_level(int index);
+    private static native final int native_get_num_image_encoding_quality_levels(int cameraId);
+    private static native final int native_get_image_encoding_quality_level(int cameraId, int index);
 }
diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp
index 7d7533a..cce9fd0 100644
--- a/media/jni/android_media_MediaProfiles.cpp
+++ b/media/jni/android_media_MediaProfiles.cpp
@@ -162,26 +162,26 @@
 }
 
 static jobject
-android_media_MediaProfiles_native_get_camcorder_profile(JNIEnv *env, jobject thiz, jint quality)
+android_media_MediaProfiles_native_get_camcorder_profile(JNIEnv *env, jobject thiz, jint id, jint quality)
 {
-    LOGV("native_get_camcorder_profile: %d", quality);
+    LOGV("native_get_camcorder_profile: %d %d", id, quality);
     if (quality != CAMCORDER_QUALITY_HIGH && quality != CAMCORDER_QUALITY_LOW) {
         jniThrowException(env, "java/lang/RuntimeException", "Unknown camcorder profile quality");
         return NULL;
     }
 
     camcorder_quality q = static_cast<camcorder_quality>(quality);
-    int duration         = sProfiles->getCamcorderProfileParamByName("duration", q);
-    int fileFormat       = sProfiles->getCamcorderProfileParamByName("file.format", q);
-    int videoCodec       = sProfiles->getCamcorderProfileParamByName("vid.codec",   q);
-    int videoBitRate     = sProfiles->getCamcorderProfileParamByName("vid.bps",     q);
-    int videoFrameRate   = sProfiles->getCamcorderProfileParamByName("vid.fps",     q);
-    int videoFrameWidth  = sProfiles->getCamcorderProfileParamByName("vid.width",   q);
-    int videoFrameHeight = sProfiles->getCamcorderProfileParamByName("vid.height",  q);
-    int audioCodec       = sProfiles->getCamcorderProfileParamByName("aud.codec",   q);
-    int audioBitRate     = sProfiles->getCamcorderProfileParamByName("aud.bps",     q);
-    int audioSampleRate  = sProfiles->getCamcorderProfileParamByName("aud.hz",      q);
-    int audioChannels    = sProfiles->getCamcorderProfileParamByName("aud.ch",      q);
+    int duration         = sProfiles->getCamcorderProfileParamByName("duration",    id, q);
+    int fileFormat       = sProfiles->getCamcorderProfileParamByName("file.format", id, q);
+    int videoCodec       = sProfiles->getCamcorderProfileParamByName("vid.codec",   id, q);
+    int videoBitRate     = sProfiles->getCamcorderProfileParamByName("vid.bps",     id, q);
+    int videoFrameRate   = sProfiles->getCamcorderProfileParamByName("vid.fps",     id, q);
+    int videoFrameWidth  = sProfiles->getCamcorderProfileParamByName("vid.width",   id, q);
+    int videoFrameHeight = sProfiles->getCamcorderProfileParamByName("vid.height",  id, q);
+    int audioCodec       = sProfiles->getCamcorderProfileParamByName("aud.codec",   id, q);
+    int audioBitRate     = sProfiles->getCamcorderProfileParamByName("aud.bps",     id, q);
+    int audioSampleRate  = sProfiles->getCamcorderProfileParamByName("aud.hz",      id, q);
+    int audioChannels    = sProfiles->getCamcorderProfileParamByName("aud.ch",      id, q);
 
     // Check on the values retrieved
     if (duration == -1 || fileFormat == -1 || videoCodec == -1 || audioCodec == -1 ||
@@ -253,17 +253,17 @@
 }
 
 static jint
-android_media_MediaProfiles_native_get_num_image_encoding_quality_levels(JNIEnv *env, jobject thiz)
+android_media_MediaProfiles_native_get_num_image_encoding_quality_levels(JNIEnv *env, jobject thiz, jint cameraId)
 {
     LOGV("native_get_num_image_encoding_quality_levels");
-    return sProfiles->getImageEncodingQualityLevels().size();
+    return sProfiles->getImageEncodingQualityLevels(cameraId).size();
 }
 
 static jint
-android_media_MediaProfiles_native_get_image_encoding_quality_level(JNIEnv *env, jobject thiz, jint index)
+android_media_MediaProfiles_native_get_image_encoding_quality_level(JNIEnv *env, jobject thiz, jint cameraId, jint index)
 {
     LOGV("native_get_image_encoding_quality_level");
-    Vector<int> levels = sProfiles->getImageEncodingQualityLevels();
+    Vector<int> levels = sProfiles->getImageEncodingQualityLevels(cameraId);
     if (index < 0 || index >= levels.size()) {
         jniThrowException(env, "java/lang/IllegalArgumentException", "out of array boundary");
         return -1;
@@ -287,7 +287,7 @@
 
 static JNINativeMethod gMethodsForCamcorderProfileClass[] = {
     {"native_init",                            "()V",                    (void *)android_media_MediaProfiles_native_init},
-    {"native_get_camcorder_profile",           "(I)Landroid/media/CamcorderProfile;",
+    {"native_get_camcorder_profile",           "(II)Landroid/media/CamcorderProfile;",
                                                                          (void *)android_media_MediaProfiles_native_get_camcorder_profile},
 };
 
@@ -302,8 +302,8 @@
 static JNINativeMethod gMethodsForCameraProfileClass[] = {
     {"native_init",                            "()V",                    (void *)android_media_MediaProfiles_native_init},
     {"native_get_num_image_encoding_quality_levels",
-                                               "()I",                    (void *)android_media_MediaProfiles_native_get_num_image_encoding_quality_levels},
-    {"native_get_image_encoding_quality_level","(I)I",                   (void *)android_media_MediaProfiles_native_get_image_encoding_quality_level},
+                                               "(I)I",                   (void *)android_media_MediaProfiles_native_get_num_image_encoding_quality_levels},
+    {"native_get_image_encoding_quality_level","(II)I",                   (void *)android_media_MediaProfiles_native_get_image_encoding_quality_level},
 };
 
 static const char* const kEncoderCapabilitiesClassPathName = "android/media/EncoderCapabilities";
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 1263373..3869389 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -272,7 +272,7 @@
 }
 
 /*static*/ MediaProfiles::CamcorderProfile*
-MediaProfiles::createCamcorderProfile(const char **atts)
+MediaProfiles::createCamcorderProfile(int cameraId, const char **atts)
 {
     CHECK(!strcmp("quality",    atts[0]) &&
           !strcmp("fileFormat", atts[2]) &&
@@ -287,16 +287,47 @@
     CHECK(fileFormat != -1);
 
     MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
+    profile->mCameraId = cameraId;
     profile->mFileFormat = static_cast<output_format>(fileFormat);
     profile->mQuality = static_cast<camcorder_quality>(quality);
     profile->mDuration = atoi(atts[5]);
     return profile;
 }
 
-/*static*/ int
-MediaProfiles::getImageEncodingQualityLevel(const char** atts)
+MediaProfiles::ImageEncodingQualityLevels*
+MediaProfiles::findImageEncodingQualityLevels(int cameraId) const
+{
+    int n = mImageEncodingQualityLevels.size();
+    for (int i = 0; i < n; i++) {
+        ImageEncodingQualityLevels *levels = mImageEncodingQualityLevels[i];
+        if (levels->mCameraId == cameraId) {
+            return levels;
+        }
+    }
+    return NULL;
+}
+
+void MediaProfiles::addImageEncodingQualityLevel(int cameraId, const char** atts)
 {
     CHECK(!strcmp("quality", atts[0]));
+    int quality = atoi(atts[1]);
+    LOGV("%s: cameraId=%d, quality=%d\n", __func__, cameraId, quality);
+    ImageEncodingQualityLevels *levels = findImageEncodingQualityLevels(cameraId);
+
+    if (levels == NULL) {
+        levels = new ImageEncodingQualityLevels();
+        levels->mCameraId = cameraId;
+        mImageEncodingQualityLevels.add(levels);
+    }
+
+    levels->mLevels.add(quality);
+}
+
+/*static*/ int
+MediaProfiles::getCameraId(const char** atts)
+{
+    if (!atts[0]) return 0;  // default cameraId = 0
+    CHECK(!strcmp("cameraId", atts[0]));
     return atoi(atts[1]);
 }
 
@@ -322,10 +353,13 @@
         profiles->mAudioDecoders.add(createAudioDecoderCap(atts));
     } else if (strcmp("EncoderOutputFileFormat", name) == 0) {
         profiles->mEncoderOutputFileFormats.add(createEncoderOutputFileFormat(atts));
+    } else if (strcmp("CamcorderProfiles", name) == 0) {
+        profiles->mCurrentCameraId = getCameraId(atts);
     } else if (strcmp("EncoderProfile", name) == 0) {
-        profiles->mCamcorderProfiles.add(createCamcorderProfile(atts));
+        profiles->mCamcorderProfiles.add(
+            createCamcorderProfile(profiles->mCurrentCameraId, atts));
     } else if (strcmp("ImageEncoding", name) == 0) {
-        profiles->mImageEncodingQualityLevels.add(getImageEncodingQualityLevel(atts));
+        profiles->addImageEncodingQualityLevel(profiles->mCurrentCameraId, atts);
     }
 }
 
@@ -383,7 +417,8 @@
         new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 360000, 352, 288, 20);
 
     AudioCodec *audioCodec = new AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
-    CamcorderProfile *profile = new CamcorderProfile;
+    CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
+    profile->mCameraId = 0;
     profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
     profile->mQuality = CAMCORDER_QUALITY_HIGH;
     profile->mDuration = 60;
@@ -402,6 +437,7 @@
         new MediaProfiles::AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
 
     MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
+    profile->mCameraId = 0;
     profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
     profile->mQuality = CAMCORDER_QUALITY_LOW;
     profile->mDuration = 30;
@@ -458,9 +494,12 @@
 /*static*/ void
 MediaProfiles::createDefaultImageEncodingQualityLevels(MediaProfiles *profiles)
 {
-    profiles->mImageEncodingQualityLevels.add(70);
-    profiles->mImageEncodingQualityLevels.add(80);
-    profiles->mImageEncodingQualityLevels.add(90);
+    ImageEncodingQualityLevels *levels = new ImageEncodingQualityLevels();
+    levels->mCameraId = 0;
+    levels->mLevels.add(70);
+    levels->mLevels.add(80);
+    levels->mLevels.add(90);
+    profiles->mImageEncodingQualityLevels.add(levels);
 }
 
 /*static*/ MediaProfiles*
@@ -629,19 +668,24 @@
     return decoders;  // copy out
 }
 
-int MediaProfiles::getCamcorderProfileParamByName(const char *name, camcorder_quality quality) const
+int MediaProfiles::getCamcorderProfileParamByName(const char *name,
+                                                  int cameraId,
+                                                  camcorder_quality quality) const
 {
-    LOGV("getCamcorderProfileParamByName: %s for quality %d", name, quality);
+    LOGV("getCamcorderProfileParamByName: %s for camera %d, quality %d",
+         name, cameraId, quality);
 
     int index = -1;
     for (size_t i = 0, n = mCamcorderProfiles.size(); i < n; ++i) {
-        if (mCamcorderProfiles[i]->mQuality == quality) {
+        if (mCamcorderProfiles[i]->mCameraId == cameraId &&
+            mCamcorderProfiles[i]->mQuality == quality) {
             index = i;
             break;
         }
     }
     if (index == -1) {
-        LOGE("The given camcorder profile quality %d is not found", quality);
+        LOGE("The given camcorder profile camera %d quality %d is not found",
+             cameraId, quality);
         return -1;
     }
 
@@ -657,13 +701,18 @@
     if (!strcmp("aud.ch", name)) return mCamcorderProfiles[index]->mAudioCodec->mChannels;
     if (!strcmp("aud.hz", name)) return mCamcorderProfiles[index]->mAudioCodec->mSampleRate;
 
-    LOGE("The given camcorder profile param name %s is not found", name);
+    LOGE("The given camcorder profile param id %d name %s is not found", cameraId, name);
     return -1;
 }
 
-Vector<int> MediaProfiles::getImageEncodingQualityLevels() const
+Vector<int> MediaProfiles::getImageEncodingQualityLevels(int cameraId) const
 {
-    return mImageEncodingQualityLevels;  // copy out
+    Vector<int> result;
+    ImageEncodingQualityLevels *levels = findImageEncodingQualityLevels(cameraId);
+    if (levels != NULL) {
+        result = levels->mLevels;  // copy out
+    }
+    return result;
 }
 
 MediaProfiles::~MediaProfiles()
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index a52c888..6a4a131 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -34,6 +34,8 @@
 #include <media/mediarecorder.h>
 #include <cutils/properties.h>
 
+#include "include/ESDS.h"
+
 namespace android {
 
 class MPEG4Writer::Track {
@@ -126,6 +128,8 @@
             int32_t *min, int32_t *avg, int32_t *max);
     void findMinMaxChunkDurations(int64_t *min, int64_t *max);
 
+    void getCodecSpecificDataFromInputFormatIfPossible();
+
     Track(const Track &);
     Track &operator=(const Track &);
 };
@@ -678,6 +682,38 @@
       mCodecSpecificDataSize(0),
       mGotAllCodecSpecificData(false),
       mReachedEOS(false) {
+    getCodecSpecificDataFromInputFormatIfPossible();
+}
+
+void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() {
+    const char *mime;
+    CHECK(mMeta->findCString(kKeyMIMEType, &mime));
+
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        uint32_t type;
+        const void *data;
+        size_t size;
+        if (mMeta->findData(kKeyAVCC, &type, &data, &size)) {
+            mCodecSpecificData = malloc(size);
+            mCodecSpecificDataSize = size;
+            memcpy(mCodecSpecificData, data, size);
+            mGotAllCodecSpecificData = true;
+        }
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
+            || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        uint32_t type;
+        const void *data;
+        size_t size;
+        if (mMeta->findData(kKeyESDS, &type, &data, &size)) {
+            ESDS esds(data, size);
+            if (esds.getCodecSpecificInfo(&data, &size) == OK) {
+                mCodecSpecificData = malloc(size);
+                mCodecSpecificDataSize = size;
+                memcpy(mCodecSpecificData, data, size);
+                mGotAllCodecSpecificData = true;
+            }
+        }
+    }
 }
 
 MPEG4Writer::Track::~Track() {
@@ -721,7 +757,10 @@
     }
 
     int64_t startTimeUs;
-    CHECK(params && params->findInt64(kKeyTime, &startTimeUs));
+    if (params == NULL || !params->findInt64(kKeyTime, &startTimeUs)) {
+        startTimeUs = 0;
+    }
+
     initTrackingProgressStatus(params);
 
     sp<MetaData> meta = new MetaData;
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a229677..75045d7 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -1,6 +1,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.systemui"
-        android:sharedUserId="android.uid.system">
+        android:sharedUserId="android.uid.system"
+        android:process="system"
+        >
 
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
index 4029ad1..4ddd45c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
@@ -374,7 +374,7 @@
     }
 
     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
-        Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+        if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
                 + " icon=" + icon);
         StatusBarIconView view = new StatusBarIconView(this, slot);
         view.set(icon);
@@ -383,14 +383,14 @@
 
     public void updateIcon(String slot, int index, int viewIndex,
             StatusBarIcon old, StatusBarIcon icon) {
-        Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+        if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
                 + " old=" + old + " icon=" + icon);
         StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
         view.set(icon);
     }
 
     public void removeIcon(String slot, int index, int viewIndex) {
-        Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
+        if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
         mStatusIcons.removeViewAt(viewIndex);
     }
 
@@ -490,7 +490,7 @@
                 && oldContentView.getPackage() != null
                 && oldContentView.getPackage().equals(contentView.getPackage())
                 && oldContentView.getLayoutId() == contentView.getLayoutId()) {
-            Slog.d(TAG, "reusing notification");
+            if (SPEW) Slog.d(TAG, "reusing notification");
             oldEntry.notification = notification;
             try {
                 // Reapply the RemoteViews
@@ -517,7 +517,7 @@
                 addNotificationViews(key, notification);
             }
         } else {
-            Slog.d(TAG, "not reusing notification");
+            if (SPEW) Slog.d(TAG, "not reusing notification");
             removeNotificationViews(key);
             addNotificationViews(key, notification);
         }
@@ -531,7 +531,7 @@
     }
 
     public void removeNotification(IBinder key) {
-        Slog.d(TAG, "removeNotification key=" + key);
+        if (SPEW) Slog.d(TAG, "removeNotification key=" + key);
         StatusBarNotification old = removeNotificationViews(key);
 
         if (old != null) {
@@ -1447,7 +1447,7 @@
             // the user's perception of what's showing, call to say that the visibility
             // has changed. (Otherwise, someone else will call to do that).
             if (expandedPosition != EXPANDED_LEAVE_ALONE) {
-                Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
+                if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
                 visibilityChanged(visible);
             }
         }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7130636..c01680e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -80,7 +80,8 @@
                 android.os.Process.THREAD_PRIORITY_FOREGROUND);
 
         BinderInternal.disableBackgroundScheduling(true);
-        
+        android.os.Process.setCanSelfBackground(false);
+
         String factoryTestStr = SystemProperties.get("ro.factorytest");
         int factoryTest = "".equals(factoryTestStr) ? SystemServer.FACTORY_TEST_OFF
                 : Integer.parseInt(factoryTestStr);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index bf86b23..483f9eb 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -572,6 +572,7 @@
                     mHaveInputMethods);
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_DISPLAY);
+            android.os.Process.setCanSelfBackground(false);
 
             synchronized (this) {
                 mService = s;
@@ -607,6 +608,7 @@
             //        Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_FOREGROUND);
+            android.os.Process.setCanSelfBackground(false);
             mPolicy.init(mContext, mService, mPM);
 
             synchronized (this) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 46861ee..93122c4 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1259,6 +1259,7 @@
 
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_FOREGROUND);
+            android.os.Process.setCanSelfBackground(false);
 
             ActivityManagerService m = new ActivityManagerService();