Merge "Add LFH offset to verbose list of APK" into gingerbread
diff --git a/Android.mk b/Android.mk
index c1324c9..5f8b235 100644
--- a/Android.mk
+++ b/Android.mk
@@ -348,7 +348,7 @@
 			framework \
 
 framework_docs_LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-framework_docs_LOCAL_DROIDDOC_HTML_DIR := docs/html
+framework_docs_LOCAL_DROIDDOC_HTML_DIR := $(LOCAL_PATH)/docs/html $(OUT_DOCS)/gen
 # The since flag (-since N.xml API_LEVEL) is used to add API Level information
 # to the reference documentation. Must be in order of oldest to newest.
 framework_docs_LOCAL_DROIDDOC_OPTIONS := \
@@ -539,8 +539,8 @@
 
 include $(BUILD_DROIDDOC)
 
-# explicitly specify that online-sdk depends on framework-res.
-$(full_target): framework-res-package-target
+# explicitly specify that online-sdk depends on framework-res and any generated docs
+$(full_target): framework-res-package-target $(ALL_GENERATED_DOCS)
 
 # ==== docs that have all of the stuff that's @hidden =======================
 include $(CLEAR_VARS)
diff --git a/api/current.xml b/api/current.xml
index 38756d3..4b0068d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -45115,6 +45115,17 @@
 <parameter name="key" type="java.lang.String">
 </parameter>
 </method>
+<method name="startCommit"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 </interface>
 <interface name="SharedPreferences.OnSharedPreferenceChangeListener"
  abstract="true"
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 2870c50..7b35e7f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2840,6 +2840,11 @@
                 }
             }
 
+            public void startCommit() {
+                // TODO: implement
+                commit();
+            }
+
             public boolean commit() {
                 boolean returnValue;
 
@@ -2914,7 +2919,7 @@
         public Editor edit() {
             return new EditorImpl();
         }
-        
+
         private FileOutputStream createFileOutputStream(File file) {
             FileOutputStream str = null;
             try {
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index a15e29e..f1b1490 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -151,14 +151,47 @@
          * {@link SharedPreferences} object it is editing.  This atomically
          * performs the requested modifications, replacing whatever is currently
          * in the SharedPreferences.
-         * 
+         *
          * <p>Note that when two editors are modifying preferences at the same
          * time, the last one to call commit wins.
-         * 
+         *
+         * <p>If you don't care about the return value and you're
+         * using this from your application's main thread, consider
+         * using {@link #startCommit} instead.
+         *
          * @return Returns true if the new values were successfully written
          * to persistent storage.
          */
         boolean commit();
+
+        /**
+         * Commit your preferences changes back from this Editor to the
+         * {@link SharedPreferences} object it is editing.  This atomically
+         * performs the requested modifications, replacing whatever is currently
+         * in the SharedPreferences.
+         *
+         * <p>Note that when two editors are modifying preferences at the same
+         * time, the last one to call commit wins.
+         *
+         * <p>Unlike {@link #commit}, which writes its preferences out
+         * to persistent storage synchronously, {@link #startCommit}
+         * commits its changes to the in-memory
+         * {@link SharedPreferences} immediately but starts an
+         * asynchronous commit to disk and you won't be notified of
+         * any failures.  If another editor on this
+         * {@link SharedPreferences} does a regular {@link #commit}
+         * while a {@link #startCommit} is still outstanding, the
+         * {@link #commit} will block until all async commits are
+         * completed as well as the commit itself.
+         *
+         * <p>If you call this from an {@link android.app.Activity},
+         * the base class will wait for any async commits to finish in
+         * its {@link android.app.Activity#onPause}.</p>
+         *
+         * @return Returns true if the new values were successfully written
+         * to persistent storage.
+         */
+        void startCommit();
     }
 
     /**
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 97921fe..0068724 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -34,11 +34,19 @@
 import java.util.List;
 
 /**
+ * <p>
  * SensorManager lets you access the device's {@link android.hardware.Sensor
  * sensors}. Get an instance of this class by calling
  * {@link android.content.Context#getSystemService(java.lang.String)
  * Context.getSystemService()} with the argument
  * {@link android.content.Context#SENSOR_SERVICE}.
+ * </p>
+ * <p>
+ * Always make sure to disable sensors you don't need, especially when your
+ * activity is paused. Failing to do so can drain the battery in just a few
+ * hours. Note that the system will <i>not</i> disable sensors automatically when
+ * the screen turns off.
+ * </p>
  *
  * <pre class="prettyprint">
  * public class SensorActivity extends Activity, implements SensorEventListener {
@@ -48,13 +56,22 @@
  *     public SensorActivity() {
  *         mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
  *         mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ *     }
+ *
+ *     protected void onResume() {
+ *         super.onResume();
  *         mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
  *     }
  *
+ *     protected void onPause() {
+ *         super.onPause();
+ *         mSensorManager.unregisterListener(this);
+ *     }
+ *
  *     public void onAccuracyChanged(Sensor sensor, int accuracy) {
  *     }
  *
- *     public abstract void onSensorChanged(SensorEvent event) {
+ *     public void onSensorChanged(SensorEvent event) {
  *     }
  * }
  * </pre>
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 40ed980..d20e89d 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -249,6 +249,8 @@
         private static final int MICRO_KIND = 3;
         private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA};
         static final int DEFAULT_GROUP_ID = 0;
+        private static final Object sThumbBufLock = new Object();
+        private static byte[] sThumbBuf;
 
         private static Bitmap getMiniThumbFromFile(Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) {
             Bitmap bitmap = null;
@@ -321,11 +323,15 @@
             long magic = thumbFile.getMagic(origId);
             if (magic != 0) {
                 if (kind == MICRO_KIND) {
-                    byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
-                    if (thumbFile.getMiniThumbFromFile(origId, data) != null) {
-                        bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
-                        if (bitmap == null) {
-                            Log.w(TAG, "couldn't decode byte array.");
+                    synchronized (sThumbBufLock) {
+                        if (sThumbBuf == null) {
+                            sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
+                        }
+                        if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
+                            bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
+                            if (bitmap == null) {
+                                Log.w(TAG, "couldn't decode byte array.");
+                            }
                         }
                     }
                     return bitmap;
@@ -357,11 +363,15 @@
 
                 // Assuming thumbnail has been generated, at least original image exists.
                 if (kind == MICRO_KIND) {
-                    byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
-                    if (thumbFile.getMiniThumbFromFile(origId, data) != null) {
-                        bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
-                        if (bitmap == null) {
-                            Log.w(TAG, "couldn't decode byte array.");
+                    synchronized (sThumbBufLock) {
+                        if (sThumbBuf == null) {
+                            sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
+                        }
+                        if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
+                            bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
+                            if (bitmap == null) {
+                                Log.w(TAG, "couldn't decode byte array.");
+                            }
                         }
                     }
                 } else if (kind == MINI_KIND) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 3e0187d..f8e60ce 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3338,7 +3338,8 @@
         setUpSelect();
         if (mNativeClass != 0 && nativeWordSelection(x, y)) {
             nativeSetExtendSelection();
-            getWebChromeClient().onSelectionStart(this);
+            WebChromeClient client = getWebChromeClient();
+            if (client != null) client.onSelectionStart(this);
             return true;
         }
         notifySelectDialogDismissed();
@@ -4126,7 +4127,8 @@
      */
     public void selectionDone() {
         if (mSelectingText) {
-            getWebChromeClient().onSelectionDone(this);
+            WebChromeClient client = getWebChromeClient();
+            if (client != null) client.onSelectionDone(this);
             invalidate(); // redraw without selection
             notifySelectDialogDismissed();
         }
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 407d2e7..62a4495 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -506,7 +506,7 @@
 static void readLocale(char* language, char* region)
 {
     char propLang[PROPERTY_VALUE_MAX], propRegn[PROPERTY_VALUE_MAX];
-    
+
     property_get("persist.sys.language", propLang, "");
     property_get("persist.sys.country", propRegn, "");
     if (*propLang == 0 && *propRegn == 0) {
@@ -710,6 +710,33 @@
         LOGW("dalvik.vm.gc.overwritefree should be 'true' or 'false'");
     }
 
+    /* enable heap verification before each gc */
+    property_get("dalvik.vm.gc.preverify", propBuf, "false");
+    if (strcmp(propBuf, "true") == 0) {
+        opt.optionString = "-Xgc:preverify";
+        mOptions.add(opt);
+    } else if (strcmp(propBuf, "false") != 0) {
+        LOGW("dalvik.vm.gc.preverify should be 'true' or 'false'");
+    }
+
+    /* enable heap verification after each gc */
+    property_get("dalvik.vm.gc.postverify", propBuf, "false");
+    if (strcmp(propBuf, "true") == 0) {
+        opt.optionString = "-Xgc:postverify";
+        mOptions.add(opt);
+    } else if (strcmp(propBuf, "false") != 0) {
+        LOGW("dalvik.vm.gc.postverify should be 'true' or 'false'");
+    }
+
+    /* enable card table verification for partial gc */
+    property_get("dalvik.vm.gc.verifycardtable", propBuf, "false");
+    if (strcmp(propBuf, "true") == 0) {
+        opt.optionString = "-Xgc:verifycardtable";
+        mOptions.add(opt);
+    } else if (strcmp(propBuf, "false") != 0) {
+        LOGW("dalvik.vm.gc.verifycardtable should be 'true' or 'false'");
+    }
+
     /* enable debugging; set suspend=y to pause during VM init */
 #ifdef HAVE_ANDROID_OS
     /* use android ADB transport */
@@ -757,16 +784,6 @@
     }
 
 #if defined(WITH_JIT)
-    /* Minimal profile threshold to trigger JIT compilation */
-    char jitThresholdBuf[sizeof("-Xjitthreshold:") + PROPERTY_VALUE_MAX];
-    property_get("dalvik.vm.jit.threshold", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        strcpy(jitThresholdBuf, "-Xjitthreshold:");
-        strcat(jitThresholdBuf, propBuf);
-        opt.optionString = jitThresholdBuf;
-        mOptions.add(opt);
-    }
-
     /* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */
     char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX];
     property_get("dalvik.vm.jit.op", propBuf, "");
@@ -777,16 +794,6 @@
         mOptions.add(opt);
     }
 
-    /*
-     * Reverse the polarity of dalvik.vm.jit.op and force interpreter-only
-     * for non-selected opcodes.
-     */
-    property_get("dalvik.vm.jit.includeop", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        opt.optionString = "-Xincludeselectedop";
-        mOptions.add(opt);
-    }
-
     /* Force interpreter-only mode for selected methods */
     char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];
     property_get("dalvik.vm.jit.method", propBuf, "");
@@ -796,37 +803,6 @@
         opt.optionString = jitMethodBuf;
         mOptions.add(opt);
     }
-
-    /*
-     * Reverse the polarity of dalvik.vm.jit.method and force interpreter-only
-     * for non-selected methods.
-     */
-    property_get("dalvik.vm.jit.includemethod", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        opt.optionString = "-Xincludeselectedmethod";
-        mOptions.add(opt);
-    }
-
-    /*
-     * Enable profile collection on JIT'ed code.
-     */
-    property_get("dalvik.vm.jit.profile", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        opt.optionString = "-Xjitprofile";
-        mOptions.add(opt);
-    }
-
-    /*
-     * Disable optimizations by setting the corresponding bit to 1.
-     */
-    char jitOptBuf[sizeof("-Xjitdisableopt:") + PROPERTY_VALUE_MAX];
-    property_get("dalvik.vm.jit.disableopt", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        strcpy(jitOptBuf, "-Xjitdisableopt:");
-        strcat(jitOptBuf, propBuf);
-        opt.optionString = jitOptBuf;
-        mOptions.add(opt);
-    }
 #endif
 
     if (executionMode == kEMIntPortable) {
diff --git a/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java
index 5968e83..951d0d8 100644
--- a/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java
+++ b/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java
@@ -24,6 +24,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
+import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.test.mock.MockCursor;
 
@@ -44,6 +45,10 @@
     public ContactEntry addInputContactEntry() {
         return mProvider.buildInputEntry();
     }
+
+    public ExportTestProvider getProvider() {
+        return mProvider;
+    }
 }
 
 /* package */ class MockEntityIterator implements EntityIterator {
diff --git a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
index c3f6f79..1563da9 100644
--- a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
+++ b/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
@@ -19,8 +19,6 @@
 import android.content.ContentProviderResult;
 import android.content.ContentValues;
 import android.net.Uri;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Event;
 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
@@ -34,6 +32,9 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.text.TextUtils;
 
@@ -45,10 +46,10 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
-import java.util.Map.Entry;
 
 /* package */ class ImportTestResolver extends MockContentResolver {
     final ImportTestProvider mProvider;
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java
index 2de0464..2962a926 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java
@@ -438,14 +438,26 @@
                 .put(Phone.TYPE, Phone.TYPE_CUSTOM)
                 .put(Phone.LABEL, "invalid");
         PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
-        elem.addExpectedNode("TEL", "1", new TypeSet("MODEM"))
-                .addExpectedNode("TEL", "2", new TypeSet("MSG"))
-                .addExpectedNode("TEL", "3", new TypeSet("BBS"))
-                .addExpectedNode("TEL", "4", new TypeSet("VIDEO"))
-                .addExpectedNode("TEL", "5", new TypeSet("VOICE"))
-                .addExpectedNode("TEL", "6", new TypeSet("CELL"))
-                .addExpectedNode("TEL", "7", new TypeSet("CELL"))
-                .addExpectedNode("TEL", "8", new TypeSet("X-invalid"));
+        if (VCardConfig.isV30(vcardType)) {
+            // vCard 3.0 accepts "invalid". Also stop using toUpper()
+            elem.addExpectedNode("TEL", "1", new TypeSet("Modem"))
+                    .addExpectedNode("TEL", "2", new TypeSet("MSG"))
+                    .addExpectedNode("TEL", "3", new TypeSet("BBS"))
+                    .addExpectedNode("TEL", "4", new TypeSet("VIDEO"))
+                    .addExpectedNode("TEL", "5", new TypeSet("VOICE"))
+                    .addExpectedNode("TEL", "6", new TypeSet("CELL"))
+                    .addExpectedNode("TEL", "7", new TypeSet("CELL"))
+                    .addExpectedNode("TEL", "8", new TypeSet("invalid"));
+        } else {
+            elem.addExpectedNode("TEL", "1", new TypeSet("MODEM"))
+                    .addExpectedNode("TEL", "2", new TypeSet("MSG"))
+                    .addExpectedNode("TEL", "3", new TypeSet("BBS"))
+                    .addExpectedNode("TEL", "4", new TypeSet("VIDEO"))
+                    .addExpectedNode("TEL", "5", new TypeSet("VOICE"))
+                    .addExpectedNode("TEL", "6", new TypeSet("CELL"))
+                    .addExpectedNode("TEL", "7", new TypeSet("CELL"))
+                    .addExpectedNode("TEL", "8", new TypeSet("X-invalid"));
+        }
     }
 
     public void testPhoneTypeHandlingV21() {
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java b/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java
index 416e872..9c6003f 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java
@@ -16,95 +16,8 @@
 
 package android.pim.vcard;
 
-import android.content.ContentProvider;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
 import android.content.ContentValues;
-import android.content.EntityIterator;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.database.CursorWindow;
-import android.database.IBulkCursor;
-import android.database.IContentObserver;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.pim.vcard.VCardConfig;
 import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * Almost a dead copy of android.test.mock.MockContentProvider, but different in that this
- * class extends ContentProvider, not implementing IContentProvider,
- * so that MockContentResolver is able to accept this class :(
- */
-class MockContentProvider extends ContentProvider {
-    @Override
-    public boolean onCreate() {
-        return true;
-    }
-
-    @Override
-    public int bulkInsert(Uri url, ContentValues[] initialValues) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("unused")
-    public IBulkCursor bulkQuery(Uri url, String[] projection, String selection,
-            String[] selectionArgs, String sortOrder, IContentObserver observer,
-            CursorWindow window) throws RemoteException {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    @SuppressWarnings("unused")
-    public int delete(Uri url, String selection, String[] selectionArgs) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public String getType(Uri url) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public Uri insert(Uri url, ContentValues initialValues) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public ParcelFileDescriptor openFile(Uri url, String mode) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public AssetFileDescriptor openAssetFile(Uri uri, String mode) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    public IBinder asBinder() {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-}
 
 /**
  * BaseClass for vCard unit tests with utility classes.
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java b/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java
index bfc3158..3cb5b9b 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java
@@ -31,6 +31,7 @@
 import android.pim.vcard.exception.VCardException;
 import android.test.AndroidTestCase;
 import android.test.mock.MockContext;
+import android.util.Log;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -51,6 +52,8 @@
 }
 
 /* package */ class VCardVerifier {
+    private static final String LOG_TAG = "VCardVerifier";
+
     private class VCardVerifierInternal implements VCardComposer.OneEntryHandler {
         public boolean onInit(Context context) {
             return true;
@@ -267,10 +270,13 @@
             final ContentResolver resolver,
             final Uri uri, final String selection,
             final String[] selectionArgs, final String sortOrder) {
-        final ContentProvider provider =
-            resolver.acquireContentProviderClient(uri).getLocalContentProvider();
-        return ((ExportTestProvider)provider).queryEntities(
-                uri, selection, selectionArgs, sortOrder);
+        if (ExportTestResolver.class.equals(resolver.getClass())) {
+            return ((ExportTestResolver)resolver).getProvider().queryEntities(
+                    uri, selection, selectionArgs, sortOrder);
+        }
+
+        Log.e(LOG_TAG, "Unexpected provider given.");
+        return null;
     }
 
     private Method getMockGetEntityIteratorMethod()
@@ -285,18 +291,19 @@
         composer.addHandler(mLineVerifier);
         composer.addHandler(mVCardVerifierInternal);
         if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
-            mTestCase.fail("init() failed. Reason: " + composer.getErrorReason());
+            AndroidTestCase.fail("init() failed. Reason: " + composer.getErrorReason());
         }
-        mTestCase.assertFalse(composer.isAfterLast());
+        AndroidTestCase.assertFalse(composer.isAfterLast());
         try {
             while (!composer.isAfterLast()) {
                 try {
                     final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod();
-                    mTestCase.assertTrue(
-                            composer.createOneEntry(getMockGetEntityIteratorMethod()));
+                    AndroidTestCase.assertNotNull(mockGetEntityIteratorMethod);
+                    AndroidTestCase.assertTrue(
+                            composer.createOneEntry(mockGetEntityIteratorMethod));
                 } catch (Exception e) {
                     e.printStackTrace();
-                    mTestCase.fail();
+                    AndroidTestCase.fail();
                 }
             }
         } finally {
diff --git a/include/media/EffectPresetReverbApi.h b/include/media/EffectPresetReverbApi.h
index 53205bb..a3f094c 100644
--- a/include/media/EffectPresetReverbApi.h
+++ b/include/media/EffectPresetReverbApi.h
@@ -43,7 +43,8 @@
     REVERB_PRESET_LARGEROOM,
     REVERB_PRESET_MEDIUMHALL,
     REVERB_PRESET_LARGEHALL,
-    REVERB_PRESET_PLATE
+    REVERB_PRESET_PLATE,
+    REVERB_PRESET_LAST = REVERB_PRESET_PLATE
 } t_reverb_presets;
 
 #if __cplusplus
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 6f7dc38..9d2cff6 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -28,6 +28,7 @@
 
 namespace android {
 
+struct AMessage;
 class String8;
 
 class DataSource : public RefBase {
@@ -59,10 +60,14 @@
 
     ////////////////////////////////////////////////////////////////////////////
 
-    bool sniff(String8 *mimeType, float *confidence);
+    bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta);
 
+    // The sniffer can optionally fill in "meta" with an AMessage containing
+    // a dictionary of values that helps the corresponding extractor initialize
+    // its state without duplicating effort already exerted by the sniffer.
     typedef bool (*SnifferFunc)(
-            const sp<DataSource> &source, String8 *mimeType, float *confidence);
+            const sp<DataSource> &source, String8 *mimeType,
+            float *confidence, sp<AMessage> *meta);
 
     static void RegisterSniffer(SnifferFunc func);
     static void RegisterDefaultSniffers();
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index 1eb178e..d016dfa 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -54,11 +54,6 @@
 
 // ----------------------------------------------------------------------------
 
-// 4 * (11 + 7 + (1 + 2*NUM_RECT_MAX) * NUM_BUFFER_MAX) * NUM_LAYERS_MAX
-// 4 * (11 + 7 + (1 + 2*7)*16) * 31
-// 1032 * 31
-// = ~27 KiB (31992)
-
 class SharedBufferStack
 {
     friend class SharedClient;
@@ -85,7 +80,7 @@
     };
 
     struct FlatRegion { // 52 bytes = 4 * (1 + 2*N)
-        static const unsigned int NUM_RECT_MAX = 6;
+        static const unsigned int NUM_RECT_MAX = 5;
         uint32_t    count;
         SmallRect   rects[NUM_RECT_MAX];
     };
@@ -93,13 +88,18 @@
     struct BufferData {
         FlatRegion dirtyRegion;
         SmallRect  crop;
+        uint8_t transform;
+        uint8_t reserved[3];
     };
     
     SharedBufferStack();
     void init(int32_t identity);
     status_t setDirtyRegion(int buffer, const Region& reg);
     status_t setCrop(int buffer, const Rect& reg);
+    status_t setTransform(int buffer, uint8_t transform);
     Region getDirtyRegion(int buffer) const;
+    Rect getCrop(int buffer) const;
+    uint32_t getTransform(int buffer) const;
 
     // these attributes are part of the conditions/updates
     volatile int32_t head;      // server's current front buffer
@@ -117,7 +117,7 @@
     int32_t     reserved32[1];
     Statistics  stats;
     int32_t     reserved;
-    BufferData  buffers[NUM_BUFFER_MAX];     // 960 bytes
+    BufferData  buffers[NUM_BUFFER_MAX];     // 1024 bytes
 };
 
 // ----------------------------------------------------------------------------
@@ -206,7 +206,7 @@
     bool needNewBuffer(int buffer) const;
     status_t setDirtyRegion(int buffer, const Region& reg);
     status_t setCrop(int buffer, const Rect& reg);
-    
+    status_t setTransform(int buffer, uint32_t transform);
 
     class SetBufferCountCallback {
         friend class SharedBufferClient;
@@ -275,6 +275,8 @@
     status_t reallocateAllExcept(int buffer);
     int32_t getQueuedCount() const;
     Region getDirtyRegion(int buffer) const;
+    Rect getCrop(int buffer) const;
+    uint32_t getTransform(int buffer) const;
 
     status_t resize(int newNumBuffers);
 
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 294c867..7c5a39b 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -216,6 +216,7 @@
     int  dispatch_crop(va_list args);
     int  dispatch_set_buffer_count(va_list args);
     int  dispatch_set_buffers_geometry(va_list args);
+    int  dispatch_set_buffers_transform(va_list args);
     
     void setUsage(uint32_t reqUsage);
     int  connect(int api);
@@ -223,6 +224,7 @@
     int  crop(Rect const* rect);
     int  setBufferCount(int bufferCount);
     int  setBuffersGeometry(int w, int h, int format);
+    int  setBuffersTransform(int transform);
 
     /*
      *  private stuff...
@@ -278,6 +280,7 @@
     Rect                        mSwapRectangle;
     int                         mConnected;
     Rect                        mNextBufferCrop;
+    uint32_t                    mNextBufferTransform;
     BufferInfo                  mBufferInfo;
     
     // protected by mSurfaceLock. These are also used from lock/unlock
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index ca89b06..d59d72b 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -85,6 +85,7 @@
     NATIVE_WINDOW_SET_CROP,
     NATIVE_WINDOW_SET_BUFFER_COUNT,
     NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
+    NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
 };
 
 /* parameter for NATIVE_WINDOW_[DIS]CONNECT */
@@ -92,6 +93,20 @@
     NATIVE_WINDOW_API_EGL = 1
 };
 
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */
+enum {
+    /* flip source image horizontally */
+    NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
+    /* flip source image vertically */
+    NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
+    /* rotate source image 90 degrees clock-wise */
+    NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
+    /* rotate source image 180 degrees */
+    NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
+    /* rotate source image 270 degrees clock-wise */
+    NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
+};
+
 struct ANativeWindow 
 {
 #ifdef __cplusplus
@@ -196,6 +211,7 @@
      *     NATIVE_WINDOW_SET_CROP
      *     NATIVE_WINDOW_SET_BUFFER_COUNT
      *     NATIVE_WINDOW_SET_BUFFERS_GEOMETRY
+     *     NATIVE_WINDOW_SET_BUFFERS_TRANSFORM
      *  
      */
     
@@ -298,6 +314,19 @@
             w, h, format);
 }
 
+/*
+ * native_window_set_buffers_transform(..., int transform)
+ * All buffers queued after this call will be displayed transformed according
+ * to the transform parameter specified.
+ */
+static inline int native_window_set_buffers_transform(
+        ANativeWindow* window,
+        int transform)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
+            transform);
+}
+
 // ---------------------------------------------------------------------------
 
 /* FIXME: this is legacy for pixmaps */
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 156a7db..4ad9f86 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -75,6 +75,14 @@
     return NO_ERROR;
 }
 
+status_t SharedBufferStack::setTransform(int buffer, uint8_t transform)
+{
+    if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+        return BAD_INDEX;
+    buffers[buffer].transform = transform;
+    return NO_ERROR;
+}
+
 status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty)
 {
     if (uint32_t(buffer) >= NUM_BUFFER_MAX)
@@ -137,6 +145,26 @@
     return res;
 }
 
+Rect SharedBufferStack::getCrop(int buffer) const
+{
+    Rect res(-1, -1);
+    if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+        return res;
+    res.left = buffers[buffer].crop.l;
+    res.top = buffers[buffer].crop.t;
+    res.right = buffers[buffer].crop.r;
+    res.bottom = buffers[buffer].crop.b;
+    return res;
+}
+
+uint32_t SharedBufferStack::getTransform(int buffer) const
+{
+    if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+        return 0;
+    return buffers[buffer].transform;
+}
+
+
 // ----------------------------------------------------------------------------
 
 SharedBufferBase::SharedBufferBase(SharedClient* sharedClient,
@@ -433,6 +461,12 @@
     return stack.setCrop(buf, crop);
 }
 
+status_t SharedBufferClient::setTransform(int buf, uint32_t transform)
+{
+    SharedBufferStack& stack( *mSharedStack );
+    return stack.setTransform(buf, uint8_t(transform));
+}
+
 status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg)
 {
     SharedBufferStack& stack( *mSharedStack );
@@ -549,6 +583,18 @@
     return stack.getDirtyRegion(buf);
 }
 
+Rect SharedBufferServer::getCrop(int buf) const
+{
+    SharedBufferStack& stack( *mSharedStack );
+    return stack.getCrop(buf);
+}
+
+uint32_t SharedBufferServer::getTransform(int buf) const
+{
+    SharedBufferStack& stack( *mSharedStack );
+    return stack.getTransform(buf);
+}
+
 /*
  * NOTE: this is not thread-safe on the server-side, meaning
  * 'head' cannot move during this operation. The client-side
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index 5ab72cd..cb76091 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -422,8 +422,10 @@
     const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
     const_cast<uint32_t&>(ANativeWindow::flags) = 0;
 
+    mNextBufferTransform = 0;
     mConnected = 0;
     mSwapRectangle.makeInvalid();
+    mNextBufferCrop = Rect(0,0);
     // two buffers by default
     mBuffers.setCapacity(2);
     mBuffers.insertAt(0, 2);
@@ -631,6 +633,7 @@
     }
     
     int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+    mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform);
     mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop);
     mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion);
     err = mSharedBufferClient->queue(bufIdx);
@@ -685,6 +688,9 @@
     case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
         res = dispatch_set_buffers_geometry( args );
         break;
+    case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
+        res = dispatch_set_buffers_transform( args );
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -719,6 +725,11 @@
     return setBuffersGeometry(w, h, f);
 }
 
+int Surface::dispatch_set_buffers_transform(va_list args) {
+    int transform = va_arg(args, int);
+    return setBuffersTransform(transform);
+}
+
 void Surface::setUsage(uint32_t reqUsage)
 {
     Mutex::Autolock _l(mSurfaceLock);
@@ -765,6 +776,10 @@
 
 int Surface::crop(Rect const* rect)
 {
+    // empty/invalid rects are not allowed
+    if (rect->isEmpty())
+        return BAD_VALUE;
+
     Mutex::Autolock _l(mSurfaceLock);
     // TODO: validate rect size
     mNextBufferCrop = *rect;
@@ -804,6 +819,13 @@
     return NO_ERROR;
 }
 
+int Surface::setBuffersTransform(int transform)
+{
+    Mutex::Autolock _l(mSurfaceLock);
+    mNextBufferTransform = transform;
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 int Surface::getConnectedApi() const
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 798271e..d856eb4 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -17,7 +17,7 @@
 
 #define LOG_TAG "Bundle"
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 
 #include <cutils/log.h>
 #include <assert.h>
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 2043e44..03f1409 100755
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -17,7 +17,7 @@
 
 #define LOG_TAG "Reverb"
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 
 #include <cutils/log.h>
 #include <assert.h>
@@ -61,42 +61,81 @@
 /* Preset definitions                                                               */
 /*                                                                                  */
 /************************************************************************************/
-LVM_UINT16 RevPreset_Level[]    = {  32,   32,   32,   32,   32,    32,   32,   32,   32,   32};
-LVM_UINT16 RevPreset_LPF[]      = {1298, 1000, 5012, 3542, 3400, 23999, 2536, 1000, 1000, 1000};
-LVM_UINT16 RevPreset_HPF[]      = {  50,   50,   50,   50,   50,    50,   50,   50,   50,   50};
-LVM_UINT16 RevPreset_T60[]      = {1490,  500, 2310, 4230, 3920,  2910, 7000, 1490, 1490,  170};
-LVM_UINT16 RevPreset_Density[]  = { 100,  100,  100,  100,  100,   100,  100,  100,  100,  100};
-LVM_UINT16 RevPreset_Damping[]  = {  54,   10,   64,   59,   70,   100,   33,   54,   21,   10};
-LVM_UINT16 RevPreset_RoomSize[] = { 100,  100,  100,  100,  100,   100,  100,  100,  100,  100};
 
-/************************************************************************************/
-/*                                                                                  */
-/* Preset definitions                                                               */
-/*                                                                                  */
-/************************************************************************************/
-#define REV_PRESET_BATHROOM         0
-#define REV_PRESET_LIVINGROOM       1
-#define REV_PRESET_STONEROOM        2
-#define REV_PRESET_AUDITORIUM       3
-#define REV_PRESET_CONCERTHALL      4
-#define REV_PRESET_CAVE             5
-#define REV_PRESET_ARENA            6
-#define REV_PRESET_FOREST           7
-#define REV_PRESET_MOUNTAINS        8
-#define REV_PRESET_PADDEDCELL       9
+const static t_reverb_settings sReverbPresets[] = {
+        // REVERB_PRESET_NONE: values are unused
+        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+        // REVERB_PRESET_SMALLROOM
+        {-1000, -600, 1100, 830, -400, 5, 500, 10, 1000, 1000},
+        // REVERB_PRESET_MEDIUMROOM
+        {-1000, -600, 1300, 830, -1000, 20, -200, 20, 1000, 1000},
+        // REVERB_PRESET_LARGEROOM
+        {-1000, -600, 1500, 830, -1600, 5, -1000, 40, 1000, 1000},
+        // REVERB_PRESET_MEDIUMHALL
+        {-1000, -600, 1800, 700, -1300, 15, -800, 30, 1000, 1000},
+        // REVERB_PRESET_LARGEHALL
+        {-1000, -600, 1800, 700, -2000, 30, -1400, 60, 1000, 1000},
+        // REVERB_PRESET_PLATE
+        {-1000, -200, 1300, 900, 0, 2, 0, 10, 1000, 750},
+};
 
-// NXP SW Reverb UUID
-const effect_descriptor_t gReverbDescriptor = {
+
+// NXP SW auxiliary environmental reverb
+const effect_descriptor_t gAuxEnvReverbDescriptor = {
         { 0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, { 0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e } },
         { 0x4a387fc0, 0x8ab3, 0x11df, 0x8bad, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } },
         EFFECT_API_VERSION,
-        (EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_INSERT_LAST),
+        EFFECT_FLAG_TYPE_AUXILIARY,
         0, // TODO
         1,
-        "Reverb",
+        "Auxiliary Environmental Reverb",
         "NXP Software Ltd.",
 };
 
+// NXP SW insert environmental reverb
+static const effect_descriptor_t gInsertEnvReverbDescriptor = {
+        {0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}},
+        {0xc7a511a0, 0xa3bb, 0x11df, 0x860e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        EFFECT_API_VERSION,
+        EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+        0, // TODO
+        1,
+        "Insert Environmental Reverb",
+        "NXP Software Ltd.",
+};
+
+// NXP SW auxiliary preset reverb
+static const effect_descriptor_t gAuxPresetReverbDescriptor = {
+        {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        {0xf29a1400, 0xa3bb, 0x11df, 0x8ddc, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        EFFECT_API_VERSION,
+        EFFECT_FLAG_TYPE_AUXILIARY,
+        0, // TODO
+        1,
+        "Auxiliary Preset Reverb",
+        "NXP Software Ltd.",
+};
+
+// NXP SW insert preset reverb
+static const effect_descriptor_t gInsertPresetReverbDescriptor = {
+        {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        {0x172cdf00, 0xa3bc, 0x11df, 0xa72f, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        EFFECT_API_VERSION,
+        EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+        0, // TODO
+        1,
+        "Insert Preset Reverb",
+        "NXP Software Ltd.",
+};
+
+// gDescriptors contains pointers to all defined effect descriptor in this library
+static const effect_descriptor_t * const gDescriptors[] = {
+        &gAuxEnvReverbDescriptor,
+        &gInsertEnvReverbDescriptor,
+        &gAuxPresetReverbDescriptor,
+        &gInsertPresetReverbDescriptor
+};
+
 struct ReverbContext{
     const struct effect_interface_s *itfe;
     effect_config_t                 config;
@@ -114,8 +153,14 @@
     FILE                            *PcmOutPtr;
     #endif
     LVM_Fs_en                       SampleRate;
+    bool                            auxiliary;
+    bool                            preset;
+    uint16_t                        curPreset;
+    uint16_t                        nextPreset;
 };
 
+#define REVERB_DEFAULT_PRESET REVERB_PRESET_MEDIUMROOM
+
 //--- local function prototypes
 int  Reverb_init            (ReverbContext *pContext);
 void Reverb_free            (ReverbContext *pContext);
@@ -125,11 +170,12 @@
                              void          *pParam,
                              size_t        *pValueSize,
                              void          *pValue);
+int Reverb_LoadPreset       (ReverbContext   *pContext);
 
 /* Effect Library Interface Implementation */
 extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects){
     LOGV("\n\tEffectQueryNumberEffects start");
-    *pNumEffects = 1;
+    *pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
     LOGV("\tEffectQueryNumberEffects creating %d effects", *pNumEffects);
     LOGV("\tEffectQueryNumberEffects end\n");
     return 0;
@@ -142,11 +188,11 @@
         LOGV("\tLVM_ERROR : EffectQueryEffect was passed NULL pointer");
         return -EINVAL;
     }
-    if (index > 0){
+    if (index >= sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)) {
         LOGV("\tLVM_ERROR : EffectQueryEffect index out of range %d", index);
         return -ENOENT;
     }
-    memcpy(pDescriptor, &gReverbDescriptor, sizeof(effect_descriptor_t));
+    memcpy(pDescriptor, gDescriptors[index], sizeof(effect_descriptor_t));
     LOGV("\tEffectQueryEffect end\n");
     return 0;
 }     /* end EffectQueryEffect */
@@ -157,6 +203,8 @@
                             effect_interface_t  *pInterface){
     int ret;
     int i;
+    int length = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
+    const effect_descriptor_t *desc;
 
     LOGV("\t\nEffectCreate start");
 
@@ -165,9 +213,16 @@
         return -EINVAL;
     }
 
-    if (memcmp(uuid, &gReverbDescriptor.uuid, sizeof(effect_uuid_t)) != 0){
-        LOGV("\tLVM_ERROR : EffectCreate() invalid UUID");
-        return -EINVAL;
+    for (i = 0; i < length; i++) {
+        desc = gDescriptors[i];
+        if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t))
+                == 0) {
+            break;
+        }
+    }
+
+    if (i == length) {
+        return -ENOENT;
     }
 
     ReverbContext *pContext = new ReverbContext;
@@ -175,6 +230,19 @@
     pContext->itfe      = &gReverbInterface;
     pContext->hInstance = NULL;
 
+    pContext->auxiliary = false;
+    if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY){
+        pContext->auxiliary = true;
+    }
+
+    pContext->preset = false;
+    if (memcmp(&desc->type, SL_IID_PRESETREVERB, sizeof(effect_uuid_t)) == 0) {
+        pContext->preset = true;
+        // force reloading preset at first call to process()
+        pContext->curPreset = REVERB_PRESET_LAST + 1;
+        pContext->nextPreset = REVERB_DEFAULT_PRESET;
+    }
+
     LOGV("\tEffectCreate - Calling Reverb_init");
     ret = Reverb_init(pContext);
 
@@ -288,6 +356,14 @@
 
    return;
 }
+
+static inline int16_t clamp16(int32_t sample)
+{
+    if ((sample>>15) ^ (sample>>31))
+        sample = 0x7FFF ^ (sample>>31);
+    return sample;
+}
+
 //----------------------------------------------------------------------------
 // process()
 //----------------------------------------------------------------------------
@@ -344,6 +420,9 @@
     fflush(pContext->PcmInPtr);
     #endif
 
+    if (pContext->preset && pContext->nextPreset != pContext->curPreset) {
+        Reverb_LoadPreset(pContext);
+    }
     // Convert to Input 32 bits
     for(int i=0; i<frameCount*samplesPerFrame; i++){
         InFrames32[i] = (LVM_INT32)pIn[i]<<8;
@@ -359,18 +438,28 @@
     //frameCount, pContext->config.inputCfg.channels, CHANNEL_MONO,
     //pContext->config.outputCfg.channels, CHANNEL_STEREO);
 
+    if (pContext->preset && pContext->curPreset == REVERB_PRESET_NONE) {
+        memset(OutFrames32, 0, frameCount * sizeof(LVM_INT32) * 2);
+    } else {
     /* Process the samples */
     LvmStatus = LVREV_Process(pContext->hInstance,      /* Instance handle */
                               InFrames32,               /* Input buffer */
                               OutFrames32,              /* Output buffer */
                               frameCount);              /* Number of samples to read */
+    }
+
+    if (!pContext->auxiliary) {
+        for (int i=0; i<frameCount*2; i++){
+            OutFrames32[i] += InFrames32[i];
+        }
+    }
 
     LVM_ERROR_CHECK(LvmStatus, "LVREV_Process", "process")
     if(LvmStatus != LVREV_SUCCESS) return -EINVAL;
 
     // Convert to 16 bits
     for(int i=0; i<frameCount*2; i++){  // Always stereo
-        OutFrames16[i] = (LVM_INT16)(OutFrames32[i]>>8);
+        OutFrames16[i] = clamp16(OutFrames32[i]>>8);
     }
 
     #ifdef LVM_PCM
@@ -382,7 +471,7 @@
     if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
         //LOGV("\tBuffer access is ACCUMULATE");
         for (int i=0; i<frameCount*2; i++){
-            pOut[i] +=  OutFrames16[i];
+            pOut[i] = clamp16((int32_t)pOut[i] + (int32_t)OutFrames16[i]);
         }
     }else{
         //LOGV("\tBuffer access is WRITE");
@@ -462,6 +551,8 @@
 
     CHECK_ARG(pConfig->inputCfg.samplingRate == pConfig->outputCfg.samplingRate);
     CHECK_ARG(pConfig->inputCfg.format == pConfig->outputCfg.format);
+    CHECK_ARG((pContext->auxiliary && pConfig->inputCfg.channels == CHANNEL_MONO) ||
+              ((!pContext->auxiliary) && pConfig->inputCfg.channels == CHANNEL_STEREO));
     CHECK_ARG(pConfig->outputCfg.channels == CHANNEL_STEREO);
     CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE
               || pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
@@ -540,18 +631,8 @@
 
 int Reverb_init(ReverbContext *pContext){
     int status;
-    int channel_mode;
 
-    LOGV("\tReverb_init start %d", gReverbDescriptor.flags);
-
-    if((gReverbDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT){
-        LOGV("\tReverb_init EFFECT_FLAG_TYPE_INSERT");
-        channel_mode = CHANNEL_STEREO;
-    }
-    if((gReverbDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY ){
-        LOGV("\tReverb_init EFFECT_FLAG_TYPE_AUXILIARY");
-        channel_mode = CHANNEL_MONO;
-    }
+    LOGV("\tReverb_init start");
 
     CHECK_ARG(pContext != NULL);
 
@@ -560,7 +641,12 @@
     }
 
     pContext->config.inputCfg.accessMode                    = EFFECT_BUFFER_ACCESS_READ;
-    pContext->config.inputCfg.channels                      = channel_mode;
+    if (pContext->auxiliary) {
+        pContext->config.inputCfg.channels                  = CHANNEL_MONO;
+    } else {
+        pContext->config.inputCfg.channels                  = CHANNEL_STEREO;
+    }
+
     pContext->config.inputCfg.format                        = SAMPLE_FORMAT_PCM_S15;
     pContext->config.inputCfg.samplingRate                  = 44100;
     pContext->config.inputCfg.bufferProvider.getBuffer      = NULL;
@@ -653,11 +739,11 @@
     /* Reverb parameters */
     params.Level          = 0;
     params.LPF            = 23999;
-    params.HPF            = RevPreset_HPF[REV_PRESET_MOUNTAINS];
-    params.T60            = RevPreset_T60[REV_PRESET_MOUNTAINS];
-    params.Density        = RevPreset_Density[REV_PRESET_MOUNTAINS];
-    params.Damping        = RevPreset_Damping[REV_PRESET_MOUNTAINS];
-    params.RoomSize       = RevPreset_RoomSize[REV_PRESET_MOUNTAINS];
+    params.HPF            = 50;
+    params.T60            = 1490;
+    params.Density        = 100;
+    params.Damping        = 21;
+    params.RoomSize       = 100;
 
     /* Saved strength is used to return the exact strength that was used in the set to the get
      * because we map the original strength range of 0:1000 to 1:15, and this will avoid
@@ -1294,6 +1380,44 @@
 }
 
 //----------------------------------------------------------------------------
+// Reverb_LoadPreset()
+//----------------------------------------------------------------------------
+// Purpose:
+// Load a the next preset
+//
+// Inputs:
+//  pContext         - handle to instance data
+//
+// Outputs:
+//
+// Side Effects:
+//
+//----------------------------------------------------------------------------
+int Reverb_LoadPreset(ReverbContext   *pContext)
+{
+    //TODO: add reflections delay, level and reverb delay when early reflections are
+    // implemented
+    pContext->curPreset = pContext->nextPreset;
+
+    if (pContext->curPreset != REVERB_PRESET_NONE) {
+        const t_reverb_settings *preset = &sReverbPresets[pContext->curPreset];
+        ReverbSetRoomLevel(pContext, preset->roomLevel);
+        ReverbSetRoomHfLevel(pContext, preset->roomHFLevel);
+        ReverbSetDecayTime(pContext, preset->decayTime);
+        ReverbSetDecayHfRatio(pContext, preset->decayHFRatio);
+        //reflectionsLevel
+        //reflectionsDelay
+        ReverbSetReverbLevel(pContext, preset->reverbLevel);
+        // reverbDelay
+        ReverbSetDiffusion(pContext, preset->diffusion);
+        ReverbSetDensity(pContext, preset->density);
+    }
+
+    return 0;
+}
+
+
+//----------------------------------------------------------------------------
 // Reverb_getParameter()
 //----------------------------------------------------------------------------
 // Purpose:
@@ -1325,6 +1449,15 @@
     t_reverb_settings *pProperties;
 
     //LOGV("\tReverb_getParameter start");
+    if (pContext->preset) {
+        if (param != REVERB_PARAM_PRESET || *pValueSize < sizeof(uint16_t)) {
+            return -EINVAL;
+        }
+
+        *(uint16_t *)pValue = pContext->nextPreset;
+        LOGV("get REVERB_PARAM_PRESET, preset %d", pContext->nextPreset);
+        return 0;
+    }
 
     switch (param){
         case REVERB_PARAM_ROOM_LEVEL:
@@ -1531,6 +1664,18 @@
     int32_t param = *pParamTemp++;
 
     //LOGV("\tReverb_setParameter start");
+    if (pContext->preset) {
+        if (param != REVERB_PARAM_PRESET) {
+            return -EINVAL;
+        }
+
+        uint16_t preset = *(uint16_t *)pValue;
+        LOGV("set REVERB_PARAM_PRESET, preset %d", preset);
+        if (preset > REVERB_PRESET_LAST) {
+            return -EINVAL;
+        }
+        pContext->nextPreset = preset;
+    }
 
     switch (param){
         case REVERB_PARAM_PROPERTIES:
diff --git a/media/libmedia/fixedfft.cpp b/media/libmedia/fixedfft.cpp
index 28eb05a..9cf05ba 100644
--- a/media/libmedia/fixedfft.cpp
+++ b/media/libmedia/fixedfft.cpp
@@ -26,7 +26,9 @@
 
 #include <stdio.h>
 #include <stdint.h>
+#ifdef __ARM_ARCH__
 #include <machine/cpu-features.h>
+#endif
 
 #define LOG_FFT_SIZE 10
 #define MAX_FFT_SIZE (1 << LOG_FFT_SIZE)
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 70af2da..1b05528 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -87,7 +87,7 @@
       mInitCheck(NO_INIT) {
     String8 mimeType;
     float confidence;
-    if (!SniffAMR(mDataSource, &mimeType, &confidence)) {
+    if (!SniffAMR(mDataSource, &mimeType, &confidence, NULL)) {
         return;
     }
 
@@ -276,7 +276,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SniffAMR(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     char header[9];
 
     if (source->readAt(0, header, sizeof(header)) != sizeof(header)) {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 11fdf56..a0cd5c3 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -458,27 +458,34 @@
         return;
     }
 
+    bool eos;
+    size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos);
+
     size_t lowWatermark = 400000;
     size_t highWatermark = 1000000;
 
-    off_t size;
-    if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) {
-        int64_t bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
+    if (eos) {
+        notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
+    } else {
+        off_t size;
+        if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) {
+            int64_t bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
 
-        size_t cachedSize = mCachedSource->cachedSize();
-        int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
+            size_t cachedSize = mCachedSource->cachedSize();
+            int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
 
-        double percentage = (double)cachedDurationUs / mDurationUs;
+            int percentage = 100.0 * (double)cachedDurationUs / mDurationUs;
+            if (percentage > 100) {
+                percentage = 100;
+            }
 
-        notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage * 100.0);
+            notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
 
-        lowWatermark = 2 * bitrate / 8;  // 2 secs
-        highWatermark = 10 * bitrate / 8;  // 10 secs
+            lowWatermark = 2 * bitrate / 8;  // 2 secs
+            highWatermark = 10 * bitrate / 8;  // 10 secs
+        }
     }
 
-    bool eos;
-    size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos);
-
     if ((mFlags & PLAYING) && !eos && (cachedDataRemaining < lowWatermark)) {
         LOGI("cache is running low (< %d) , pausing.", lowWatermark);
         mFlags |= CACHE_UNDERRUN;
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 90a596c..49eac62 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -25,6 +25,7 @@
 
 #include "matroska/MatroskaExtractor.h"
 
+#include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/FileSource.h>
 #include <media/stagefright/MediaErrors.h>
@@ -56,19 +57,23 @@
 Mutex DataSource::gSnifferMutex;
 List<DataSource::SnifferFunc> DataSource::gSniffers;
 
-bool DataSource::sniff(String8 *mimeType, float *confidence) {
+bool DataSource::sniff(
+        String8 *mimeType, float *confidence, sp<AMessage> *meta) {
     *mimeType = "";
     *confidence = 0.0f;
+    meta->clear();
 
     Mutex::Autolock autoLock(gSnifferMutex);
     for (List<SnifferFunc>::iterator it = gSniffers.begin();
          it != gSniffers.end(); ++it) {
         String8 newMimeType;
         float newConfidence;
-        if ((*it)(this, &newMimeType, &newConfidence)) {
+        sp<AMessage> newMeta;
+        if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) {
             if (newConfidence > *confidence) {
                 *mimeType = newMimeType;
                 *confidence = newConfidence;
+                *meta = newMeta;
             }
         }
     }
@@ -92,13 +97,13 @@
 
 // static
 void DataSource::RegisterDefaultSniffers() {
-    RegisterSniffer(SniffMP3);
     RegisterSniffer(SniffMPEG4);
-    RegisterSniffer(SniffAMR);
-    RegisterSniffer(SniffWAV);
-    RegisterSniffer(SniffOgg);
     RegisterSniffer(SniffMatroska);
+    RegisterSniffer(SniffOgg);
+    RegisterSniffer(SniffWAV);
+    RegisterSniffer(SniffAMR);
     RegisterSniffer(SniffMPEG2TS);
+    RegisterSniffer(SniffMP3);
 }
 
 // static
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 4058fbc..2e36968 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -22,6 +22,7 @@
 
 #include "include/ID3.h"
 
+#include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
@@ -456,15 +457,31 @@
     MP3Source &operator=(const MP3Source &);
 };
 
-MP3Extractor::MP3Extractor(const sp<DataSource> &source)
+MP3Extractor::MP3Extractor(
+        const sp<DataSource> &source, const sp<AMessage> &meta)
     : mDataSource(source),
       mFirstFramePos(-1),
       mFixedHeader(0),
       mByteNumber(0) {
     off_t pos = 0;
     uint32_t header;
-    bool success = Resync(mDataSource, 0, &pos, &header);
-    CHECK(success);
+    bool success;
+
+    int64_t meta_offset;
+    uint32_t meta_header;
+    if (meta != NULL
+            && meta->findInt64("offset", &meta_offset)
+            && meta->findInt32("header", (int32_t *)&meta_header)) {
+        // The sniffer has already done all the hard work for us, simply
+        // accept its judgement.
+        pos = (off_t)meta_offset;
+        header = meta_header;
+
+        success = true;
+    } else {
+        success = Resync(mDataSource, 0, &pos, &header);
+        CHECK(success);
+    }
 
     if (success) {
         mFirstFramePos = pos;
@@ -759,15 +776,20 @@
 }
 
 bool SniffMP3(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType,
+        float *confidence, sp<AMessage> *meta) {
     off_t pos = 0;
     uint32_t header;
     if (!Resync(source, 0, &pos, &header)) {
         return false;
     }
 
+    *meta = new AMessage;
+    (*meta)->setInt64("offset", pos);
+    (*meta)->setInt32("header", header);
+
     *mimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
-    *confidence = 0.3f;
+    *confidence = 0.2f;
 
     return true;
 }
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 12a1e6e..ba90407 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1738,7 +1738,7 @@
         || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8)
         || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)) {
         *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
-        *confidence = 0.1;
+        *confidence = 0.4;
 
         return true;
     }
@@ -1805,13 +1805,14 @@
     }
 
     *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
-    *confidence = 0.3f;
+    *confidence = 0.4f;
 
     return true;
 }
 
 bool SniffMPEG4(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     if (BetterSniffMPEG4(source, mimeType, confidence)) {
         return true;
     }
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 56e6136..9bc94de 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -27,6 +27,7 @@
 
 #include "matroska/MatroskaExtractor.h"
 
+#include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaExtractor.h>
@@ -46,10 +47,12 @@
 // static
 sp<MediaExtractor> MediaExtractor::Create(
         const sp<DataSource> &source, const char *mime) {
+    sp<AMessage> meta;
+
     String8 tmp;
     if (mime == NULL) {
         float confidence;
-        if (!source->sniff(&tmp, &confidence)) {
+        if (!source->sniff(&tmp, &confidence, &meta)) {
             LOGV("FAILED to autodetect media content.");
 
             return NULL;
@@ -64,7 +67,7 @@
             || !strcasecmp(mime, "audio/mp4")) {
         return new MPEG4Extractor(source);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
-        return new MP3Extractor(source);
+        return new MP3Extractor(source, meta);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)
             || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
         return new AMRExtractor(source);
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 9630092..2c1311a 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -804,7 +804,8 @@
 }
 
 bool SniffOgg(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     char tmp[4];
     if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) {
         return false;
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 8d820c0..57c1075 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -404,7 +404,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SniffWAV(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     char header[12];
     if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
         return false;
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index ca0c68c..da340f7 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -142,11 +142,22 @@
     mSize = size;
 
     if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) {
+        free(mData);
+        mData = NULL;
+
         return false;
     }
 
-    if (header.flags & 0x80) {
+    if (header.version_major == 4) {
+        if (!removeUnsynchronizationV2_4()) {
+            free(mData);
+            mData = NULL;
+
+            return false;
+        }
+    } else if (header.flags & 0x80) {
         LOGV("removing unsynchronization");
+
         removeUnsynchronization();
     }
 
@@ -243,6 +254,74 @@
     }
 }
 
+static void WriteSyncsafeInteger(uint8_t *dst, size_t x) {
+    for (size_t i = 0; i < 4; ++i) {
+        dst[3 - i] = (x & 0x7f);
+        x >>= 7;
+    }
+}
+
+bool ID3::removeUnsynchronizationV2_4() {
+    size_t oldSize = mSize;
+
+    size_t offset = 0;
+    while (offset + 10 <= mSize) {
+        if (!memcmp(&mData[offset], "\0\0\0\0", 4)) {
+            break;
+        }
+
+        size_t dataSize;
+        if (!ParseSyncsafeInteger(&mData[offset + 4], &dataSize)) {
+            return false;
+        }
+
+        if (offset + dataSize + 10 > mSize) {
+            return false;
+        }
+
+        uint16_t flags = U16_AT(&mData[offset + 8]);
+        uint16_t prevFlags = flags;
+
+        if (flags & 1) {
+            // Strip data length indicator
+
+            memmove(&mData[offset + 10], &mData[offset + 14], mSize - offset - 14);
+            mSize -= 4;
+            dataSize -= 4;
+
+            flags &= ~1;
+        }
+
+        if (flags & 2) {
+            // Unsynchronization added.
+
+            for (size_t i = 0; i + 1 < dataSize; ++i) {
+                if (mData[offset + 10 + i] == 0xff
+                        && mData[offset + 11 + i] == 0x00) {
+                    memmove(&mData[offset + 11 + i], &mData[offset + 12 + i],
+                            mSize - offset - 12 - i);
+                    --mSize;
+                    --dataSize;
+                }
+            }
+
+            flags &= ~2;
+        }
+
+        if (flags != prevFlags) {
+            WriteSyncsafeInteger(&mData[offset + 4], dataSize);
+            mData[offset + 8] = flags >> 8;
+            mData[offset + 9] = flags & 0xff;
+        }
+
+        offset += 10 + dataSize;
+    }
+
+    memset(&mData[mSize], 0, oldSize - mSize);
+
+    return true;
+}
+
 ID3::Iterator::Iterator(const ID3 &parent, const char *id)
     : mParent(parent),
       mID(NULL),
@@ -529,10 +608,11 @@
 
             uint16_t flags = U16_AT(&mParent.mData[mOffset + 8]);
 
-            if ((mParent.mVersion == ID3_V2_4 && (flags & 0x000e))
+            if ((mParent.mVersion == ID3_V2_4 && (flags & 0x000c))
                 || (mParent.mVersion == ID3_V2_3 && (flags & 0x00c0))) {
-                // Compression, Encryption or per-Frame unsynchronization
-                // are not supported at this time.
+                // Compression or encryption are not supported at this time.
+                // Per-frame unsynchronization and data-length indicator
+                // have already been taken care of.
 
                 LOGV("Skipping unsupported frame (compression, encryption "
                      "or per-frame unsynchronization flagged");
diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h
index db49fe4..1cdf36d 100644
--- a/media/libstagefright/include/AMRExtractor.h
+++ b/media/libstagefright/include/AMRExtractor.h
@@ -22,6 +22,7 @@
 
 namespace android {
 
+struct AMessage;
 class String8;
 
 class AMRExtractor : public MediaExtractor {
@@ -49,7 +50,8 @@
 };
 
 bool SniffAMR(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
index c6b1a8b..7ddbb41 100644
--- a/media/libstagefright/include/ID3.h
+++ b/media/libstagefright/include/ID3.h
@@ -80,6 +80,7 @@
     bool parseV1(const sp<DataSource> &source);
     bool parseV2(const sp<DataSource> &source);
     void removeUnsynchronization();
+    bool removeUnsynchronizationV2_4();
 
     static bool ParseSyncsafeInteger(const uint8_t encoded[4], size_t *x);
 
diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h
index 3ce6df3..0e6ccde 100644
--- a/media/libstagefright/include/MP3Extractor.h
+++ b/media/libstagefright/include/MP3Extractor.h
@@ -22,13 +22,14 @@
 
 namespace android {
 
+struct AMessage;
 class DataSource;
 class String8;
 
 class MP3Extractor : public MediaExtractor {
 public:
     // Extractor assumes ownership of "source".
-    MP3Extractor(const sp<DataSource> &source);
+    MP3Extractor(const sp<DataSource> &source, const sp<AMessage> &meta);
 
     virtual size_t countTracks();
     virtual sp<MediaSource> getTrack(size_t index);
@@ -52,7 +53,8 @@
 };
 
 bool SniffMP3(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *meta);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index c96973b..1bf4cd1 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -9,6 +9,7 @@
 
 namespace android {
 
+struct AMessage;
 struct AnotherPacketSource;
 struct ATSParser;
 struct DataSource;
@@ -47,7 +48,8 @@
 };
 
 bool SniffMPEG2TS(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index c8663d5..1c9cc7e 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -23,6 +23,7 @@
 
 namespace android {
 
+struct AMessage;
 class DataSource;
 class SampleTable;
 class String8;
@@ -75,7 +76,8 @@
 };
 
 bool SniffMPEG4(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/OggExtractor.h b/media/libstagefright/include/OggExtractor.h
index 7066669..1eda025 100644
--- a/media/libstagefright/include/OggExtractor.h
+++ b/media/libstagefright/include/OggExtractor.h
@@ -22,6 +22,7 @@
 
 namespace android {
 
+struct AMessage;
 class DataSource;
 class String8;
 
@@ -53,7 +54,8 @@
 };
 
 bool SniffOgg(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h
index 3e847b9..df6d3e7 100644
--- a/media/libstagefright/include/WAVExtractor.h
+++ b/media/libstagefright/include/WAVExtractor.h
@@ -22,6 +22,7 @@
 
 namespace android {
 
+struct AMessage;
 class DataSource;
 class String8;
 
@@ -58,7 +59,8 @@
 };
 
 bool SniffWAV(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 71f6587..7c7d69e 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -579,7 +579,8 @@
 }
 
 bool SniffMatroska(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     DataSourceReader reader(source);
     mkvparser::EBMLHeader ebmlHeader;
     long long pos;
diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h
index 7471848..fa20b84 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.h
+++ b/media/libstagefright/matroska/MatroskaExtractor.h
@@ -27,6 +27,7 @@
 
 namespace android {
 
+struct AMessage;
 class String8;
 
 struct DataSourceReader;
@@ -69,7 +70,8 @@
 };
 
 bool SniffMatroska(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index b287c95..56ca375 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -174,7 +174,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SniffMPEG2TS(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
 #if 0
     char header;
     if (source->readAt(0, &header, 1) != 1 || header != 0x47) {
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 185d72a9..8349fe6 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -47,7 +47,8 @@
     <bool name="def_networks_available_notification_on">true</bool>
     
     <bool name="def_backup_enabled">false</bool>
-    <string name="def_backup_transport" translatable="false"></string>
+    <string name="def_backup_transport" translatable="false">android/com.android.internal.backup.LocalTransport</string>
+
     <!-- Default value for whether or not to pulse the notification LED when there is a 
          pending notification -->
     <bool name="def_notification_pulse">true</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2e95932..81d82de 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -22,6 +22,8 @@
 import java.security.SecureRandom;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import android.app.backup.BackupManager;
 import android.content.ContentProvider;
@@ -37,6 +39,7 @@
 import android.media.RingtoneManager;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.FileObserver;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemProperties;
 import android.provider.DrmStore;
@@ -56,9 +59,15 @@
 
     // Cache for settings, access-ordered for acting as LRU.
     // Guarded by themselves.
-    private static final int MAX_CACHE_ENTRIES = 50;
-    private static final SettingsCache sSystemCache = new SettingsCache();
-    private static final SettingsCache sSecureCache = new SettingsCache();
+    private static final int MAX_CACHE_ENTRIES = 200;
+    private static final SettingsCache sSystemCache = new SettingsCache("system");
+    private static final SettingsCache sSecureCache = new SettingsCache("secure");
+
+    // The count of how many known (handled by SettingsProvider)
+    // database mutations are currently being handled.  Used by
+    // sFileObserver to not reload the database when it's ourselves
+    // modifying it.
+    private static final AtomicInteger sKnownMutationsInFlight = new AtomicInteger(0);
 
     // Over this size we don't reject loading or saving settings but
     // we do consider them broken/malicious and don't keep them in
@@ -67,6 +76,10 @@
 
     private static final Bundle NULL_SETTING = Bundle.forPair("value", null);
 
+    // Used as a sentinel value in an instance equality test when we
+    // want to cache the existence of a key, but not store its value.
+    private static final Bundle TOO_LARGE_TO_CACHE_MARKER = Bundle.forPair("_dummy", null);
+
     protected DatabaseHelper mOpenHelper;
     private BackupManager mBackupManager;
 
@@ -201,6 +214,43 @@
         }
     }
 
+    // FileObserver for external modifications to the database file.
+    // Note that this is for platform developers only with
+    // userdebug/eng builds who should be able to tinker with the
+    // sqlite database out from under the SettingsProvider, which is
+    // normally the exclusive owner of the database.  But we keep this
+    // enabled all the time to minimize development-vs-user
+    // differences in testing.
+    private static SettingsFileObserver sObserverInstance;
+    private class SettingsFileObserver extends FileObserver {
+        private final AtomicBoolean mIsDirty = new AtomicBoolean(false);
+        private final String mPath;
+
+        public SettingsFileObserver(String path) {
+            super(path, FileObserver.CLOSE_WRITE |
+                  FileObserver.CREATE | FileObserver.DELETE |
+                  FileObserver.MOVED_TO | FileObserver.MODIFY);
+            mPath = path;
+        }
+
+        public void onEvent(int event, String path) {
+            int modsInFlight = sKnownMutationsInFlight.get();
+            if (modsInFlight > 0) {
+                // our own modification.
+                return;
+            }
+            Log.d(TAG, "external modification to " + mPath + "; event=" + event);
+            if (!mIsDirty.compareAndSet(false, true)) {
+                // already handled. (we get a few update events
+                // during an sqlite write)
+                return;
+            }
+            Log.d(TAG, "updating our caches for " + mPath);
+            fullyPopulateCaches();
+            mIsDirty.set(false);
+        }
+    }
+
     @Override
     public boolean onCreate() {
         mOpenHelper = new DatabaseHelper(getContext());
@@ -210,9 +260,65 @@
             return false;
         }
 
+        // Watch for external modifications to the database file,
+        // keeping our cache in sync.
+        // It's kinda lame to call mOpenHelper.getReadableDatabase()
+        // during onCreate(), but since ensureAndroidIdIsSet has
+        // already done it above and initialized/upgraded the
+        // database, might as well just use it...
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        sObserverInstance = new SettingsFileObserver(db.getPath());
+        sObserverInstance.startWatching();
+        startAsyncCachePopulation();
         return true;
     }
 
+    private void startAsyncCachePopulation() {
+        new Thread("populate-settings-caches") {
+            public void run() {
+                fullyPopulateCaches();
+            }
+        }.start();
+    }
+
+    private void fullyPopulateCaches() {
+        fullyPopulateCache("secure", sSecureCache);
+        fullyPopulateCache("system", sSystemCache);
+    }
+
+    // Slurp all values (if sane in number & size) into cache.
+    private void fullyPopulateCache(String table, SettingsCache cache) {
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        Cursor c = db.query(
+            table,
+            new String[] { Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE },
+            null, null, null, null, null,
+            "" + (MAX_CACHE_ENTRIES + 1) /* limit */);
+        try {
+            synchronized (cache) {
+                cache.clear();
+                cache.setFullyMatchesDisk(true);  // optimistic
+                int rows = 0;
+                while (c.moveToNext()) {
+                    rows++;
+                    String name = c.getString(0);
+                    String value = c.getString(1);
+                    cache.populate(name, value);
+                }
+                if (rows > MAX_CACHE_ENTRIES) {
+                    // Somewhat redundant, as removeEldestEntry() will
+                    // have already done this, but to be explicit:
+                    cache.setFullyMatchesDisk(false);
+                    Log.d(TAG, "row count exceeds max cache entries for table " + table);
+                }
+                Log.d(TAG, "cache for settings table '" + table + "' fullycached=" +
+                      cache.fullyMatchesDisk());
+            }
+        } finally {
+            c.close();
+        }
+    }
+
     private boolean ensureAndroidIdIsSet() {
         final Cursor c = query(Settings.Secure.CONTENT_URI,
                 new String[] { Settings.NameValueTable.VALUE },
@@ -262,7 +368,19 @@
     private Bundle lookupValue(String table, SettingsCache cache, String key) {
         synchronized (cache) {
             if (cache.containsKey(key)) {
-                return cache.get(key);
+                Bundle value = cache.get(key);
+                if (value != TOO_LARGE_TO_CACHE_MARKER) {
+                    return value;
+                }
+                // else we fall through and read the value from disk
+            } else if (cache.fullyMatchesDisk()) {
+                // Fast path (very common).  Don't even try touch disk
+                // if we know we've slurped it all in.  Trying to
+                // touch the disk would mean waiting for yaffs2 to
+                // give us access, which could takes hundreds of
+                // milliseconds.  And we're very likely being called
+                // from somebody's UI thread...
+                return NULL_SETTING;
             }
         }
 
@@ -338,6 +456,7 @@
         checkWritePermissions(args);
         SettingsCache cache = SettingsCache.forTable(args.table);
 
+        sKnownMutationsInFlight.incrementAndGet();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         db.beginTransaction();
         try {
@@ -350,6 +469,7 @@
             db.setTransactionSuccessful();
         } finally {
             db.endTransaction();
+            sKnownMutationsInFlight.decrementAndGet();
         }
 
         sendNotify(uri);
@@ -449,8 +569,10 @@
             return Uri.withAppendedPath(url, name);
         }
 
+        sKnownMutationsInFlight.incrementAndGet();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         final long rowId = db.insert(args.table, null, initialValues);
+        sKnownMutationsInFlight.decrementAndGet();
         if (rowId <= 0) return null;
 
         SettingsCache.populate(cache, initialValues);  // before we notify
@@ -471,12 +593,15 @@
         }
         checkWritePermissions(args);
 
+        sKnownMutationsInFlight.incrementAndGet();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         int count = db.delete(args.table, args.where, args.args);
+        sKnownMutationsInFlight.decrementAndGet();
         if (count > 0) {
             SettingsCache.wipe(args.table);  // before we notify
             sendNotify(url);
         }
+        startAsyncCachePopulation();
         if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) deleted");
         return count;
     }
@@ -489,12 +614,15 @@
         }
         checkWritePermissions(args);
 
+        sKnownMutationsInFlight.incrementAndGet();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        sKnownMutationsInFlight.decrementAndGet();
         int count = db.update(args.table, initialValues, args.where, args.args);
         if (count > 0) {
             SettingsCache.wipe(args.table);  // before we notify
             sendNotify(url);
         }
+        startAsyncCachePopulation();
         if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) <- " + initialValues);
         return count;
     }
@@ -506,12 +634,12 @@
          * When a client attempts to openFile the default ringtone or
          * notification setting Uri, we will proxy the call to the current
          * default ringtone's Uri (if it is in the DRM or media provider).
-         */ 
+         */
         int ringtoneType = RingtoneManager.getDefaultType(uri);
         // Above call returns -1 if the Uri doesn't match a default type
         if (ringtoneType != -1) {
             Context context = getContext();
-            
+
             // Get the current value for the default sound
             Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
 
@@ -531,7 +659,7 @@
                             throw new FileNotFoundException(e.getMessage());
                         }
                     }
-                    
+
                     return context.getContentResolver().openFileDescriptor(soundUri, mode);
                 }
             }
@@ -607,13 +735,38 @@
      */
     private static final class SettingsCache extends LinkedHashMap<String, Bundle> {
 
-        public SettingsCache() {
+        private final String mCacheName;
+        private boolean mCacheFullyMatchesDisk = false;  // has the whole database slurped.
+
+        public SettingsCache(String name) {
             super(MAX_CACHE_ENTRIES, 0.75f /* load factor */, true /* access ordered */);
+            mCacheName = name;
+        }
+
+        /**
+         * Is the whole database table slurped into this cache?
+         */
+        public boolean fullyMatchesDisk() {
+            synchronized (this) {
+                return mCacheFullyMatchesDisk;
+            }
+        }
+
+        public void setFullyMatchesDisk(boolean value) {
+            synchronized (this) {
+                mCacheFullyMatchesDisk = value;
+            }
         }
 
         @Override
         protected boolean removeEldestEntry(Map.Entry eldest) {
-            return size() > MAX_CACHE_ENTRIES;
+            if (size() <= MAX_CACHE_ENTRIES) {
+                return false;
+            }
+            synchronized (this) {
+                mCacheFullyMatchesDisk = false;
+            }
+            return true;
         }
 
         /**
@@ -658,11 +811,15 @@
                 return;
             }
             String value = contentValues.getAsString(Settings.NameValueTable.VALUE);
-            synchronized (cache) {
+            cache.populate(name, value);
+        }
+
+        public void populate(String name, String value) {
+            synchronized (this) {
                 if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) {
-                    cache.put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value));
+                    put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value));
                 } else {
-                    cache.remove(name);
+                    put(name, TOO_LARGE_TO_CACHE_MARKER);
                 }
             }
         }
@@ -678,6 +835,7 @@
             }
             synchronized (cache) {
                 cache.clear();
+                cache.mCacheFullyMatchesDisk = true;
             }
         }
 
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b37cd89..c316074 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -609,8 +609,8 @@
      * All currently bound service connections.  Keys are the IBinder of
      * the client's IServiceConnection.
      */
-    final HashMap<IBinder, ConnectionRecord> mServiceConnections
-            = new HashMap<IBinder, ConnectionRecord>();
+    final HashMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections
+            = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
 
     /**
      * List of services that we have been asked to start,
@@ -7376,12 +7376,14 @@
             if (mServiceConnections.size() > 0) {
                 if (needSep) pw.println(" ");
                 pw.println("  Connection bindings to services:");
-                Iterator<ConnectionRecord> it
+                Iterator<ArrayList<ConnectionRecord>> it
                         = mServiceConnections.values().iterator();
                 while (it.hasNext()) {
-                    ConnectionRecord r = it.next();
-                    pw.print("  * "); pw.println(r);
-                    r.dump(pw, "    ");
+                    ArrayList<ConnectionRecord> r = it.next();
+                    for (int i=0; i<r.size(); i++) {
+                        pw.print("  * "); pw.println(r.get(i));
+                        r.get(i).dump(pw, "    ");
+                    }
                 }
                 needSep = true;
             }
@@ -7659,18 +7661,21 @@
                 while (it.hasNext()) {
                     ServiceRecord r = it.next();
                     if (r.connections.size() > 0) {
-                        Iterator<ConnectionRecord> jt
+                        Iterator<ArrayList<ConnectionRecord>> jt
                                 = r.connections.values().iterator();
                         while (jt.hasNext()) {
-                            ConnectionRecord c = jt.next();
-                            if (c.binding.client != app) {
-                                try {
-                                    //c.conn.connected(r.className, null);
-                                } catch (Exception e) {
-                                    // todo: this should be asynchronous!
-                                    Slog.w(TAG, "Exception thrown disconnected servce "
-                                          + r.shortName
-                                          + " from app " + app.processName, e);
+                            ArrayList<ConnectionRecord> cl = jt.next();
+                            for (int i=0; i<cl.size(); i++) {
+                                ConnectionRecord c = cl.get(i);
+                                if (c.binding.client != app) {
+                                    try {
+                                        //c.conn.connected(r.className, null);
+                                    } catch (Exception e) {
+                                        // todo: this should be asynchronous!
+                                        Slog.w(TAG, "Exception thrown disconnected servce "
+                                              + r.shortName
+                                              + " from app " + app.processName, e);
+                                    }
                                 }
                             }
                         }
@@ -7700,7 +7705,9 @@
                 }
                 sr.app = null;
                 sr.executeNesting = 0;
-                mStoppingServices.remove(sr);
+                if (mStoppingServices.remove(sr)) {
+                    if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
+                }
                 
                 boolean hasClients = sr.bindings.size() > 0;
                 if (hasClients) {
@@ -7753,6 +7760,7 @@
             ServiceRecord sr = mStoppingServices.get(i);
             if (sr.app == app) {
                 mStoppingServices.remove(i);
+                if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
             }
         }
         
@@ -8016,11 +8024,15 @@
         if (r.app != null && r.app.persistent) {
             info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS;
         }
-        for (ConnectionRecord conn : r.connections.values()) {
-            if (conn.clientLabel != 0) {
-                info.clientPackage = conn.binding.client.info.packageName;
-                info.clientLabel = conn.clientLabel;
-                break;
+
+        for (ArrayList<ConnectionRecord> connl : r.connections.values()) {
+            for (int i=0; i<connl.size(); i++) {
+                ConnectionRecord conn = connl.get(i);
+                if (conn.clientLabel != 0) {
+                    info.clientPackage = conn.binding.client.info.packageName;
+                    info.clientLabel = conn.clientLabel;
+                    return info;
+                }
             }
         }
         return info;
@@ -8055,9 +8067,11 @@
         synchronized (this) {
             ServiceRecord r = mServices.get(name);
             if (r != null) {
-                for (ConnectionRecord conn : r.connections.values()) {
-                    if (conn.clientIntent != null) {
-                        return conn.clientIntent;
+                for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
+                    for (int i=0; i<conn.size(); i++) {
+                        if (conn.get(i).clientIntent != null) {
+                            return conn.get(i).clientIntent;
+                        }
                     }
                 }
             }
@@ -8234,8 +8248,8 @@
         while (r.pendingStarts.size() > 0) {
             try {
                 ServiceRecord.StartItem si = r.pendingStarts.remove(0);
-                if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to service: "
-                        + r.name + " " + r.intent + " args=" + si.intent);
+                if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to: "
+                        + r + " " + r.intent + " args=" + si.intent);
                 if (si.intent == null) {
                     // If somehow we got a dummy start at the front, then
                     // just drop it here.
@@ -8248,6 +8262,7 @@
                     grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid,
                             r.packageName, si.intent, si);
                 }
+                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING start of " + r);
                 bumpServiceExecutingLocked(r);
                 if (!oomAdjusted) {
                     oomAdjusted = true;
@@ -8264,6 +8279,7 @@
             } catch (RemoteException e) {
                 // Remote process gone...  we'll let the normal cleanup take
                 // care of this.
+                if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while scheduling start: " + r);
                 break;
             } catch (Exception e) {
                 Slog.w(TAG, "Unexpected exception", e);
@@ -8280,9 +8296,9 @@
         }
         if ((!i.requested || rebind) && i.apps.size() > 0) {
             try {
+                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bind of " + r
+                        + " in " + i + ": shouldUnbind=" + i.hasBound);
                 bumpServiceExecutingLocked(r);
-                if (DEBUG_SERVICE) Slog.v(TAG, "Connecting binding " + i
-                        + ": shouldUnbind=" + i.hasBound);
                 r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
                 if (!rebind) {
                     i.requested = true;
@@ -8290,6 +8306,7 @@
                 i.hasBound = true;
                 i.doRebind = false;
             } catch (RemoteException e) {
+                if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r);
                 return false;
             }
         }
@@ -8316,13 +8333,12 @@
         r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
 
         app.services.add(r);
+        if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING create of " + r + " " + r.intent);
         bumpServiceExecutingLocked(r);
         updateLruProcessLocked(app, true, true);
 
         boolean created = false;
         try {
-            if (DEBUG_SERVICE) Slog.v(TAG, "Scheduling start service: "
-                    + r.name + " " + r.intent);
             mStringBuilder.setLength(0);
             r.intent.getIntent().toShortString(mStringBuilder, false, true);
             EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE,
@@ -8482,8 +8498,7 @@
             return true;
         }
 
-        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up service " + r.name
-                + " " + r.intent);
+        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent);
 
         // We are now bringing the service up, so no longer in the
         // restarting state.
@@ -8534,27 +8549,30 @@
             if (!force) {
                 // XXX should probably keep a count of the number of auto-create
                 // connections directly in the service.
-                Iterator<ConnectionRecord> it = r.connections.values().iterator();
+                Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
                 while (it.hasNext()) {
-                    ConnectionRecord cr = it.next();
-                    if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
-                        return;
+                    ArrayList<ConnectionRecord> cr = it.next();
+                    for (int i=0; i<cr.size(); i++) {
+                        if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
+                            return;
+                        }
                     }
                 }
             }
 
             // Report to all of the connections that the service is no longer
             // available.
-            Iterator<ConnectionRecord> it = r.connections.values().iterator();
+            Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
             while (it.hasNext()) {
-                ConnectionRecord c = it.next();
-                try {
-                    // todo: shouldn't be a synchronous call!
-                    c.conn.connected(r.name, null);
-                } catch (Exception e) {
-                    Slog.w(TAG, "Failure disconnecting service " + r.name +
-                          " to connection " + c.conn.asBinder() +
-                          " (in " + c.binding.client.processName + ")", e);
+                ArrayList<ConnectionRecord> c = it.next();
+                for (int i=0; i<c.size(); i++) {
+                    try {
+                        c.get(i).conn.connected(r.name, null);
+                    } catch (Exception e) {
+                        Slog.w(TAG, "Failure disconnecting service " + r.name +
+                              " to connection " + c.get(i).conn.asBinder() +
+                              " (in " + c.get(i).binding.client.processName + ")", e);
+                    }
                 }
             }
         }
@@ -8568,6 +8586,8 @@
                         + ": hasBound=" + ibr.hasBound);
                 if (r.app != null && r.app.thread != null && ibr.hasBound) {
                     try {
+                        if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bring down unbind of " + r
+                                + " for " + ibr);
                         bumpServiceExecutingLocked(r);
                         updateOomAdjLocked(r.app);
                         ibr.hasBound = false;
@@ -8582,15 +8602,13 @@
             }
         }
 
-        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down service " + r.name
-                 + " " + r.intent);
+        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent);
         EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE,
                 System.identityHashCode(r), r.shortName,
                 (r.app != null) ? r.app.pid : -1);
 
         mServices.remove(r.name);
         mServicesByIntent.remove(r.intent);
-        if (localLOGV) Slog.v(TAG, "BRING DOWN SERVICE: " + r.shortName);
         r.totalRestartCount = 0;
         unscheduleServiceRestartLocked(r);
 
@@ -8599,8 +8617,7 @@
         for (int i=0; i<N; i++) {
             if (mPendingServices.get(i) == r) {
                 mPendingServices.remove(i);
-                if (DEBUG_SERVICE) Slog.v(
-                    TAG, "Removed pending service: " + r.shortName);
+                if (DEBUG_SERVICE) Slog.v(TAG, "Removed pending: " + r);
                 i--;
                 N--;
             }
@@ -8622,8 +8639,11 @@
             r.app.services.remove(r);
             if (r.app.thread != null) {
                 try {
-                    if (DEBUG_SERVICE) Slog.v(TAG,
-                            "Stopping service: " + r.shortName);
+                    if (DEBUG_SERVICE) {
+                        RuntimeException here = new RuntimeException();
+                        here.fillInStackTrace();
+                        Slog.v(TAG, ">>> EXECUTING stop of " + r, here);
+                    }
                     bumpServiceExecutingLocked(r);
                     mStoppingServices.add(r);
                     updateOomAdjLocked(r.app);
@@ -8636,11 +8656,11 @@
                 updateServiceForegroundLocked(r.app, false);
             } else {
                 if (DEBUG_SERVICE) Slog.v(
-                    TAG, "Removed service that has no process: " + r.shortName);
+                    TAG, "Removed service that has no process: " + r);
             }
         } else {
             if (DEBUG_SERVICE) Slog.v(
-                TAG, "Removed service that is not running: " + r.shortName);
+                TAG, "Removed service that is not running: " + r);
         }
     }
 
@@ -8675,8 +8695,7 @@
             int targetPermissionUid = checkGrantUriPermissionFromIntentLocked(
                     callingUid, r.packageName, service);
             if (unscheduleServiceRestartLocked(r)) {
-                if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: "
-                        + r.shortName);
+                if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
             }
             r.startRequested = true;
             r.callStart = false;
@@ -8970,7 +8989,7 @@
 
             if (unscheduleServiceRestartLocked(s)) {
                 if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: "
-                        + s.shortName);
+                        + s);
             }
 
             AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
@@ -8978,7 +8997,12 @@
                     connection, flags, clientLabel, clientIntent);
 
             IBinder binder = connection.asBinder();
-            s.connections.put(binder, c);
+            ArrayList<ConnectionRecord> clist = s.connections.get(binder);
+            if (clist == null) {
+                clist = new ArrayList<ConnectionRecord>();
+                s.connections.put(binder, clist);
+            }
+            clist.add(c);
             b.connections.add(c);
             if (activity != null) {
                 if (activity.connections == null) {
@@ -8987,7 +9011,12 @@
                 activity.connections.add(c);
             }
             b.client.connections.add(c);
-            mServiceConnections.put(binder, c);
+            clist = mServiceConnections.get(binder);
+            if (clist == null) {
+                clist = new ArrayList<ConnectionRecord>();
+                mServiceConnections.put(binder, clist);
+            }
+            clist.add(c);
 
             if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                 s.lastActivity = SystemClock.uptimeMillis();
@@ -9038,7 +9067,13 @@
         IBinder binder = c.conn.asBinder();
         AppBindRecord b = c.binding;
         ServiceRecord s = b.service;
-        s.connections.remove(binder);
+        ArrayList<ConnectionRecord> clist = s.connections.get(binder);
+        if (clist != null) {
+            clist.remove(c);
+            if (clist.size() == 0) {
+                s.connections.remove(binder);
+            }
+        }
         b.connections.remove(c);
         if (c.activity != null && c.activity != skipAct) {
             if (c.activity.connections != null) {
@@ -9048,7 +9083,13 @@
         if (b.client != skipApp) {
             b.client.connections.remove(c);
         }
-        mServiceConnections.remove(binder);
+        clist = mServiceConnections.get(binder);
+        if (clist != null) {
+            clist.remove(c);
+            if (clist.size() == 0) {
+                mServiceConnections.remove(binder);
+            }
+        }
 
         if (b.connections.size() == 0) {
             b.intent.apps.remove(b.client);
@@ -9059,6 +9100,8 @@
         if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
                 && b.intent.hasBound) {
             try {
+                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING unbind of " + s
+                        + " from " + b);
                 bumpServiceExecutingLocked(s);
                 updateOomAdjLocked(s.app);
                 b.intent.hasBound = false;
@@ -9081,8 +9124,8 @@
         synchronized (this) {
             IBinder binder = connection.asBinder();
             if (DEBUG_SERVICE) Slog.v(TAG, "unbindService: conn=" + binder);
-            ConnectionRecord r = mServiceConnections.get(binder);
-            if (r == null) {
+            ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
+            if (clist == null) {
                 Slog.w(TAG, "Unbind failed: could not find connection for "
                       + connection.asBinder());
                 return false;
@@ -9090,11 +9133,14 @@
 
             final long origId = Binder.clearCallingIdentity();
 
-            removeConnectionLocked(r, null, null);
+            while (clist.size() > 0) {
+                ConnectionRecord r = clist.get(0);
+                removeConnectionLocked(r, null, null);
 
-            if (r.binding.service.app != null) {
-                // This could have made the service less important.
-                updateOomAdjLocked(r.binding.service.app);
+                if (r.binding.service.app != null) {
+                    // This could have made the service less important.
+                    updateOomAdjLocked(r.binding.service.app);
+                }
             }
 
             Binder.restoreCallingIdentity(origId);
@@ -9117,7 +9163,7 @@
 
             final long origId = Binder.clearCallingIdentity();
 
-            if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING SERVICE " + r.name
+            if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r
                     + " " + intent + ": " + service);
             if (r != null) {
                 Intent.FilterComparison filter
@@ -9128,26 +9174,29 @@
                     b.requested = true;
                     b.received = true;
                     if (r.connections.size() > 0) {
-                        Iterator<ConnectionRecord> it
+                        Iterator<ArrayList<ConnectionRecord>> it
                                 = r.connections.values().iterator();
                         while (it.hasNext()) {
-                            ConnectionRecord c = it.next();
-                            if (!filter.equals(c.binding.intent.intent)) {
-                                if (DEBUG_SERVICE) Slog.v(
-                                        TAG, "Not publishing to: " + c);
-                                if (DEBUG_SERVICE) Slog.v(
-                                        TAG, "Bound intent: " + c.binding.intent.intent);
-                                if (DEBUG_SERVICE) Slog.v(
-                                        TAG, "Published intent: " + intent);
-                                continue;
-                            }
-                            if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
-                            try {
-                                c.conn.connected(r.name, service);
-                            } catch (Exception e) {
-                                Slog.w(TAG, "Failure sending service " + r.name +
-                                      " to connection " + c.conn.asBinder() +
-                                      " (in " + c.binding.client.processName + ")", e);
+                            ArrayList<ConnectionRecord> clist = it.next();
+                            for (int i=0; i<clist.size(); i++) {
+                                ConnectionRecord c = clist.get(i);
+                                if (!filter.equals(c.binding.intent.intent)) {
+                                    if (DEBUG_SERVICE) Slog.v(
+                                            TAG, "Not publishing to: " + c);
+                                    if (DEBUG_SERVICE) Slog.v(
+                                            TAG, "Bound intent: " + c.binding.intent.intent);
+                                    if (DEBUG_SERVICE) Slog.v(
+                                            TAG, "Published intent: " + intent);
+                                    continue;
+                                }
+                                if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
+                                try {
+                                    c.conn.connected(r.name, service);
+                                } catch (Exception e) {
+                                    Slog.w(TAG, "Failure sending service " + r.name +
+                                          " to connection " + c.conn.asBinder() +
+                                          " (in " + c.binding.client.processName + ")", e);
+                                }
                             }
                         }
                     }
@@ -9208,9 +9257,6 @@
             ServiceRecord r = (ServiceRecord)token;
             boolean inStopping = mStoppingServices.contains(token);
             if (r != null) {
-                if (DEBUG_SERVICE) Slog.v(TAG, "DONE EXECUTING SERVICE " + r.name
-                        + ": nesting=" + r.executeNesting
-                        + ", inStopping=" + inStopping);
                 if (r != token) {
                     Slog.w(TAG, "Done executing service " + r.name
                           + " with incorrect token: given " + token
@@ -9267,13 +9313,16 @@
                 serviceDoneExecutingLocked(r, inStopping);
                 Binder.restoreCallingIdentity(origId);
             } else {
-                Slog.w(TAG, "Done executing unknown service " + r.name
-                        + " with token " + token);
+                Slog.w(TAG, "Done executing unknown service from pid "
+                        + Binder.getCallingPid());
             }
         }
     }
 
     public void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) {
+        if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r
+                + ": nesting=" + r.executeNesting
+                + ", inStopping=" + inStopping + ", app=" + r.app);
         r.executeNesting--;
         if (r.executeNesting <= 0 && r.app != null) {
             r.app.executingServices.remove(r);
@@ -9281,6 +9330,7 @@
                 mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app);
             }
             if (inStopping) {
+                if (DEBUG_SERVICE) Slog.v(TAG, "doneExecuting remove stopping " + r);
                 mStoppingServices.remove(r);
             }
             updateOomAdjLocked(r.app);
@@ -11071,61 +11121,64 @@
                 }
                 if (s.connections.size() > 0 && (adj > FOREGROUND_APP_ADJ
                         || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
-                    Iterator<ConnectionRecord> kt
+                    Iterator<ArrayList<ConnectionRecord>> kt
                             = s.connections.values().iterator();
                     while (kt.hasNext() && adj > FOREGROUND_APP_ADJ) {
-                        // XXX should compute this based on the max of
-                        // all connected clients.
-                        ConnectionRecord cr = kt.next();
-                        if (cr.binding.client == app) {
-                            // Binding to ourself is not interesting.
-                            continue;
-                        }
-                        if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
-                            ProcessRecord client = cr.binding.client;
-                            int myHiddenAdj = hiddenAdj;
-                            if (myHiddenAdj > client.hiddenAdj) {
-                                if (client.hiddenAdj >= VISIBLE_APP_ADJ) {
-                                    myHiddenAdj = client.hiddenAdj;
-                                } else {
-                                    myHiddenAdj = VISIBLE_APP_ADJ;
+                        ArrayList<ConnectionRecord> clist = kt.next();
+                        for (int i=0; i<clist.size() && adj > FOREGROUND_APP_ADJ; i++) {
+                            // XXX should compute this based on the max of
+                            // all connected clients.
+                            ConnectionRecord cr = clist.get(i);
+                            if (cr.binding.client == app) {
+                                // Binding to ourself is not interesting.
+                                continue;
+                            }
+                            if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
+                                ProcessRecord client = cr.binding.client;
+                                int myHiddenAdj = hiddenAdj;
+                                if (myHiddenAdj > client.hiddenAdj) {
+                                    if (client.hiddenAdj >= VISIBLE_APP_ADJ) {
+                                        myHiddenAdj = client.hiddenAdj;
+                                    } else {
+                                        myHiddenAdj = VISIBLE_APP_ADJ;
+                                    }
+                                }
+                                int clientAdj = computeOomAdjLocked(
+                                    client, myHiddenAdj, TOP_APP, true);
+                                if (adj > clientAdj) {
+                                    adj = clientAdj >= VISIBLE_APP_ADJ
+                                            ? clientAdj : VISIBLE_APP_ADJ;
+                                    if (!client.hidden) {
+                                        app.hidden = false;
+                                    }
+                                    app.adjType = "service";
+                                    app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                            .REASON_SERVICE_IN_USE;
+                                    app.adjSource = cr.binding.client;
+                                    app.adjTarget = s.name;
+                                }
+                                if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+                                    if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
+                                        schedGroup = Process.THREAD_GROUP_DEFAULT;
+                                    }
                                 }
                             }
-                            int clientAdj = computeOomAdjLocked(
-                                client, myHiddenAdj, TOP_APP, true);
-                            if (adj > clientAdj) {
-                                adj = clientAdj >= VISIBLE_APP_ADJ
-                                        ? clientAdj : VISIBLE_APP_ADJ;
-                                if (!client.hidden) {
-                                    app.hidden = false;
-                                }
+                            ActivityRecord a = cr.activity;
+                            //if (a != null) {
+                            //    Slog.i(TAG, "Connection to " + a ": state=" + a.state);
+                            //}
+                            if (a != null && adj > FOREGROUND_APP_ADJ &&
+                                    (a.state == ActivityState.RESUMED
+                                     || a.state == ActivityState.PAUSING)) {
+                                adj = FOREGROUND_APP_ADJ;
+                                schedGroup = Process.THREAD_GROUP_DEFAULT;
+                                app.hidden = false;
                                 app.adjType = "service";
                                 app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                                         .REASON_SERVICE_IN_USE;
-                                app.adjSource = cr.binding.client;
+                                app.adjSource = a;
                                 app.adjTarget = s.name;
                             }
-                            if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
-                                if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
-                                    schedGroup = Process.THREAD_GROUP_DEFAULT;
-                                }
-                            }
-                        }
-                        ActivityRecord a = cr.activity;
-                        //if (a != null) {
-                        //    Slog.i(TAG, "Connection to " + a ": state=" + a.state);
-                        //}
-                        if (a != null && adj > FOREGROUND_APP_ADJ &&
-                                (a.state == ActivityState.RESUMED
-                                 || a.state == ActivityState.PAUSING)) {
-                            adj = FOREGROUND_APP_ADJ;
-                            schedGroup = Process.THREAD_GROUP_DEFAULT;
-                            app.hidden = false;
-                            app.adjType = "service";
-                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                    .REASON_SERVICE_IN_USE;
-                            app.adjSource = a;
-                            app.adjTarget = s.name;
                         }
                     }
                 }
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 255fbe3..d5b050b 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -72,8 +72,8 @@
     final HashMap<Intent.FilterComparison, IntentBindRecord> bindings
             = new HashMap<Intent.FilterComparison, IntentBindRecord>();
                             // All active bindings to the service.
-    final HashMap<IBinder, ConnectionRecord> connections
-            = new HashMap<IBinder, ConnectionRecord>();
+    final HashMap<IBinder, ArrayList<ConnectionRecord>> connections
+            = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
                             // IBinder -> ConnectionRecord of all bound clients
 
     ProcessRecord app;      // where this service is running or null.
@@ -296,10 +296,12 @@
         }
         if (connections.size() > 0) {
             pw.print(prefix); pw.println("All Connections:");
-            Iterator<ConnectionRecord> it = connections.values().iterator();
+            Iterator<ArrayList<ConnectionRecord>> it = connections.values().iterator();
             while (it.hasNext()) {
-                ConnectionRecord c = it.next();
-                pw.print(prefix); pw.print("  "); pw.println(c);
+                ArrayList<ConnectionRecord> c = it.next();
+                for (int i=0; i<c.size(); i++) {
+                    pw.print(prefix); pw.print("  "); pw.println(c.get(i));
+                }
             }
         }
     }
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 629d993..695cbfa 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -162,9 +162,13 @@
     const uint32_t hwFlags = hw.getFlags();
     
     mFormat = format;
-    mReqFormat = format;
     mWidth  = w;
     mHeight = h;
+
+    mReqFormat = format;
+    mReqWidth = w;
+    mReqHeight = h;
+
     mSecure = (flags & ISurfaceComposer::eSecure) ? true : false;
     mNeedsBlending = (info.h_alpha - info.l_alpha) > 0;
 
@@ -196,12 +200,16 @@
     } else {
 slowpath:
         GGLSurface t;
-        status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
-        LOGE_IF(res, "error %d (%s) locking buffer %p",
-                res, strerror(res), buffer.get());
-        if (res == NO_ERROR) {
-            mBufferManager.loadTexture(dirty, t);
-            buffer->unlock();
+        if (buffer->usage & GRALLOC_USAGE_SW_READ_MASK) {
+            status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
+            LOGE_IF(res, "error %d (%s) locking buffer %p",
+                    res, strerror(res), buffer.get());
+            if (res == NO_ERROR) {
+                mBufferManager.loadTexture(dirty, t);
+                buffer->unlock();
+            }
+        } else {
+            // we can't do anything
         }
     }
 }
@@ -300,16 +308,22 @@
     uint32_t w, h, f;
     { // scope for the lock
         Mutex::Autolock _l(mLock);
-        const bool fixedSizeChanged = mFixedSize != (reqWidth && reqHeight);
-        const bool formatChanged    = mReqFormat != reqFormat;
-        mReqWidth  = reqWidth;
-        mReqHeight = reqHeight;
-        mReqFormat = reqFormat;
-        mFixedSize = reqWidth && reqHeight;
-        w = reqWidth  ? reqWidth  : mWidth;
-        h = reqHeight ? reqHeight : mHeight;
-        f = reqFormat ? reqFormat : mFormat;
-        if (fixedSizeChanged || formatChanged) {
+
+        // zero means default
+        if (!reqFormat) reqFormat = mFormat;
+        if (!reqWidth)  reqWidth = mWidth;
+        if (!reqHeight) reqHeight = mHeight;
+
+        w = reqWidth;
+        h = reqHeight;
+        f = reqFormat;
+
+        if ((reqWidth != mReqWidth) || (reqHeight != mReqHeight) ||
+                (reqFormat != mReqFormat)) {
+            mReqWidth  = reqWidth;
+            mReqHeight = reqHeight;
+            mReqFormat = reqFormat;
+
             lcblk->reallocateAllExcept(index);
         }
     }
@@ -473,9 +487,9 @@
         return;
     }
 
-    // get the dirty region
     sp<GraphicBuffer> newFrontBuffer(getBuffer(buf));
     if (newFrontBuffer != NULL) {
+        // get the dirty region
         // compute the posted region
         const Region dirty(lcblk->getDirtyRegion(buf));
         mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() );
@@ -511,6 +525,13 @@
             // we now have the correct size, unfreeze the screen
             mFreezeLock.clear();
         }
+
+        // get the crop region
+        setBufferCrop( lcblk->getCrop(buf) );
+
+        // get the transformation
+        setBufferTransform( lcblk->getTransform(buf) );
+
     } else {
         // this should not happen unless we ran out of memory while
         // allocating the buffer. we're hoping that things will get back
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 91ac915..6fc5010 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -54,6 +54,8 @@
 {
     const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
     mFlags = hw.getFlags();
+    mBufferCrop.makeInvalid();
+    mBufferTransform = 0;
 }
 
 LayerBase::~LayerBase()
@@ -345,6 +347,14 @@
     clearWithOpenGL(clip,0,0,0,0);
 }
 
+template <typename T>
+static inline
+void swap(T& a, T& b) {
+    T t(a);
+    a = b;
+    b = t;
+}
+
 void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
 {
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
@@ -378,37 +388,72 @@
         }
     }
 
-    Region::const_iterator it = clip.begin();
-    Region::const_iterator const end = clip.end();
-    const GLfloat texCoords[4][2] = {
-            { 0,  0 },
-            { 0,  1 },
-            { 1,  1 },
-            { 1,  0 }
+    /*
+     *  compute texture coordinates
+     *  here, we handle NPOT, cropping and buffer transformations
+     */
+
+    GLfloat cl, ct, cr, cb;
+    if (!mBufferCrop.isEmpty()) {
+        // source is cropped
+        const GLfloat us = (texture.NPOTAdjust ? texture.wScale : 1.0f) / width;
+        const GLfloat vs = (texture.NPOTAdjust ? texture.hScale : 1.0f) / height;
+        cl = mBufferCrop.left   * us;
+        ct = mBufferCrop.top    * vs;
+        cr = mBufferCrop.right  * us;
+        cb = mBufferCrop.bottom * vs;
+    } else {
+        cl = 0;
+        ct = 0;
+        cr = (texture.NPOTAdjust ? texture.wScale : 1.0f);
+        cb = (texture.NPOTAdjust ? texture.hScale : 1.0f);
+    }
+
+    struct TexCoords {
+        GLfloat u;
+        GLfloat v;
     };
 
-    glMatrixMode(GL_TEXTURE);
-    glLoadIdentity();
+    enum {
+        // name of the corners in the texture map
+        LB = 0, // left-bottom
+        LT = 1, // left-top
+        RT = 2, // right-top
+        RB = 3  // right-bottom
+    };
+
+    // vertices in screen space
+    int vLT = LB;
+    int vLB = LT;
+    int vRB = RT;
+    int vRT = RB;
 
     // the texture's source is rotated
-    switch (texture.transform) {
-        case HAL_TRANSFORM_ROT_90:
-            glTranslatef(0, 1, 0);
-            glRotatef(-90, 0, 0, 1);
-            break;
-        case HAL_TRANSFORM_ROT_180:
-            glTranslatef(1, 1, 0);
-            glRotatef(-180, 0, 0, 1);
-            break;
-        case HAL_TRANSFORM_ROT_270:
-            glTranslatef(1, 0, 0);
-            glRotatef(-270, 0, 0, 1);
-            break;
+    uint32_t transform = mBufferTransform;
+    if (transform & HAL_TRANSFORM_ROT_90) {
+        vLT = RB;
+        vLB = LB;
+        vRB = LT;
+        vRT = RT;
+    }
+    if (transform & HAL_TRANSFORM_FLIP_V) {
+        swap(vLT, vLB);
+        swap(vRB, vRT);
+    }
+    if (transform & HAL_TRANSFORM_FLIP_H) {
+        swap(vLT, vRB);
+        swap(vLB, vRT);
     }
 
-    if (texture.NPOTAdjust) {
-        glScalef(texture.wScale, texture.hScale, 1.0f);
-    }
+    TexCoords texCoords[4];
+    texCoords[vLT].u = cl;
+    texCoords[vLT].v = ct;
+    texCoords[vLB].u = cl;
+    texCoords[vLB].v = cb;
+    texCoords[vRB].u = cr;
+    texCoords[vRB].v = cb;
+    texCoords[vRT].u = cr;
+    texCoords[vRT].v = ct;
 
     if (needsDithering()) {
         glEnable(GL_DITHER);
@@ -420,6 +465,8 @@
     glVertexPointer(2, GL_FLOAT, 0, mVertices);
     glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
 
+    Region::const_iterator it = clip.begin();
+    Region::const_iterator const end = clip.end();
     while (it != end) {
         const Rect& r = *it++;
         const GLint sy = fbHeight - (r.top + r.height());
@@ -429,6 +476,16 @@
     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 }
 
+void LayerBase::setBufferCrop(const Rect& crop) {
+    if (!crop.isEmpty()) {
+        mBufferCrop = crop;
+    }
+}
+
+void LayerBase::setBufferTransform(uint32_t transform) {
+    mBufferTransform = transform;
+}
+
 void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const
 {
     const Layer::State& s(drawingState());
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 22bf857..8cba287 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -226,9 +226,17 @@
           void clearWithOpenGL(const Region& clip) const;
           void drawWithOpenGL(const Region& clip, const Texture& texture) const;
           
+          // these must be called from the post/drawing thread
+          void setBufferCrop(const Rect& crop);
+          void setBufferTransform(uint32_t transform);
+
                 sp<SurfaceFlinger> mFlinger;
                 uint32_t        mFlags;
 
+                // post/drawing thread
+                Rect mBufferCrop;
+                uint32_t mBufferTransform;
+
                 // cached during validateVisibility()
                 bool            mNeedsFiltering;
                 int32_t         mOrientation;
diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp
index 64a43c7..2ee21b9 100644
--- a/services/surfaceflinger/LayerBlur.cpp
+++ b/services/surfaceflinger/LayerBlur.cpp
@@ -241,6 +241,8 @@
             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
         }
         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+        glLoadIdentity();
+        glMatrixMode(GL_MODELVIEW);
     }
 }
 
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index 5f836366..0240748 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -485,7 +485,7 @@
         mTextureManager.loadTexture(&mTexture, dirty, t);
     }
 
-    mTexture.transform = mBufferHeap.transform;
+    mLayer.setBufferTransform(mBufferHeap.transform);
     mLayer.drawWithOpenGL(clip, mTexture);
 }
 
diff --git a/services/surfaceflinger/TextureManager.h b/services/surfaceflinger/TextureManager.h
index c7c14e7..18c43486 100644
--- a/services/surfaceflinger/TextureManager.h
+++ b/services/surfaceflinger/TextureManager.h
@@ -40,12 +40,11 @@
 struct Image {
     enum { TEXTURE_2D=0, TEXTURE_EXTERNAL=1 };
     Image() : name(-1U), image(EGL_NO_IMAGE_KHR), width(0), height(0),
-        transform(0), dirty(1), target(TEXTURE_2D) { }
+        dirty(1), target(TEXTURE_2D) { }
     GLuint        name;
     EGLImageKHR   image;
     GLuint        width;
     GLuint        height;
-    uint32_t      transform;
     unsigned      dirty     : 1;
     unsigned      target    : 1;
 };
diff --git a/tests/CoreTests/android/core/JavaPerformanceTests.java b/tests/CoreTests/android/core/JavaPerformanceTests.java
index fbe70cc..95075ea 100644
--- a/tests/CoreTests/android/core/JavaPerformanceTests.java
+++ b/tests/CoreTests/android/core/JavaPerformanceTests.java
@@ -23,7 +23,6 @@
 
     public static String[] children() {
         return new String[] {
-                StringTest.class.getName(),
                 HashMapPerformanceTest.class.getName(),
                 ArrayListPerformanceTest.class.getName(),
                 TreeMapPerformanceTest.class.getName(),
diff --git a/tests/CoreTests/android/core/StringTest.java b/tests/CoreTests/android/core/StringTest.java
deleted file mode 100644
index 128531c..0000000
--- a/tests/CoreTests/android/core/StringTest.java
+++ /dev/null
@@ -1,951 +0,0 @@
-/*
- * Copyright (C) 2007 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.core;
-
-import java.util.Locale;
-
-import android.test.PerformanceTestBase;
-import android.test.PerformanceTestCase;
-
-public class StringTest extends PerformanceTestBase {
-    public static final int ITERATIONS = 1000;
-    public static final String STATIC_STRING_01 = "Hello Android";
-    public static final String STATIC_STRING_02 =
-            "Remember, today is the tomorrow you worried about yesterday";
-    public static final char[] STATIC_CHAR_ARRAY =
-            {'N', 'A', 'N', 'D', 'R', 'O', 'I', 'D'};
-    public static StringBuffer STATIC_SBUF = new StringBuffer(STATIC_STRING_02);
-
-    @Override
-    public int startPerformance(PerformanceTestCase.Intermediates intermediates) {
-        intermediates.setInternalIterations(ITERATIONS);
-        return 0;
-    }
-
-    /** Create an empty String object* */
-
-    public void testStringCreate() {
-        String rString;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-        }
-    }
-
-    /** Create an initialised String object* */
-
-    public void testStringCreate1() {
-        String rString, str = STATIC_STRING_01;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str); // 10
-        }
-    }
-
-    /** equals() with for loop* */
-    public void testStringEquals() {
-        String mString = new String(STATIC_STRING_01);
-        String str = STATIC_STRING_01;
-        boolean result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-        }
-    }
-
-    /**
-     * ContentEquals- Comparing the content of a String with that of a String
-     * Buffer*
-     */
-
-    public void testStringContentEquals() {
-        StringBuffer sBuf = new StringBuffer(STATIC_STRING_01);
-        String str = STATIC_STRING_01;
-        boolean result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-        }
-    }
-
-    /** Compare string objects lexicographically using compareTo() with for loop* */
-
-    public void testStringCompareTo() {
-        String str1 = new String(STATIC_STRING_01);
-        String str2 = STATIC_STRING_01;
-        int result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-        }
-
-    }
-
-    /** Compare string objects using compareToIgnorecase() with for loop* */
-
-    public void testStringCompareToIgnoreCase() {
-        String mString = new String(STATIC_STRING_01);
-        String str2 = STATIC_STRING_01;
-        int result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-        }
-    }
-
-    /** startsWith * */
-
-    public void testStringstartsWith() {
-        boolean result;
-        String str1 = STATIC_STRING_02, str2 = "Rem";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-        }
-    }
-
-    /** startsWith(String seq, int begin) * */
-
-    public void testStringstartsWith1() {
-        String str1 = STATIC_STRING_02, str2 = "tom";
-        int pos = 10;
-        boolean result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-        }
-    }
-
-    /** endsWith * */
-
-    public void testStringendsWith() {
-        String str = STATIC_STRING_02, str1 = "day";
-        boolean result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-        }
-    }
-
-    /**
-     * indexOf to determine whether a string contains a substring
-     */
-    public void testStringindexOf() {
-        boolean result;
-        String str = STATIC_STRING_02, str1 = "tomo";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-        }
-    }
-
-    /** indexOf()* */
-
-    public void testStringindexOf1() {
-        int index;
-        String str = STATIC_STRING_02;
-        char c = 't';
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-        }
-
-    }
-
-    /** indexOf(char c, int start)* */
-    public void testStringindexOf2() {
-        int index, pos = 12;
-        String str = STATIC_STRING_02, str1 = "tom";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-        }
-    }
-
-    /** lastIndexOf()* */
-
-    public void testStringlastIndexOf() {
-        int index;
-        char c = 't';
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-        }
-    }
-
-    /** lastIndexOf()* */
-
-    public void testStringlastIndexOf1() {
-        int index, pos = 36;
-        String str = STATIC_STRING_02, str1 = "tom";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-        }
-    }
-
-    /**
-     * contains() to determine whether a string contains a substring
-     */
-
-    public void testStringcontains() {
-        boolean result;
-        String str = STATIC_STRING_02, str1 = "tomo";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-        }
-    }
-
-    /** substring(int start) */
-
-    public void testStringsubstring() {
-        String rString;
-        String str = STATIC_STRING_02;
-        int index = 10;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-        }
-    }
-
-    /** substring(int start, int end) in a for loop* */
-
-    public void testStringsubstring1() {
-        String rString;
-        String str = STATIC_STRING_02;
-        int start = 10, end = 48;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-        }
-    }
-
-    /**
-     * valueOf(char[] cArray) String representation of a character array
-     */
-    public void testStringvalueOf() {
-        String rString;
-        char[] cArray = STATIC_CHAR_ARRAY;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-        }
-    }
-
-    /** valueOf(char[] cArray, int offset, int count)* */
-
-    public void testStringvalueOf1() {
-        String rString;
-        char[] cArray = STATIC_CHAR_ARRAY;
-        int start = 1, end = 7;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-        }
-    }
-
-    /** Convert a string to a char Array* */
-
-    public void testStringtoCharArray() {
-        char[] cArray;
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-        }
-    }
-
-    /** length()* */
-
-    public void testStringlength() {
-        int len;
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-        }
-    }
-
-    /** hashcode()* */
-
-    public void testStringhashCode() {
-        int index;
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-        }
-    }
-
-    /** replace()* */
-
-    public void testStringreplace() {
-        String rString;
-        String str = STATIC_STRING_02;
-        char c1 = ' ', c2 = ' ';
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-        }
-    }
-
-    public void testStringreplaceAll() {
-        String rString;
-        String str = STATIC_STRING_02, str1 = " ", str2 = "/";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-        }
-    }
-
-    /** Convert a StringBuffer to a String* */
-
-    public void testStringtoString() {
-        StringBuffer sBuf = new StringBuffer(STATIC_STRING_02);
-
-        String rString;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-        }
-    }
-
-    /** Split a string into an array of strings* */
-
-    public void testStringsplit() {
-        String[] strings;
-        String str1 = STATIC_STRING_02, str = " ";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-
-        }
-    }
-
-    /** Split a string into an array of strings* */
-
-    public void testStringsplit1() {
-        String str = STATIC_STRING_02, str1 = " ";
-        String[] strings;
-        int pos = 8;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-        }
-    }
-
-    public void testStringgetBytes() {
-        byte[] bytes;
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-        }
-    }
-
-    /** copyValueOf(char[] data) * */
-
-    public void testStringcopyValueOf() {
-        String rString;
-        char[] cArray = STATIC_CHAR_ARRAY;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-        }
-    }
-
-    /** copyValueOf(char[] data, int index, int count)* */
-
-    public void testStringcopyValueOf1() {
-        String rString;
-        int start = 1, end = 7;
-        char[] cArray = STATIC_CHAR_ARRAY;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-        }
-    }
-
-    /** trim()* */
-
-    public void testStringtrim() {
-        String mString =
-                new String(
-                        "                            HELLO ANDROID                                                ");
-        String rString;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-        }
-    }
-
-    /** getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)* */
-
-    public void testStringgetChars() {
-        char[] cArray = STATIC_CHAR_ARRAY;
-        String str = STATIC_STRING_01;
-        int value1 = 7, value2 = 12, value3 = 1;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-        }
-    }
-
-    /** toUpperCase()* */
-
-    public void testStringtoUpperCase() {
-        String rString, str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-        }
-    }
-
-    /** toUpperCase() with locale* */
-
-    public void testStringtoUpperCase1() {
-        Locale locale = new Locale("tr");
-        String str = STATIC_STRING_02;
-        String rString;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-        }
-    }
-
-    /** toLowerCase* */
-
-    public void StringtoLowerCase() {
-        String rString, str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-        }
-    }
-
-    /** toLowerCase with locale* */
-
-    public void testStringtoLowerCase1() {
-        Locale locale = new Locale("tr");
-        String rString, str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-        }
-    }
-
-    /** charAt()* */
-
-    public void testStringcharAt() {
-        String str = STATIC_STRING_02;
-        int index, pos = 21;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-        }
-    }
-
-    public void testStringConcat() {
-        String mString, str1 = STATIC_STRING_01, str2 = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-        }
-    }
-
-    public void testStringBufferAppend() {
-        StringBuffer sBuf = new StringBuffer(" ");
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-        }
-    }
-
-    public void testStringBufferInsert() {
-        StringBuffer sBuf = new StringBuffer(" ");
-        int index = sBuf.length();
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-        }
-    }
-
-    public void testStringBufferReverse() {
-        StringBuffer sBuf = STATIC_SBUF;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-        }
-    }
-
-    public void testStringBufferSubstring() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String rString;
-        int index = 0;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-        }
-    }
-
-    public void testStringBufferSubstring1() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String rString;
-        int start = 5, end = 25;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-        }
-    }
-
-    public void testStringBufferReplace() {
-        StringBuffer sBuf = STATIC_SBUF;
-        int start = 3, end = 6;
-        String str = "ind";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-        }
-    }
-
-    public void testStringBufferIndexOf() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String str = "t";
-        int index;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-        }
-    }
-
-    public void testStringBufferIndexOf1() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String str = "tom";
-        int index, pos = 12;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-        }
-
-    }
-
-    public void testStringBufferLastIndexOf() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String str = "t";
-        int index;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-        }
-    }
-
-    public void testStringBufferLastIndexOf1() {
-        StringBuffer sBuf = STATIC_SBUF;
-        int index, pos = 36;
-        String str = "tom";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-        }
-    }
-}
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index b339a2c..094b7db 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -41,7 +41,7 @@
 	libpng
 
 ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt
+LOCAL_LDLIBS += -lrt -lpthread
 endif
 
 # Statically link libz for MinGW (Win SDK under Linux),
diff --git a/tools/localize/Android.mk b/tools/localize/Android.mk
index ab79f8d..f284e86 100644
--- a/tools/localize/Android.mk
+++ b/tools/localize/Android.mk
@@ -34,7 +34,7 @@
 	libcutils
     
 ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt
+LOCAL_LDLIBS += -lrt -lpthread
 endif
 
 
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
index e71c293..ec8d0ed 100644
--- a/voip/java/android/net/sip/SipProfile.java
+++ b/voip/java/android/net/sip/SipProfile.java
@@ -35,7 +35,7 @@
  * Class containing a SIP account, domain and server information.
  * @hide
  */
-public class SipProfile implements Parcelable, Serializable {
+public class SipProfile implements Parcelable, Serializable, Cloneable {
     private static final long serialVersionUID = 1L;
     private static final int DEFAULT_PORT = 5060;
     private Address mAddress;
@@ -46,6 +46,7 @@
     private String mProfileName;
     private boolean mSendKeepAlive = false;
     private boolean mAutoRegistration = true;
+    private boolean mAllowOutgoingCall = false;
 
     /** @hide */
     public static final Parcelable.Creator<SipProfile> CREATOR =
@@ -79,6 +80,23 @@
         }
 
         /**
+         * Creates a builder based on the given profile.
+         */
+        public Builder(SipProfile profile) {
+            if (profile == null) throw new NullPointerException();
+            try {
+                mProfile = (SipProfile) profile.clone();
+            } catch (CloneNotSupportedException e) {
+                throw new RuntimeException("should not occur", e);
+            }
+            mProfile.mAddress = null;
+            mUri = profile.getUri();
+            mUri.setUserPassword(profile.getPassword());
+            mDisplayName = profile.getDisplayName();
+            mProxyAddress = profile.getProxyAddress();
+        }
+
+        /**
          * Constructor.
          *
          * @param uriString the URI string as "sip:<user_name>@<domain>"
@@ -226,6 +244,18 @@
         }
 
         /**
+         * Sets the allow-outgoing-call flag.
+         *
+         * @param flag true if allowing to make outgoing call on the profile;
+         *      false otherwise
+         * @return this builder object
+         */
+        public Builder setOutgoingCallAllowed(boolean flag) {
+            mProfile.mAllowOutgoingCall = flag;
+            return this;
+        }
+
+        /**
          * Builds and returns the SIP profile object.
          *
          * @return the profile object created
@@ -262,6 +292,7 @@
         mProfileName = in.readString();
         mSendKeepAlive = (in.readInt() == 0) ? false : true;
         mAutoRegistration = (in.readInt() == 0) ? false : true;
+        mAllowOutgoingCall = (in.readInt() == 0) ? false : true;
     }
 
     /** @hide */
@@ -274,6 +305,7 @@
         out.writeString(mProfileName);
         out.writeInt(mSendKeepAlive ? 1 : 0);
         out.writeInt(mAutoRegistration ? 1 : 0);
+        out.writeInt(mAllowOutgoingCall ? 1 : 0);
     }
 
     /** @hide */
@@ -398,4 +430,11 @@
     public boolean getAutoRegistration() {
         return mAutoRegistration;
     }
+
+    /**
+     * Returns true if allowing to make outgoing calls on this profile.
+     */
+    public boolean isOutgoingCallAllowed() {
+        return mAllowOutgoingCall;
+    }
 }