Merge "AnimatedImageView: Stop the animation when we're not visible." into gingerbread
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index e56e257..6d19f41 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -68,6 +68,7 @@
     private static File RECOVERY_DIR = new File("/cache/recovery");
     private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
     private static File LOG_FILE = new File(RECOVERY_DIR, "log");
+    private static String LAST_LOG_FILENAME = "last_log";
 
     // Length limits for reading files.
     private static int LOG_FILE_MAX_LENGTH = 64 * 1024;
@@ -399,9 +400,10 @@
             Log.e(TAG, "Error reading recovery log", e);
         }
 
-        // Delete everything in RECOVERY_DIR
+        // Delete everything in RECOVERY_DIR except LAST_LOG_FILENAME
         String[] names = RECOVERY_DIR.list();
         for (int i = 0; names != null && i < names.length; i++) {
+            if (names[i].equals(LAST_LOG_FILENAME)) continue;
             File f = new File(RECOVERY_DIR, names[i]);
             if (!f.delete()) {
                 Log.e(TAG, "Can't delete: " + f);
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index e05fe7b..bcb151a 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -225,10 +225,10 @@
         }
         String name = propValues[0];
         if (name.equals("Name")) {
+            mBluetoothService.setProperty(name, propValues[1]);
             Intent intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
             intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, propValues[1]);
             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-            mBluetoothService.setProperty(name, propValues[1]);
         } else if (name.equals("Pairable") || name.equals("Discoverable")) {
             String pairable = name.equals("Pairable") ? propValues[1] :
                 mBluetoothService.getPropertyInternal("Pairable");
@@ -239,6 +239,7 @@
             if (pairable == null || discoverable == null)
                 return;
 
+            mBluetoothService.setProperty(name, propValues[1]);
             int mode = BluetoothService.bluezStringToScanMode(
                     pairable.equals("true"),
                     discoverable.equals("true"));
@@ -248,9 +249,9 @@
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
             }
-            mBluetoothService.setProperty(name, propValues[1]);
         } else if (name.equals("Discovering")) {
             Intent intent;
+            mBluetoothService.setProperty(name, propValues[1]);
             if (propValues[1].equals("true")) {
                 mBluetoothService.setIsDiscovering(true);
                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
@@ -261,7 +262,6 @@
                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
             }
             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-            mBluetoothService.setProperty(name, propValues[1]);
         } else if (name.equals("Devices")) {
             String value = null;
             int len = Integer.valueOf(propValues[1]);
@@ -294,19 +294,20 @@
         }
         BluetoothDevice device = mAdapter.getRemoteDevice(address);
         if (name.equals("Name")) {
+            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
             Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
             intent.putExtra(BluetoothDevice.EXTRA_NAME, propValues[1]);
             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
         } else if (name.equals("Class")) {
+            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
             Intent intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
             intent.putExtra(BluetoothDevice.EXTRA_CLASS,
                     new BluetoothClass(Integer.valueOf(propValues[1])));
             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
         } else if (name.equals("Connected")) {
+            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
             Intent intent = null;
             if (propValues[1].equals("true")) {
                 intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
@@ -320,7 +321,6 @@
             }
             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
         } else if (name.equals("UUIDs")) {
             String uuid = null;
             int len = Integer.valueOf(propValues[1]);
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e377f49..d9177e7 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -381,8 +381,10 @@
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_storage">Storage</string>
+    <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this.   [CHAR LIMIT=30] -->
+    <string name="permgroupdesc_storage" product="nosdcard">Access the shared storage.</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permgroupdesc_storage">Access the SD card.</string>
+    <string name="permgroupdesc_storage" product="default">Access the SD card.</string>
 
     <!--  Permissions -->
 
@@ -1224,10 +1226,14 @@
     <string name="permdesc_writeDictionary">Allows an application to write new words into the
       user dictionary.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+    <string name="permlab_sdcardWrite" product="nosdcard">modify/delete shared storage contents</string>
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_sdcardWrite">modify/delete SD card contents</string>
+    <string name="permlab_sdcardWrite" product="default">modify/delete SD card contents</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+    <string name="permdesc_sdcardWrite" product="nosdcard">Allows an application to write to the shared storage.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_sdcardWrite">Allows an application to write to the SD card.</string>
+    <string name="permdesc_sdcardWrite" product="default">Allows an application to write to the SD card.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_cache_filesystem">access the cache filesystem</string>
@@ -2079,12 +2085,16 @@
 
     <!-- See USB_STORAGE.  USB_STORAGE_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to mount.  This is the title. -->
     <string name="usb_storage_title">USB connected</string>
+    <!-- See USB_STORAGE.    This is the message. [CHAR LIMIT=NONE] -->
+    <string name="usb_storage_message" product="nosdcard">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s shared storage.</string>
     <!-- See USB_STORAGE.    This is the message. -->
-    <string name="usb_storage_message">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s SD card.</string>
+    <string name="usb_storage_message" product="default">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s SD card.</string>
     <!-- See USB_STORAGE.    This is the button text to mount the phone on the computer. -->
     <string name="usb_storage_button_mount">Turn on USB storage</string>
+    <!-- See USB_STORAGE_DIALOG.  If there was an error mounting, this is the text. [CHAR LIMIT=NONE] -->
+    <string name="usb_storage_error_message" product="nosdcard">There is a problem using your shared storage for USB storage.</string>
     <!-- See USB_STORAGE_DIALOG.  If there was an error mounting, this is the text. -->
-    <string name="usb_storage_error_message">There is a problem using your SD card for USB storage.</string>
+    <string name="usb_storage_error_message" product="default">There is a problem using your SD card for USB storage.</string>
     <!-- USB_STORAGE: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across.  This is the title -->
     <string name="usb_storage_notification_title">USB connected</string>
     <!-- See USB_STORAGE. This is the message. -->
@@ -2099,8 +2109,10 @@
     <!-- This is the label for the activity, and should never be visible to the user. -->
     <!-- See USB_STORAGE_STOP.  USB_STORAGE_STOP_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to stop usb storage.  This is the title. -->
     <string name="usb_storage_stop_title">USB storage in use</string>
+    <!-- See USB_STORAGE_STOP.    This is the message. [CHAR LIMIT=NONE] -->
+    <string name="usb_storage_stop_message" product="nosdcard">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s shared storage from your computer.</string>
     <!-- See USB_STORAGE_STOP.    This is the message. -->
-    <string name="usb_storage_stop_message">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s SD card from your computer.</string>
+    <string name="usb_storage_stop_message" product="default">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s SD card from your computer.</string>
     <!-- See USB_STORAGE_STOP.    This is the button text to stop usb storage. -->
     <string name="usb_storage_stop_button_mount">Turn off USB storage</string>
     <!-- See USB_STORAGE_STOP_DIALOG.  If there was an error stopping, this is the text. -->
@@ -2117,10 +2129,14 @@
 
     <!-- External media format dialog strings -->
     <!-- This is the label for the activity, and should never be visible to the user. -->
+    <!-- See EXTMEDIA_FORMAT.  EXTMEDIA_FORMAT_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to format the SD card.  This is the title. [CHAR LIMIT=20] -->
+    <string name="extmedia_format_title" product="nosdcard">Format shared storage</string>
     <!-- See EXTMEDIA_FORMAT.  EXTMEDIA_FORMAT_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to format the SD card.  This is the title. -->
-    <string name="extmedia_format_title">Format SD card</string>
+    <string name="extmedia_format_title" product="default">Format SD card</string>
+    <!-- See EXTMEDIA_FORMAT.   This is the message. [CHAR LIMIT=NONE] -->
+    <string name="extmedia_format_message" product="nosdcard">Format shared storage, erasing all files stored there?  Action cannot be reversed!</string>
     <!-- See EXTMEDIA_FORMAT.   This is the message. -->
-    <string name="extmedia_format_message">Are you sure you want to format the SD card? All data on your card will be lost.</string>
+    <string name="extmedia_format_message" product="default">Are you sure you want to format the SD card? All data on your card will be lost.</string>
     <!-- See EXTMEDIA_FORMAT.    This is the button text to format the sd card. -->
     <string name="extmedia_format_button_format">Format</string>
 
@@ -2145,29 +2161,51 @@
     <string name="candidates_style"><u>candidates</u></string>
 
     <!-- External media notification strings -->
+    <!-- Shown when external media is being checked [CHAR LIMIT=30] -->
+    <string name="ext_media_checking_notification_title" product="nosdcard">Preparing shared storage</string>
     <!-- Shown when external media is being checked -->
-    <string name="ext_media_checking_notification_title">Preparing SD card</string>
+    <string name="ext_media_checking_notification_title" product="default">Preparing SD card</string>
     <string name="ext_media_checking_notification_message">Checking for errors.</string>
 
+    <!-- Shown when external media is blank (or unsupported filesystem) [CHAR LIMIT=30] -->
+    <string name="ext_media_nofs_notification_title" product="nosdcard">Blank shared storage</string>
     <!-- Shown when external media is blank (or unsupported filesystem) -->
-    <string name="ext_media_nofs_notification_title">Blank SD card</string>
-    <string name="ext_media_nofs_notification_message">SD card blank or has unsupported filesystem.</string>
+    <string name="ext_media_nofs_notification_title" product="default">Blank SD card</string>
+    <!-- Shown when shared storage cannot be read.  [CHAR LIMIT=NONE] -->
+    <string name="ext_media_nofs_notification_message" product="nosdcard">Shared storage blank or has unsupported filesystem.</string>
+    <string name="ext_media_nofs_notification_message" product="default">SD card blank or has unsupported filesystem.</string>
 
+    <!-- Shown when external media is unmountable (corrupt)) [CHAR LIMIT=30] -->
+    <string name="ext_media_unmountable_notification_title" product="nosdcard">Damaged shared storage</string>
     <!-- Shown when external media is unmountable (corrupt)) -->
-    <string name="ext_media_unmountable_notification_title">Damaged SD card</string>
-    <string name="ext_media_unmountable_notification_message">SD card damaged. You may have to reformat it.</string>
+    <string name="ext_media_unmountable_notification_title" product="default">Damaged SD card</string>
+    <!-- Shown when shared storage cannot be read.  [CHAR LIMIT=NONE] -->
+    <string name="ext_media_unmountable_notification_message" product="nosdcard">Shared storage damaged. You may have to reformat it.</string>
+    <string name="ext_media_unmountable_notification_message" product="default">SD card damaged. You may have to reformat it.</string>
 
+    <!-- Shown when external media is unsafely removed [CHAR LIMIT=30] -->
+    <string name="ext_media_badremoval_notification_title" product="nosdcard">Shared storage unexpectedly removed</string>
     <!-- Shown when external media is unsafely removed -->
-    <string name="ext_media_badremoval_notification_title">SD card unexpectedly removed</string>
-    <string name="ext_media_badremoval_notification_message">Unmount SD card before removing to avoid data loss.</string>
+    <string name="ext_media_badremoval_notification_title" product="default">SD card unexpectedly removed</string>
+    <!-- Shown when external media is unsafely removed.  [CHAR LIMIT=NONE] -->
+    <string name="ext_media_badremoval_notification_message" product="nosdcard">Unmount shared storage before removing to avoid data loss.</string>
+    <string name="ext_media_badremoval_notification_message" product="default">Unmount SD card before removing to avoid data loss.</string>
 
+    <!-- Shown when external media has been safely removed [CHAR LIMIT=30] -->
+    <string name="ext_media_safe_unmount_notification_title" product="nosdcard">Shared storage safe to remove</string>
     <!-- Shown when external media has been safely removed -->
-    <string name="ext_media_safe_unmount_notification_title">SD card safe to remove</string>
-    <string name="ext_media_safe_unmount_notification_message">You can safely remove SD card.</string>
+    <string name="ext_media_safe_unmount_notification_title" product="default">SD card safe to remove</string>
+    <!-- Shown when external media has been safely removed.  [CHAR LIMIT=NONE] -->
+    <string name="ext_media_safe_unmount_notification_message" product="nosdcard">You can safely remove shared storage.</string>
+    <string name="ext_media_safe_unmount_notification_message" product="default">You can safely remove SD card.</string>
 
+    <!-- Shown when external media is missing [CHAR LIMIT=30] -->
+    <string name="ext_media_nomedia_notification_title" product="nosdcard">Removed shared storage</string>
     <!-- Shown when external media is missing -->
-    <string name="ext_media_nomedia_notification_title">Removed SD card</string>
-    <string name="ext_media_nomedia_notification_message">SD card removed. Insert a new one.</string>
+    <string name="ext_media_nomedia_notification_title" product="default">Removed SD card</string>
+    <!-- Shown when external media is missing.  [CHAR LIMIT=NONE] -->
+    <string name="ext_media_nomedia_notification_message" product="nosdcard">Shared storage removed. Insert new media.</string>
+    <string name="ext_media_nomedia_notification_message" product="default">SD card removed. Insert a new one.</string>
 
     <!-- Shown in LauncherActivity when the requested target Intent didn't return any matching Activities, leaving the list empty. -->
     <string name="activity_list_empty">No matching activities found</string>
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png
index 157491e..a0e59cf 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png
index aea18ed..1626895 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png
old mode 100755
new mode 100644
index 1a25a2c..3c2e2b9
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png
index 2134d49..bb41db0 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png
Binary files differ
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index 798a5a5..25ca559 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -24,9 +24,10 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.PhoneLookup;
 import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -36,7 +37,7 @@
 
 public class CallerInfoAsyncQuery {
 
-    private static final boolean DBG = false;
+    private static final boolean DBG = true; // STOPSHIP: disable debugging before ship
     private static final String LOG_TAG = "CallerInfoAsyncQuery";
 
     private static final int EVENT_NEW_QUERY = 1;
@@ -189,7 +190,7 @@
          */
         @Override
         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
-            if (DBG) log("query complete for token: " + token);
+            if (DBG) log("##### onQueryComplete() #####   query complete for token: " + token);
 
             //get the cookie and notify the listener.
             CookieWrapper cw = (CookieWrapper) cookie;
@@ -227,6 +228,8 @@
                     mCallerInfo = new CallerInfo().markAsVoiceMail();
                 } else {
                     mCallerInfo = CallerInfo.getCallerInfo(mQueryContext, mQueryUri, cursor);
+                    if (DBG) log("==> Got mCallerInfo: " + mCallerInfo);
+
                     // Use the number entered by the user for display.
                     if (!TextUtils.isEmpty(cw.number)) {
                         mCallerInfo.phoneNumber = PhoneNumberUtils.formatNumber(cw.number);
@@ -238,7 +241,7 @@
                 //notify that we can clean up the queue after this.
                 CookieWrapper endMarker = new CookieWrapper();
                 endMarker.event = EVENT_END_OF_QUEUE;
-                startQuery (token, endMarker, null, null, null, null, null);
+                startQuery(token, endMarker, null, null, null, null, null);
             }
 
             //notify the listener that the query is complete.
@@ -274,24 +277,82 @@
         cw.cookie = cookie;
         cw.event = EVENT_NEW_QUERY;
 
-        c.mHandler.startQuery (token, cw, contactRef, null, null, null, null);
+        c.mHandler.startQuery(token, cw, contactRef, null, null, null, null);
 
         return c;
     }
 
     /**
-     * Factory method to start query with a number
+     * Factory method to start the query based on a number.
+     *
+     * Note: if the number contains an "@" character we treat it
+     * as a SIP address, and look it up directly in the Data table
+     * rather than using the PhoneLookup table.
+     * TODO: But eventually we should expose two separate methods, one for
+     * numbers and one for SIP addresses, and then have
+     * PhoneUtils.startGetCallerInfo() decide which one to call based on
+     * the phone type of the incoming connection.
      */
     public static CallerInfoAsyncQuery startQuery(int token, Context context, String number,
             OnQueryCompleteListener listener, Object cookie) {
-        //construct the URI object and start Query.
-        Uri contactRef = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
+        if (DBG) {
+            log("##### CallerInfoAsyncQuery startQuery()... #####");
+            log("- number: " + number);
+            log("- cookie: " + cookie);
+        }
+
+        // Construct the URI object and query params, and start the query.
+
+        Uri contactRef;
+        String selection;
+        String[] selectionArgs;
+
+        if (PhoneNumberUtils.isUriNumber(number)) {
+            // "number" is really a SIP address.
+            if (DBG) log("  - Treating number as a SIP address: " + number);
+
+            // We look up SIP addresses directly in the Data table:
+            contactRef = Data.CONTENT_URI;
+
+            // Note Data.DATA1 and SipAddress.SIP_ADDRESS are equivalent.
+            //
+            // Also note we use "upper(data1)" in the WHERE clause, and
+            // uppercase the incoming SIP address, in order to do a
+            // case-insensitive match.
+            //
+            // TODO: need to confirm that the use of upper() doesn't
+            // prevent us from using the index!  (Linear scan of the whole
+            // contacts DB can be very slow.)
+            //
+            // TODO: May also need to normalize by adding "sip:" as a
+            // prefix, if we start storing SIP addresses that way in the
+            // database.
+
+            selection = "upper(" + Data.DATA1 + ")=?"
+                    + " AND "
+                    + Data.MIMETYPE + "='" + SipAddress.CONTENT_ITEM_TYPE + "'";
+            selectionArgs = new String[] { number.toUpperCase() };
+
+        } else {
+            // "number" is a regular phone number.  Use the PhoneLookup table:
+            contactRef = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
+            selection = null;
+            selectionArgs = null;
+        }
+
+        if (DBG) {
+            log("==> contactRef: " + contactRef);
+            log("==> selection: " + selection);
+            if (selectionArgs != null) {
+                for (int i = 0; i < selectionArgs.length; i++) {
+                    log("==> selectionArgs[" + i + "]: " + selectionArgs[i]);
+                }
+            }
+        }
 
         CallerInfoAsyncQuery c = new CallerInfoAsyncQuery();
         c.allocate(context, contactRef);
 
-        if (DBG) log("starting query for number: " + number + " handler: " + c.toString());
-
         //create cookieWrapper, start query
         CookieWrapper cw = new CookieWrapper();
         cw.listener = listener;
@@ -307,10 +368,15 @@
             cw.event = EVENT_NEW_QUERY;
         }
 
-        c.mHandler.startQuery (token, cw, contactRef, null, null, null, null);
-
+        c.mHandler.startQuery(token,
+                              cw,  // cookie
+                              contactRef,  // uri
+                              null,  // projection
+                              selection,  // selection
+                              selectionArgs,  // selectionArgs
+                              null);  // orderBy
         return c;
-   }
+    }
 
     /**
      * Method to add listeners to a currently running query
@@ -326,7 +392,7 @@
         cw.cookie = cookie;
         cw.event = EVENT_ADD_LISTENER;
 
-        mHandler.startQuery (token, cw, null, null, null, null, null);
+        mHandler.startQuery(token, cw, null, null, null, null, null);
     }
 
     /**
diff --git a/voip/java/android/net/rtp/AudioCodec.java b/voip/java/android/net/rtp/AudioCodec.java
index dfa6841..f171806 100644
--- a/voip/java/android/net/rtp/AudioCodec.java
+++ b/voip/java/android/net/rtp/AudioCodec.java
@@ -81,7 +81,7 @@
     public static final AudioCodec AMR = new AudioCodec(97, "AMR/8000", null);
 
     // TODO: add rest of the codecs when the native part is done.
-    private static final AudioCodec[] sCodecs = {GSM, PCMU, PCMA};
+    private static final AudioCodec[] sCodecs = {GSM_EFR, GSM, PCMU, PCMA};
 
     private AudioCodec(int type, String rtpmap, String fmtp) {
         this.type = type;
diff --git a/voip/jni/rtp/AmrCodec.cpp b/voip/jni/rtp/AmrCodec.cpp
new file mode 100644
index 0000000..9a2227d
--- /dev/null
+++ b/voip/jni/rtp/AmrCodec.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyrightm (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AudioCodec.h"
+
+#include "gsmamr_dec.h"
+#include "gsmamr_enc.h"
+
+namespace {
+
+class GsmEfrCodec : public AudioCodec
+{
+public:
+    GsmEfrCodec() {
+        if (AMREncodeInit(&mEncoder, &mSidSync, false)) {
+            mEncoder = NULL;
+        }
+        if (GSMInitDecode(&mDecoder, (Word8 *)"RTP")) {
+            mDecoder = NULL;
+        }
+    }
+
+    ~GsmEfrCodec() {
+        if (mEncoder) {
+            AMREncodeExit(&mEncoder, &mSidSync);
+        }
+        if (mDecoder) {
+            GSMDecodeFrameExit(&mDecoder);
+        }
+    }
+
+    int set(int sampleRate, const char *fmtp) {
+        return (sampleRate == 8000 && mEncoder && mDecoder) ? 160 : -1;
+    }
+
+    int encode(void *payload, int16_t *samples);
+    int decode(int16_t *samples, void *payload, int length);
+
+private:
+    void *mEncoder;
+    void *mSidSync;
+    void *mDecoder;
+};
+
+int GsmEfrCodec::encode(void *payload, int16_t *samples)
+{
+    unsigned char *bytes = (unsigned char *)payload;
+    Frame_Type_3GPP type;
+
+    int length = AMREncode(mEncoder, mSidSync, MR122,
+        samples, bytes, &type, AMR_TX_WMF);
+
+    if (type == AMR_122 && length == 32) {
+        bytes[0] = 0xC0 | (bytes[1] >> 4);
+        for (int i = 1; i < 31; ++i) {
+            bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4);
+        }
+        return 31;
+    }
+    return -1;
+}
+
+int GsmEfrCodec::decode(int16_t *samples, void *payload, int length)
+{
+    unsigned char *bytes = (unsigned char *)payload;
+    if (length == 31 && (bytes[0] >> 4) == 0x0C) {
+        for (int i = 0; i < 30; ++i) {
+            bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4);
+        }
+        bytes[30] <<= 4;
+
+        if (AMRDecode(mDecoder, AMR_122, bytes, samples, MIME_IETF) == 31) {
+            return 160;
+        }
+    }
+    return -1;
+}
+
+} // namespace
+
+AudioCodec *newGsmEfrCodec()
+{
+    return new GsmEfrCodec;
+}
diff --git a/voip/jni/rtp/Android.mk b/voip/jni/rtp/Android.mk
index 29683bd..5909c0d 100644
--- a/voip/jni/rtp/Android.mk
+++ b/voip/jni/rtp/Android.mk
@@ -27,6 +27,7 @@
 	rtp_jni.cpp
 
 LOCAL_SRC_FILES += \
+	AmrCodec.cpp \
 	G711Codec.cpp \
 	GsmCodec.cpp
 
@@ -34,13 +35,20 @@
 	libnativehelper \
 	libcutils \
 	libutils \
-	libmedia
+	libmedia \
+	libstagefright
 
 LOCAL_STATIC_LIBRARIES := libgsm
 
 LOCAL_C_INCLUDES += \
 	$(JNI_H_INCLUDE) \
-	external/libgsm/inc
+	external/libgsm/inc \
+	frameworks/base/media/libstagefright/codecs/amrnb/common/include \
+	frameworks/base/media/libstagefright/codecs/amrnb/common/ \
+	frameworks/base/media/libstagefright/codecs/amrnb/enc/include \
+	frameworks/base/media/libstagefright/codecs/amrnb/enc/src \
+	frameworks/base/media/libstagefright/codecs/amrnb/dec/include \
+	frameworks/base/media/libstagefright/codecs/amrnb/dec/src
 
 LOCAL_CFLAGS += -fvisibility=hidden
 
diff --git a/voip/jni/rtp/AudioCodec.cpp b/voip/jni/rtp/AudioCodec.cpp
index fc33ef2..afc193c 100644
--- a/voip/jni/rtp/AudioCodec.cpp
+++ b/voip/jni/rtp/AudioCodec.cpp
@@ -21,6 +21,7 @@
 extern AudioCodec *newAlawCodec();
 extern AudioCodec *newUlawCodec();
 extern AudioCodec *newGsmCodec();
+extern AudioCodec *newGsmEfrCodec();
 
 struct AudioCodecType {
     const char *name;
@@ -29,6 +30,7 @@
     {"PCMA", newAlawCodec},
     {"PCMU", newUlawCodec},
     {"GSM", newGsmCodec},
+    {"GSM-EFR", newGsmEfrCodec},
     {NULL, NULL},
 };
 
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index 72c882b..f09edb6 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -86,8 +86,6 @@
     void decode(int tick);
 
 private:
-    bool isNatAddress(struct sockaddr_storage *addr);
-
     enum {
         NORMAL = 0,
         SEND_ONLY = 1,
@@ -101,6 +99,7 @@
     AudioCodec *mCodec;
     uint32_t mCodecMagic;
     uint32_t mDtmfMagic;
+    bool mFixRemote;
 
     int mTick;
     int mSampleRate;
@@ -181,6 +180,20 @@
     if (codec) {
         mRemote = *remote;
         mCodec = codec;
+
+        // Here we should never get an private address, but some buggy proxy
+        // servers do give us one. To solve this, we replace the address when
+        // the first time we successfully decode an incoming packet.
+        mFixRemote = false;
+        if (remote->ss_family == AF_INET) {
+            unsigned char *address =
+                (unsigned char *)&((sockaddr_in *)remote)->sin_addr;
+            if (address[0] == 10 ||
+                (address[0] == 172 && (address[1] >> 4) == 1) ||
+                (address[0] == 192 && address[1] == 168)) {
+                mFixRemote = true;
+            }
+        }
     }
 
     LOGD("stream[%d] is configured as %s %dkHz %dms", mSocket,
@@ -318,16 +331,6 @@
         sizeof(mRemote));
 }
 
-bool AudioStream::isNatAddress(struct sockaddr_storage *addr) {
-    if (addr->ss_family != AF_INET) return false;
-    struct sockaddr_in *s4 = (struct sockaddr_in *)addr;
-    unsigned char *d = (unsigned char *) &s4->sin_addr;
-    if ((d[0] == 10)
-        || ((d[0] == 172) && (d[1] & 0x10))
-        || ((d[0] == 192) && (d[1] == 168))) return true;
-    return false;
-}
-
 void AudioStream::decode(int tick)
 {
     char c;
@@ -375,21 +378,11 @@
             MSG_TRUNC | MSG_DONTWAIT) >> 1;
     } else {
         __attribute__((aligned(4))) uint8_t buffer[2048];
-        struct sockaddr_storage src_addr;
-        socklen_t addrlen;
-        length = recvfrom(mSocket, buffer, sizeof(buffer),
-            MSG_TRUNC|MSG_DONTWAIT, (sockaddr*)&src_addr, &addrlen);
+        sockaddr_storage remote;
+        socklen_t len = sizeof(remote);
 
-        // The following if clause is for fixing the target address if
-        // proxy server did not replace the NAT address with its media
-        // port in SDP. Although it is proxy server's responsibility for
-        // replacing the connection address with correct one, we will change
-        // the target address as we detect the difference for now until we
-        // know the best way to get rid of this issue.
-        if ((memcmp((void*)&src_addr, (void*)&mRemote, addrlen) != 0) &&
-            isNatAddress(&mRemote)) {
-            memcpy((void*)&mRemote, (void*)&src_addr, addrlen);
-        }
+        length = recvfrom(mSocket, buffer, sizeof(buffer),
+            MSG_TRUNC | MSG_DONTWAIT, (sockaddr *)&remote, &len);
 
         // Do we need to check SSRC, sequence, and timestamp? They are not
         // reliable but at least they can be used to identify duplicates?
@@ -409,8 +402,12 @@
         if (length >= 0) {
             length = mCodec->decode(samples, &buffer[offset], length);
         }
+        if (length > 0 && mFixRemote) {
+            mRemote = remote;
+            mFixRemote = false;
+        }
     }
-    if (length != mSampleCount) {
+    if (length <= 0) {
         LOGD("stream[%d] decoder error", mSocket);
         return;
     }