Merge "Use the default SecureRandom provider." into gingerbread
diff --git a/api/current.xml b/api/current.xml
index 956f2b8..4a81b42 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -947,6 +947,17 @@
  visibility="public"
 >
 </field>
+<field name="SET_ALARM"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;com.android.alarm.permission.SET_ALARM&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SET_ALWAYS_FINISH"
  type="java.lang.String"
  transient="false"
@@ -131718,6 +131729,67 @@
 </package>
 <package name="android.provider"
 >
+<class name="AlarmClock"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AlarmClock"
+ type="android.provider.AlarmClock"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="ACTION_SET_ALARM"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.SET_ALARM&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_HOUR"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.alarm.HOUR&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_MESSAGE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.alarm.MESSAGE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_MINUTES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.alarm.MINUTES&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <interface name="BaseColumns"
  abstract="true"
  static="false"
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 8ab94ad..8b54871 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "stagefright"
+#include <media/stagefright/foundation/ADebug.h>
+
 #include <sys/time.h>
 
 #include <stdlib.h>
@@ -27,10 +31,11 @@
 #include <media/IMediaPlayerService.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include "include/ARTSPController.h"
+#include "include/LiveSource.h"
+#include "include/NuCachedSource2.h"
 #include <media/stagefright/AudioPlayer.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/JPEGSource.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaExtractor.h>
@@ -43,6 +48,8 @@
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MPEG4Writer.h>
 
+#include <fcntl.h>
+
 using namespace android;
 
 static long gNumRepetitions;
@@ -120,7 +127,7 @@
 
             bool shouldSeek = false;
             if (err == INFO_FORMAT_CHANGED) {
-                CHECK_EQ(buffer, NULL);
+                CHECK(buffer == NULL);
 
                 printf("format changed.\n");
                 continue;
@@ -206,7 +213,7 @@
             options.clearSeekTo();
 
             if (err != OK) {
-                CHECK_EQ(buffer, NULL);
+                CHECK(buffer == NULL);
 
                 if (err == INFO_FORMAT_CHANGED) {
                     printf("format changed.\n");
@@ -267,14 +274,115 @@
     }
 }
 
-static void writeSourceToMP4(const sp<MediaSource> &source) {
+////////////////////////////////////////////////////////////////////////////////
+
+struct DetectSyncSource : public MediaSource {
+    DetectSyncSource(const sp<MediaSource> &source);
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options);
+
+private:
+    enum StreamType {
+        AVC,
+        MPEG4,
+        H263,
+        OTHER,
+    };
+
+    sp<MediaSource> mSource;
+    StreamType mStreamType;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DetectSyncSource);
+};
+
+DetectSyncSource::DetectSyncSource(const sp<MediaSource> &source)
+    : mSource(source),
+      mStreamType(OTHER) {
+    const char *mime;
+    CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        mStreamType = AVC;
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)) {
+        mStreamType = MPEG4;
+        CHECK(!"sync frame detection not implemented yet for MPEG4");
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) {
+        mStreamType = H263;
+        CHECK(!"sync frame detection not implemented yet for H.263");
+    }
+}
+
+status_t DetectSyncSource::start(MetaData *params) {
+    return mSource->start(params);
+}
+
+status_t DetectSyncSource::stop() {
+    return mSource->stop();
+}
+
+sp<MetaData> DetectSyncSource::getFormat() {
+    return mSource->getFormat();
+}
+
+static bool isIDRFrame(MediaBuffer *buffer) {
+    const uint8_t *data =
+        (const uint8_t *)buffer->data() + buffer->range_offset();
+    size_t size = buffer->range_length();
+    for (size_t i = 0; i + 3 < size; ++i) {
+        if (!memcmp("\x00\x00\x01", &data[i], 3)) {
+            uint8_t nalType = data[i + 3] & 0x1f;
+            if (nalType == 5) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+status_t DetectSyncSource::read(
+        MediaBuffer **buffer, const ReadOptions *options) {
+    status_t err = mSource->read(buffer, options);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (mStreamType == AVC && isIDRFrame(*buffer)) {
+        (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true);
+    } else {
+        (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true);
+    }
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static void writeSourcesToMP4(
+        Vector<sp<MediaSource> > &sources, bool syncInfoPresent) {
     sp<MPEG4Writer> writer =
         new MPEG4Writer(gWriteMP4Filename.string());
 
-    CHECK_EQ(writer->addSource(source), OK);
+    // at most one minute.
+    writer->setMaxFileDuration(60000000ll);
+
+    for (size_t i = 0; i < sources.size(); ++i) {
+        sp<MediaSource> source = sources.editItemAt(i);
+
+        CHECK_EQ(writer->addSource(
+                    syncInfoPresent ? source : new DetectSyncSource(source)),
+                (status_t)OK);
+    }
 
     sp<MetaData> params = new MetaData;
-    CHECK_EQ(writer->start(), OK);
+    params->setInt32(kKeyNotRealTime, true);
+    CHECK_EQ(writer->start(params.get()), (status_t)OK);
 
     while (!writer->reachedEOS()) {
         usleep(100000);
@@ -283,7 +391,7 @@
 }
 
 static void performSeekTest(const sp<MediaSource> &source) {
-    CHECK_EQ(OK, source->start());
+    CHECK_EQ((status_t)OK, source->start());
 
     int64_t durationUs;
     CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs));
@@ -335,7 +443,7 @@
         }
     }
 
-    CHECK_EQ(OK, source->stop());
+    CHECK_EQ((status_t)OK, source->stop());
 }
 
 static void usage(const char *me) {
@@ -481,10 +589,10 @@
         for (int k = 0; k < argc; ++k) {
             const char *filename = argv[k];
 
-            CHECK_EQ(retriever->setDataSource(filename), OK);
+            CHECK_EQ(retriever->setDataSource(filename), (status_t)OK);
             CHECK_EQ(retriever->setMode(
                         METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL),
-                     OK);
+                     (status_t)OK);
 
             sp<IMemory> mem = retriever->captureFrame();
 
@@ -530,7 +638,7 @@
             Vector<CodecCapabilities> results;
             CHECK_EQ(QueryCodecs(omx, kMimeTypes[k],
                                  true, // queryDecoders
-                                 &results), OK);
+                                 &results), (status_t)OK);
 
             for (size_t i = 0; i < results.size(); ++i) {
                 printf("  decoder '%s' supports ",
@@ -579,12 +687,16 @@
     status_t err = client.connect();
 
     for (int k = 0; k < argc; ++k) {
+        bool syncInfoPresent = true;
+
         const char *filename = argv[k];
 
         sp<DataSource> dataSource = DataSource::CreateFromURI(filename);
 
-        if ((strncasecmp(filename, "sine:", 5)
-                && strncasecmp(filename, "rtsp://", 7)) && dataSource == NULL) {
+        if (strncasecmp(filename, "sine:", 5)
+                && strncasecmp(filename, "rtsp://", 7)
+                && strncasecmp(filename, "httplive://", 11)
+                && dataSource == NULL) {
             fprintf(stderr, "Unable to create data source.\n");
             return 1;
         }
@@ -596,10 +708,14 @@
             isJPEG = true;
         }
 
+        Vector<sp<MediaSource> > mediaSources;
         sp<MediaSource> mediaSource;
 
         if (isJPEG) {
             mediaSource = new JPEGSource(dataSource);
+            if (gWriteMP4) {
+                mediaSources.push(mediaSource);
+            }
         } else if (!strncasecmp("sine:", filename, 5)) {
             char *end;
             long sampleRate = strtol(filename + 5, &end, 10);
@@ -608,6 +724,9 @@
                 sampleRate = 44100;
             }
             mediaSource = new SineSource(sampleRate, 1);
+            if (gWriteMP4) {
+                mediaSources.push(mediaSource);
+            }
         } else {
             sp<MediaExtractor> extractor;
 
@@ -625,6 +744,20 @@
                 }
 
                 extractor = rtspController.get();
+
+                syncInfoPresent = false;
+            } else if (!strncasecmp("httplive://", filename, 11)) {
+                String8 uri("http://");
+                uri.append(filename + 11);
+
+                dataSource = new LiveSource(uri.string());
+                dataSource = new NuCachedSource2(dataSource);
+
+                extractor =
+                    MediaExtractor::Create(
+                            dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
+
+                syncInfoPresent = false;
             } else {
                 extractor = MediaExtractor::Create(dataSource);
                 if (extractor == NULL) {
@@ -635,46 +768,75 @@
 
             size_t numTracks = extractor->countTracks();
 
-            sp<MetaData> meta;
-            size_t i;
-            for (i = 0; i < numTracks; ++i) {
-                meta = extractor->getTrackMetaData(
-                        i, MediaExtractor::kIncludeExtensiveMetaData);
+            if (gWriteMP4) {
+                bool haveAudio = false;
+                bool haveVideo = false;
+                for (size_t i = 0; i < numTracks; ++i) {
+                    sp<MediaSource> source = extractor->getTrack(i);
 
-                const char *mime;
-                meta->findCString(kKeyMIMEType, &mime);
+                    const char *mime;
+                    CHECK(source->getFormat()->findCString(
+                                kKeyMIMEType, &mime));
 
-                if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
-                    break;
+                    bool useTrack = false;
+                    if (!haveAudio && !strncasecmp("audio/", mime, 6)) {
+                        haveAudio = true;
+                        useTrack = true;
+                    } else if (!haveVideo && !strncasecmp("video/", mime, 6)) {
+                        haveVideo = true;
+                        useTrack = true;
+                    }
+
+                    if (useTrack) {
+                        mediaSources.push(source);
+
+                        if (haveAudio && haveVideo) {
+                            break;
+                        }
+                    }
+                }
+            } else {
+                sp<MetaData> meta;
+                size_t i;
+                for (i = 0; i < numTracks; ++i) {
+                    meta = extractor->getTrackMetaData(
+                            i, MediaExtractor::kIncludeExtensiveMetaData);
+
+                    const char *mime;
+                    meta->findCString(kKeyMIMEType, &mime);
+
+                    if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
+                        break;
+                    }
+
+                    if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
+                        break;
+                    }
+
+                    meta = NULL;
                 }
 
-                if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
-                    break;
+                if (meta == NULL) {
+                    fprintf(stderr,
+                            "No suitable %s track found. The '-a' option will "
+                            "target audio tracks only, the default is to target "
+                            "video tracks only.\n",
+                            audioOnly ? "audio" : "video");
+                    return -1;
                 }
 
-                meta = NULL;
-            }
+                int64_t thumbTimeUs;
+                if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
+                    printf("thumbnailTime: %lld us (%.2f secs)\n",
+                           thumbTimeUs, thumbTimeUs / 1E6);
+                }
 
-            if (meta == NULL) {
-                fprintf(stderr,
-                        "No suitable %s track found. The '-a' option will "
-                        "target audio tracks only, the default is to target "
-                        "video tracks only.\n",
-                        audioOnly ? "audio" : "video");
-                return -1;
+                mediaSource = extractor->getTrack(i);
             }
-
-            int64_t thumbTimeUs;
-            if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
-                printf("thumbnailTime: %lld us (%.2f secs)\n",
-                       thumbTimeUs, thumbTimeUs / 1E6);
-            }
-
-            mediaSource = extractor->getTrack(i);
         }
 
         if (gWriteMP4) {
-            writeSourceToMP4(mediaSource);
+            writeSourcesToMP4(mediaSources, syncInfoPresent);
         } else if (seekTest) {
             performSeekTest(mediaSource);
         } else {
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index c5a3277..7803bf2 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -50,6 +50,12 @@
     private static boolean DBG = true;
     private static final String TAG = "NetworkStateTracker";
 
+    // Share the event space with ConnectivityService (which we can't see, but
+    // must send events to).  If you change these, change ConnectivityService
+    // too.
+    private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
+    private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
+
     public static final int EVENT_STATE_CHANGED = 1;
     public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2;
     /**
@@ -61,16 +67,6 @@
     public static final int EVENT_CONFIGURATION_CHANGED = 4;
     public static final int EVENT_ROAMING_CHANGED = 5;
     public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6;
-    public static final int EVENT_RESTORE_DEFAULT_NETWORK = 7;
-    /**
-     * arg1: network type
-     * arg2: condition (0 bad, 100 good)
-     */
-    public static final int EVENT_INET_CONDITION_CHANGE = 8;
-    /**
-     * arg1: network type
-     */
-    public static final int EVENT_INET_CONDITION_HOLD_END = 9;
 
     public NetworkStateTracker(Context context,
             Handler target,
diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java
new file mode 100644
index 0000000..b93dfd8
--- /dev/null
+++ b/core/java/android/provider/AlarmClock.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+
+/**
+ * The AlarmClock provider contains an Intent action and extras that can be used
+ * to start an Activity to set a new alarm in an alarm clock application.
+ *
+ * Applications that wish to receive the ACTION_SET_ALARM Intent should create
+ * an activity to handle the Intent that requires the permission
+ * com.android.alarm.permission.SET_ALARM.  Applications that wish to create a
+ * new alarm should use
+ * {@link android.content.Context#startActivity Context.startActivity()} so that
+ * the user has the option of choosing which alarm clock application to use.
+ */
+public final class AlarmClock {
+    /**
+     * Activity Action: Set an alarm.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
+
+    /**
+     * Activity Extra: Provide a custom message for the alarm.
+     * <p>
+     * This can be passed as an extra field in the Intent created with
+     * ACTION_SET_ALARM.
+     */
+    public static final String EXTRA_MESSAGE = "android.intent.extra.alarm.MESSAGE";
+
+    /**
+     * Activity Extra: The hour of the alarm being set.
+     * <p>
+     * This value can be passed as an extra field to the Intent created with
+     * ACTION_SET_ALARM.  If it is not provided, the behavior is undefined and
+     * is up to the application.  The value is an integer and ranges from 0 to
+     * 23.
+     */
+    public static final String EXTRA_HOUR = "android.intent.extra.alarm.HOUR";
+
+    /**
+     * Activity Extra: The minutes of the alarm being set.
+     * <p>
+     * This value can be passed as an extra field to the Intent created with
+     * ACTION_SET_ALARM.  If it is not provided, the behavior is undefined and
+     * is up to the application.  The value is an integer and ranges from 0 to
+     * 59.
+     */
+    public static final String EXTRA_MINUTES = "android.intent.extra.alarm.MINUTES";
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ab0ff3f..68a5a14 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -242,6 +242,14 @@
         android:description="@string/permdesc_writeHistoryBookmarks"
         android:protectionLevel="dangerous" />
 
+    <!-- Allows an application to broadcast an Intent to set an alarm for the
+         user. -->
+    <permission android:name="com.android.alarm.permission.SET_ALARM"
+        android:permissionGroup="android.permission-group.PERSONAL_INFO"
+        android:label="@string/permlab_setAlarm"
+        android:description="@string/permdesc_setAlarm"
+        android:protectionLevel="normal" />
+
     <!-- ======================================= -->
     <!-- Permissions for accessing location info -->
     <!-- ======================================= -->
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index dda1fc5..d58dedc 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Umožňuje aplikaci uvolnit paměť telefonu smazáním souborů v adresáři mezipaměti aplikace. Přístup je velmi omezený, většinou pouze pro systémové procesy."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Přesun zdrojů aplikace"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Umožňuje aplikaci přesunout své zdroje z interní paměti na externí médium a opačně."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"čtení systémových souborů protokolu"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Umožňuje aplikaci číst různé systémové soubory protokolů. Toto nastavení aplikaci umožní získat obecné informace o činnostech s telefonem, ale neměly by obsahovat žádné osobní či soukromé informace."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"čtení systémových souborů protokolu"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Umožňuje aplikaci číst různé systémové soubory protokolů. Toto nastavení aplikaci umožní získat obecné informace o činnostech s telefonem, ale neměly by obsahovat žádné osobní či soukromé informace."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"čtení nebo zápis do prostředků funkce diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Umožňuje aplikaci číst libovolné prostředky ve skupině diag, např. soubory ve složce /dev, a zapisovat do nich. Může dojít k ovlivnění stability a bezpečnosti systému. Toto nastavení by měl používat pouze výrobce či operátor pro diagnostiku hardwaru."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"povolení či zakázání komponent aplikací"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Práce"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Jiné"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Vlastní"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"pomocí <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> pomocí <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Zadejte kód PIN"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Vybrat vše"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Označit text"</string>
+    <string name="selectText" msgid="4862359311088898878">"Vybrat slovo"</string>
     <string name="cut" msgid="3092569408438626261">"Vyjmout"</string>
     <string name="copy" msgid="2681946229533511987">"Kopírovat"</string>
     <string name="paste" msgid="5629880836805036433">"Vložit"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 7b3e82c..3e6a63c 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Tillader, at et program frigør plads på telefonen ved at slette filer i programmets cachemappe. Adgang er normalt meget begrænset til systemprocesser."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Flyt programressourcer"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Tillader, at et program flytter programressourcer fra interne til eksterne medier og omvendt."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"læs systemlogfiler"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Tillader, at et program læser fra systemets forskellige logfiler. Dermed kan generelle oplysninger om, hvad du laver med telefonen, registreres, men logfilerne bør ikke indeholde personlige eller private oplysninger."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"læs systemlogfiler"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Tillader, at et program læser fra systemets forskellige logfiler. Dermed kan generelle oplysninger om, hvad du laver med telefonen, registreres, men logfilerne bør ikke indeholde personlige eller private oplysninger."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"læs/skriv til ressourcer ejet af diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Tillader, at et program læser og skriver til alle ressourcer, der ejes af diag-gruppen, som f.eks. flier i /dev. Dette kan muligvis påvirke systemets stabilitet og sikkerhed. Dette bør KUN bruges til hardwarespecifikke diagnosticeringer foretaget af producent eller udbyder."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"aktiver eller deaktiver programkomponenter"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Arbejde"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Andre"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Tilpasset"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Indtast PIN-kode"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Vælg alle"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Marker tekst"</string>
+    <string name="selectText" msgid="4862359311088898878">"Vælg ord"</string>
     <string name="cut" msgid="3092569408438626261">"Klip"</string>
     <string name="copy" msgid="2681946229533511987">"Kopier"</string>
     <string name="paste" msgid="5629880836805036433">"Indsæt"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 4c5cc8c..c0ac156 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Ermöglicht einer Anwendung, Telefonspeicher durch das Löschen von Dateien im Cache-Verzeichnis der Anwendung freizugeben. Der Zugriff beschränkt sich in der Regel auf Systemprozesse."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Anwendungsressourcen verschieben"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Ermöglicht einer Anwendung, Anwendungsressourcen von interne auf externe Medien zu verschieben und umgekehrt."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"System-Protokolldateien lesen"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Ermöglicht einer Anwendung, die verschiedenen Protokolldateien des Systems zu lesen. So können allgemeine Informationen zu den auf Ihrem Telefon durchgeführten Aktionen eingesehen werden, diese sollten jedoch keine persönlichen oder geheimen Daten enthalten."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"System-Protokolldateien lesen"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Ermöglicht einer Anwendung, die verschiedenen Protokolldateien des Systems zu lesen. So können allgemeine Informationen zu den auf Ihrem Telefon durchgeführten Aktionen eingesehen werden, diese sollten jedoch keine persönlichen oder geheimen Daten enthalten."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"Lese-/Schreibberechtigung für zu Diagnosegruppe gehörige Elemente"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Ermöglicht einer Anwendung, alle Elemente in der Diagnosegruppe zu lesen und zu bearbeiten, etwa Dateien in \"/dev\". Dies könnte eine potenzielle Gefährdung für die Stabilität und Sicherheit des Systems darstellen und sollte NUR für Hardware-spezifische Diagnosen des Herstellers oder Netzbetreibers verwendet werden."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"Anwendungskomponenten aktivieren oder deaktivieren"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Beruflich"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Andere"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Benutzerdefiniert"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"über <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> über <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN-Code eingeben"</string>
@@ -536,7 +544,7 @@
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Display gesperrt."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Drücken Sie die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Zum Entsperren die Menütaste drücken"</string>
-    <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Schema für Entsperrung zeichnen"</string>
+    <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Muster zum Entsperren zeichnen"</string>
     <string name="lockscreen_emergency_call" msgid="5347633784401285225">"Notruf"</string>
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Zurück zum Anruf"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Korrekt!"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Alles auswählen"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Text auswählen"</string>
+    <string name="selectText" msgid="4862359311088898878">"Wort auswählen"</string>
     <string name="cut" msgid="3092569408438626261">"Ausschneiden"</string>
     <string name="copy" msgid="2681946229533511987">"Kopieren"</string>
     <string name="paste" msgid="5629880836805036433">"Einfügen"</string>
@@ -788,7 +796,7 @@
     <string name="usb_storage_button_mount" msgid="1052259930369508235">"USB-Speicher aktivieren"</string>
     <string name="usb_storage_error_message" msgid="2534784751603345363">"Bei der Verwendung Ihrer SD-Karte als USB-Speicher ist ein Problem aufgetreten."</string>
     <string name="usb_storage_notification_title" msgid="8175892554757216525">"USB-Verbindung"</string>
-    <string name="usb_storage_notification_message" msgid="7380082404288219341">"Wählen Sie die Dateien aus, die von Ihrem oder auf Ihren Computer kopiert werden sollen."</string>
+    <string name="usb_storage_notification_message" msgid="7380082404288219341">"Zum Kopieren von Dateien zu/von Ihrem Computer."</string>
     <string name="usb_storage_stop_notification_title" msgid="2336058396663516017">"USB-Speicher deaktivieren"</string>
     <string name="usb_storage_stop_notification_message" msgid="2591813490269841539">"Auswählen, um USB-Speicher zu deaktivieren."</string>
     <string name="usb_storage_stop_title" msgid="660129851708775853">"USB-Speicher in Verwendung"</string>
@@ -809,7 +817,7 @@
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"Kandidaten"</u></string>
     <string name="ext_media_checking_notification_title" msgid="5457603418970994050">"SD-Karte wird vorbereitet"</string>
-    <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Nach Fehlern wird gesucht."</string>
+    <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Suche nach Fehlern"</string>
     <string name="ext_media_nofs_notification_title" msgid="780477838241212997">"SD-Karte leer"</string>
     <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"SD-Karte ist leer oder verfügt über ein nicht unterstütztes Dateisystem."</string>
     <string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"Beschädigte SD-Karte"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 45863e7..ca0eec7 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Επιτρέπει σε μια εφαρμογή να αυξήσει τον ελεύθερο χώρο αποθήκευσης του τηλεφώνου διαγράφοντας αρχεία από τον κατάλογο προσωρινής μνήμης της εφαρμογής. Η πρόσβαση είναι συνήθως πολύ περιορισμένη στη διαδικασία συστήματος."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Μετακίνηση πόρων εφαρμογής"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Επιτρέπει σε μια εφαρμογή τη μετακίνηση πόρων εφαρμογής από ένα εσωτερικό σε ένα εξωτερικό μέσο και αντίστροφα."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"ανάγνωση αρχείων καταγραφής συστήματος"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Επιτρέπει σε μια εφαρμογή να αναγνώσει τα αρχεία καταγραφής του συστήματος. Έτσι μπορεί να ανακαλύψει γενικές πληροφορίες σχετικά με τις δραστηριότητές σας στο τηλέφωνο, όμως δεν θα πρέπει να περιέχουν προσωπικές ή ιδιωτικές πληροφορίες."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"ανάγνωση αρχείων καταγραφής συστήματος"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Επιτρέπει σε μια εφαρμογή να αναγνώσει τα αρχεία καταγραφής του συστήματος. Έτσι μπορεί να ανακαλύψει γενικές πληροφορίες σχετικά με τις δραστηριότητές σας στο τηλέφωνο, όμως δεν θα πρέπει να περιέχουν προσωπικές ή ιδιωτικές πληροφορίες."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"ανάγνωση/εγγραφή σε πόρους που ανήκουν στο διαγνωστικό"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Επιτρέπει σε μια εφαρμογή την ανάγνωση και την εγγραφή σε πόρο που ανήκει στην ομάδα διαγνωστικού (π.χ. αρχεία στον κατάλογο /dev). Αυτό ενδέχεται να επηρεάσει την σταθερότητα και την ασφάλεια του συστήματος. Θα πρέπει να χρησιμοποιείται ΜΟΝΟ για διαγνωστικά υλικού του κατασκευαστή ή του χειριστή."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"ενεργοποίηση ή απενεργοποίηση στοιχείων εφαρμογής"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Εργασία"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Άλλο"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Προσαρμοσμένο"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"μέσω <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> μέσω <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Πληκτρολογήστε τον κωδικό αριθμό PIN"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Επιλογή όλων"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Επιλογή κειμένου"</string>
+    <string name="selectText" msgid="4862359311088898878">"Επιλογή λέξης"</string>
     <string name="cut" msgid="3092569408438626261">"Αποκοπή"</string>
     <string name="copy" msgid="2681946229533511987">"Αντιγραφή"</string>
     <string name="paste" msgid="5629880836805036433">"Επικόλληση"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 2765e96..b0da3753 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Admite una aplicación que libera espacio de almacenamiento en el teléfono al eliminar archivos del directorio de memoria caché de la aplicación. En general, el acceso es muy restringido para el proceso del sistema."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos de la aplicación"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite a una aplicación mover recursos de aplicación de medios internos a externos y viceversa."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"leer archivos de registro del sistema"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Admite una aplicación que lee diversos archivos de registro del sistema. Esto le permite descubrir información general sobre lo que haces con el teléfono, pero no debe contener información personal ni privada."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"leer archivos de registro del sistema"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Admite una aplicación que lee diversos archivos de registro del sistema. Esto le permite descubrir información general sobre lo que haces con el teléfono, pero no debe contener información personal ni privada."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"leer y escribir a recursos dentro del grupo de diagnóstico"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Admite una aplicación que lee y escribe a cualquier recurso dentro del grupo de diagnóstico; por ejemplo, archivos con /dev. Esto puede afectar potencialmente la estabilidad y la seguridad del sistema. Debe utilizarlo SÓLO el fabricante o el operador en los diagnósticos específicos del hardware."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"activar o desactivar componentes de la aplicación"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Trabajo"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Otro"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Personalizado"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"a través de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> a través de <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Ingresar el código de PIN"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Seleccionar todos"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Seleccionar texto"</string>
+    <string name="selectText" msgid="4862359311088898878">"Seleccionar palabra"</string>
     <string name="cut" msgid="3092569408438626261">"Cortar"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
     <string name="paste" msgid="5629880836805036433">"Pegar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 0d2f932..74f5874 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permite que una aplicación libere espacio de almacenamiento en el teléfono mediante la eliminación de archivos en el directorio de caché de la aplicación. El acceso al proceso del sistema suele estar muy restringido."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos de aplicaciones"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite que una aplicación mueva los recursos de aplicaciones de un medio interno a otro externo y viceversa."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"leer archivos de registro del sistema"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Permite que una aplicación lea los distintos archivos de registro del sistema. Con este permiso, la aplicación puede ver información general sobre las acciones que realiza el usuario con el teléfono, pero los registros no deberían contener información personal o privada."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"leer archivos de registro del sistema"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Permite que una aplicación lea los distintos archivos de registro del sistema. Con este permiso, la aplicación puede ver información general sobre las acciones que realiza el usuario con el teléfono, pero los registros no deberían contener información personal o privada."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"leer/escribir en los recursos propiedad del grupo de diagnóstico"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Permite que una aplicación lea y escriba en cualquier recurso propiedad del grupo de diagnóstico como, por ejemplo, archivos in/dev. Este permiso podría afectar a la seguridad y estabilidad del sistema. SÓLO se debe utilizar para diagnósticos específicos de hardware realizados por el fabricante o el operador."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"habilitar o inhabilitar componentes de la aplicación"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Trabajo"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Otra"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Personalizada"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"a través de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> a través de <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Introduce el código PIN"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Seleccionar todo"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Seleccionar texto"</string>
+    <string name="selectText" msgid="4862359311088898878">"Seleccionar palabra"</string>
     <string name="cut" msgid="3092569408438626261">"Cortar"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
     <string name="paste" msgid="5629880836805036433">"Pegar"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index b4f8046..bc69793 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permet à une application de libérer de l\'espace dans la mémoire du téléphone en supprimant des fichiers du répertoire du cache des applications. Cet accès est en général limité aux processus système."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Déplacer des ressources d\'application"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Autorise l\'application à déplacer des ressources d\'application d\'un support interne à un support externe et inversement."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"Lecture des fichiers journaux du système"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Permet à une application de lire les différents fichiers journaux du système afin d\'obtenir des informations générales sur la façon dont vous utilisez votre téléphone,  sans pour autant récupérer des informations d\'ordre personnel ou privé."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"Lecture des fichiers journaux du système"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Permet à une application de lire les différents fichiers journaux du système afin d\'obtenir des informations générales sur la façon dont vous utilisez votre téléphone,  sans pour autant récupérer des informations d\'ordre personnel ou privé."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"Lecture/écriture dans les ressources appartenant aux diagnostics"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Permet à une application de lire et d\'éditer toute ressource appartenant au groupe de diagnostics (par exemple, les fichiers in/dev). Ceci peut affecter la stabilité et la sécurité du système. Cette fonctionnalité est UNIQUEMENT réservée aux diagnostics matériels effectués par le fabricant ou l\'opérateur."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"Activer ou désactiver des éléments de l\'application"</string>
@@ -434,7 +434,7 @@
     <string name="policydesc_wipeData" msgid="2314060933796396205">"Rétablit les paramètres d\'usine, supprimant toutes vos données sans demande de confirmation."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Domicile"</item>
-    <item msgid="869923650527136615">"Portable"</item>
+    <item msgid="869923650527136615">"Mobile"</item>
     <item msgid="7897544654242874543">"Bureau"</item>
     <item msgid="1103601433382158155">"Télécopie bureau"</item>
     <item msgid="1735177144948329370">"Télécopie domicile"</item>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Bureau"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Autre"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Personnalisé"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Saisissez le code PIN"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Tout sélectionner"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Sélectionner le texte"</string>
+    <string name="selectText" msgid="4862359311088898878">"Sélectionner texte"</string>
     <string name="cut" msgid="3092569408438626261">"Couper"</string>
     <string name="copy" msgid="2681946229533511987">"Copier"</string>
     <string name="paste" msgid="5629880836805036433">"Coller"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index d4019b6..85415bd 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Consente a un\'applicazione di liberare spazio sul telefono eliminando file nella directory della cache dell\'applicazione. L\'accesso è generalmente limitato a processi di sistema."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Spostare risorse dell\'applicazione"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Consente a un\'applicazione di spostare risorse applicative da supporti interni a esterni e viceversa."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"lettura file di registro sistema"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Consente a un\'applicazione di leggere vari file di registro del sistema per trovare informazioni generali sulle operazioni effettuate con il telefono. Tali file non dovrebbero contenere informazioni personali o riservate."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"lettura file di registro sistema"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Consente a un\'applicazione di leggere vari file di registro del sistema per trovare informazioni generali sulle operazioni effettuate con il telefono. Tali file non dovrebbero contenere informazioni personali o riservate."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"lettura/scrittura risorse di proprietà di diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Consente a un\'applicazione di leggere le risorse del gruppo diag e scrivere a esse, per esempio i file in /dev. Questa capacità potrebbe influire sulla stabilità e sicurezza del sistema. Dovrebbe essere utilizzata SOLTANTO per diagnostiche specifiche dell\'hardware effettuate dal produttore o dall\'operatore."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"attivazione/disattivazione componenti applicazioni"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Lavoro"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Altro"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Personalizzato"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"tramite <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> tramite <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Inserisci il PIN"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Seleziona tutto"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Seleziona testo"</string>
+    <string name="selectText" msgid="4862359311088898878">"Seleziona parola"</string>
     <string name="cut" msgid="3092569408438626261">"Taglia"</string>
     <string name="copy" msgid="2681946229533511987">"Copia"</string>
     <string name="paste" msgid="5629880836805036433">"Incolla"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index ae44bb2..70a3019 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"アプリケーションのキャッシュディレクトリからファイルを削除して携帯電話のメモリを解放することをアプリケーションに許可します。通常、アクセスはシステムプロセスのみに制限されます。"</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"アプリケーションリソースの移動"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"内部と外部のメディア間でのアプリケーションリソースの移動をアプリケーションに許可します。"</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"システムログファイルの読み取り"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"システムのさまざまなログファイルの読み取りをアプリケーションに許可します。これにより携帯電話の使用状況に関する全般情報が取得されますが、個人情報や非公開情報が含まれることはありません。"</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"システムログファイルの読み取り"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"システムのさまざまなログファイルの読み取りをアプリケーションに許可します。これにより携帯電話の使用状況に関する全般情報が取得されますが、個人情報や非公開情報が含まれることはありません。"</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"diagが所有するリソースの読み書き"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"diagグループが所有するリソース(例:/dev内のファイル)への読み書きをアプリケーションに許可します。システムの安定性とセキュリティに影響する恐れがあります。メーカー/オペレーターによるハードウェア固有の診断以外には使用しないでください。"</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"アプリケーションのコンポーネントを有効/無効にする"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"勤務先"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"その他"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"カスタム"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"<xliff:g id="SOURCE">%1$s</xliff:g>経由"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g>、更新元: <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PINコードを入力"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"すべて選択"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"テキストを選択"</string>
+    <string name="selectText" msgid="4862359311088898878">"語句を選択"</string>
     <string name="cut" msgid="3092569408438626261">"切り取り"</string>
     <string name="copy" msgid="2681946229533511987">"コピー"</string>
     <string name="paste" msgid="5629880836805036433">"貼り付け"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 040f9e8..0f33772 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"애플리케이션이 애플리케이션 캐시 디렉토리에 있는 파일을 삭제하여 휴대전화의 저장공간을 늘릴 수 있도록 합니다. 액세스는 일반적으로 시스템 프로세스로 제한됩니다."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"애플리케이션 리소스 이동"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"애플리케이션이 애플리케이션 리소스를 내부에서 외부 미디어로 또는 그 반대로 이동할 수 있도록 합니다."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"시스템 로그 파일 읽기"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"애플리케이션이 시스템의 다양한 로그 파일을 읽을 수 있도록 합니다. 이 경우 애플리케이션은 사용자가 휴대전화로 수행하는 작업에 대한 일반적인 정보를 검색할 수 있습니다. 하지만 로그 파일에 어떠한 개인정보도 포함되어서는 안 됩니다."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"시스템 로그 파일 읽기"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"애플리케이션이 시스템의 다양한 로그 파일을 읽을 수 있도록 합니다. 이 경우 애플리케이션은 사용자가 휴대전화로 수행하는 작업에 대한 일반적인 정보를 검색할 수 있습니다. 하지만 로그 파일에 어떠한 개인정보도 포함되어서는 안 됩니다."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"진단 그룹 소유의 리소스 읽기/쓰기"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"애플리케이션이 진단 그룹 소유의 리소스(예: /dev에 있는 파일)를 읽고 쓸 수 있도록 합니다. 이 기능은 시스템 안정성 및 보안에 영향을 미칠 수 있으므로 제조업체 또는 사업자가 하드웨어 관련 진단을 수행하는 경우에만 사용해야 합니다."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"애플리케이션 구성 요소 사용 또는 사용 안함"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"직장"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"기타"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"맞춤설정"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"<xliff:g id="SOURCE">%1$s</xliff:g>을(를) 통해"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g>(<xliff:g id="SOURCE">%2$s</xliff:g> 사용)"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN 코드 입력"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"모두 선택"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"텍스트 선택"</string>
+    <string name="selectText" msgid="4862359311088898878">"단어 선택"</string>
     <string name="cut" msgid="3092569408438626261">"잘라내기"</string>
     <string name="copy" msgid="2681946229533511987">"복사"</string>
     <string name="paste" msgid="5629880836805036433">"붙여넣기"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 4ca84ed..b3727b4 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Lar applikasjonen frigjøre lagringsplass ved å slette filer i applikasjoners hurtigbufferkatalog. Tilgangen er vanligvis sterkt begrenset, til systemprosesser."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Flytter programressurser"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Gir et program tillatelse til å flytte programressurser fra interne til eksterne medier og omvendt."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"lese systemets loggfiler"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Lar applikasjonen lese fra diverse loggfiler på systemet. Disse inneholder generell informasjon om hva som gjøres med telefonen, men skal ikke inneholde personlig eller privat informasjon."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"lese systemets loggfiler"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Lar applikasjonen lese fra diverse loggfiler på systemet. Disse inneholder generell informasjon om hva som gjøres med telefonen, men skal ikke inneholde personlig eller privat informasjon."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"lese/skrive ressurser eid av diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Lar applikasjonen lese og skrive enhver ressurs eid av gruppen diag; for eksempel, filer i /dev. Dette kan potensielt påvirke systemets sikkerhet og stabilitet. Dette bør KUN brukes for maskinvarespesifikke diagnoseverktøy laget av operatøren eller produsenten."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"aktivere eller deaktigere applikasjonskomponenter"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Arbeid"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Annen"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Egendefinert"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Skriv inn PIN-kode:"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Merk alt"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Merk tekst"</string>
+    <string name="selectText" msgid="4862359311088898878">"Velg ord"</string>
     <string name="cut" msgid="3092569408438626261">"Klipp ut"</string>
     <string name="copy" msgid="2681946229533511987">"Kopier"</string>
     <string name="paste" msgid="5629880836805036433">"Lim inn"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c8f00689..9c5d6bf 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Hiermee kan een toepassing opslagruimte op de telefoon vrij maken door bestanden te verwijderen uit de cachemap van de toepassing. De toegang is doorgaans beperkt tot het systeemproces."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Toepassingsbronnen verplaatsen"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Een toepassing toestaan toepassingsbronnen te verplaatsen van interne naar externe media en omgekeerd."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"systeemlogbestanden lezen"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Hiermee kan een toepassing de verschillende logbestanden van het systeem lezen. De toepassing kan op deze manier algemene informatie achterhalen over uw telefoongebruik. Hierin is geen persoonlijke of privé-informatie opgenomen."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"systeemlogbestanden lezen"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Hiermee kan een toepassing de verschillende logbestanden van het systeem lezen. De toepassing kan op deze manier algemene informatie achterhalen over uw telefoongebruik. Hierin is geen persoonlijke of privé-informatie opgenomen."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"lezen/schrijven naar bronnen van diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Hiermee kan een toepassing lezen en schrijven naar elke bron die hoort bij de diagnostische groep, zoals bestanden in /dev. Hierdoor kan de systeemstabiliteit en -veiligheid worden beïnvloed. Dit mag ALLEEN worden gebruikt voor hardwarespecifieke diagnostiek door de fabrikant of operator."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"toepassingscomponenten in- of uitschakelen"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Werk"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Overig"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Aangepast"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN-code invoeren"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Alles selecteren"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Tekst selecteren"</string>
+    <string name="selectText" msgid="4862359311088898878">"Woord selecteren"</string>
     <string name="cut" msgid="3092569408438626261">"Knippen"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiëren"</string>
     <string name="paste" msgid="5629880836805036433">"Plakken"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ae897a8..f61d1d6 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Pozwala aplikacji na zwalnianie pamięci telefonu przez usuwanie plików z katalogu pamięci podręcznej aplikacji. Dostęp jest bardzo ograniczony, z reguły do procesów systemu."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Przenoszenie zasobów aplikacji"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Zezwala aplikacji na przeniesienie zasobów aplikacji z nośnika wewnętrznego na zewnętrzny i odwrotnie."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"czytanie plików dziennika systemu"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Umożliwia aplikacji czytanie różnych plików dziennika systemowego. Pozwala to na uzyskanie ogólnych informacji o czynnościach wykonywanych w telefonie, ale bez ujawniania danych osobowych lub osobistych informacji."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"czytanie plików dziennika systemu"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Umożliwia aplikacji czytanie różnych plików dziennika systemowego. Pozwala to na uzyskanie ogólnych informacji o czynnościach wykonywanych w telefonie, ale bez ujawniania danych osobowych lub osobistych informacji."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"czytanie/zapisywanie w zasobach należących do diagnostyki"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Pozwala aplikacji na czytanie i zapisywanie we wszystkich zasobach posiadanych przez diagnozowaną grupę, jak na przykład pliki w katalogu /dev. Może to potencjalnie wpłynąć na stabilność i bezpieczeństwo systemu. Powinno być wykorzystywane TYLKO w celach diagnozowania sprzętu przez producenta lub operatora."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"włączanie lub wyłączanie składników aplikacji"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Służbowy"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Inny"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Niestandardowy"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"przez <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> przez <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Wprowadź kod PIN"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Zaznacz wszystko"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Zaznacz tekst"</string>
+    <string name="selectText" msgid="4862359311088898878">"Zaznacz wyraz"</string>
     <string name="cut" msgid="3092569408438626261">"Wytnij"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiuj"</string>
     <string name="paste" msgid="5629880836805036433">"Wklej"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index b9f9be2..5b44e61 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permite a uma aplicação libertar espaço de armazenamento no telefone eliminando ficheiros no directório da cache da aplicação. Geralmente, o acesso é muito limitado para processamento do sistema."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos de aplicações"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite que uma aplicação mova recursos de aplicações de meios internos para meios externos e vice-versa."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"ler ficheiros de registo do sistema"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Permite a uma aplicação ler a partir dos diversos ficheiros de registo do sistema. Isto permite descobrir informações gerais sobre a forma como o utilizador usa o telefone, mas estas não devem conter quaisquer dados pessoais ou privados."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"ler ficheiros de registo do sistema"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Permite a uma aplicação ler a partir dos diversos ficheiros de registo do sistema. Isto permite descobrir informações gerais sobre a forma como o utilizador usa o telefone, mas estas não devem conter quaisquer dados pessoais ou privados."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"ler/escrever em recursos propriedade de diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Permite a uma aplicação ler e escrever em qualquer recurso que seja propriedade do grupo diag. Por exemplo, ficheiros em /dev. Isto pode afectar potencialmente a estabilidade e a segurança do sistema e deve ser utilizado APENAS para diagnósticos específicos do hardware pelo fabricante ou pelo operador."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"activar ou desactivar componentes da aplicação"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Emprego"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Outro"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Personalizado"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"através do <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> através de <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Introduzir código PIN"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Seleccionar tudo"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Seleccionar texto"</string>
+    <string name="selectText" msgid="4862359311088898878">"Seleccionar palavra"</string>
     <string name="cut" msgid="3092569408438626261">"Cortar"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
     <string name="paste" msgid="5629880836805036433">"Colar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 7cc266a..60e9108 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permite que um aplicativo libere o espaço de armazenamento do telefone excluindo arquivos no diretório de cache do aplicativo. O acesso é normalmente muito restrito para o processo do sistema."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos do aplicativo"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite que um aplicativo mova os recursos do aplicativo da mídia interna para a externa e vice-versa."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"ler arquivos de registro do sistema"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Permite que um aplicativo leia os diversos arquivos de registro do sistema. Isso permite que ele descubra informações gerais sobre o que você está fazendo com o telefone, porém esses arquivos não devem conter informações pessoais ou privadas."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"ler arquivos de registro do sistema"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Permite que um aplicativo leia os diversos arquivos de registro do sistema. Isso permite que ele descubra informações gerais sobre o que você está fazendo com o telefone, porém esses arquivos não devem conter informações pessoais ou privadas."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"ler/gravar em recursos pertencentes ao diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Permite que um aplicativo leia e grave em qualquer recurso que pertença ao grupo de diagnósticos; por exemplo, arquivos em /dev. Isso possivelmente pode afetar a estabilidade e a segurança do sistema. Isso deve ser usado APENAS para diagnósticos específicos do hardware realizados pelo fabricante ou pelo operador."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"ativar ou desativar os componentes do aplicativo"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Comercial"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Outros"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Personalizado"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"por meio de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Digite o código PIN"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Selecionar tudo"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Selecionar texto"</string>
+    <string name="selectText" msgid="4862359311088898878">"Selecionar palavra"</string>
     <string name="cut" msgid="3092569408438626261">"Recortar"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
     <string name="paste" msgid="5629880836805036433">"Colar"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 29b7e46..9475d2d 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Позволяет приложению освобождать память телефона с помощью удаления файлов из каталога кэша приложений. Обычно это разрешается только системным процессам."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Перемещать ресурсы приложения"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Позволяет приложению перемещать ресурсы приложения с внутренних на внешние носители и наоборот."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"считывать системные файлы журналов"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Позволяет приложению считывать информацию из различных журналов системы. Приложение может получить сведения о работе пользователя с телефоном, но они не должны содержать какой-либо личной или конфиденциальной информации."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"считывать системные файлы журналов"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Позволяет приложению считывать информацию из различных журналов системы. Приложение может получить сведения о работе пользователя с телефоном, но они не должны содержать какой-либо личной или конфиденциальной информации."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"считывать/записывать данные в ресурсы, принадлежащие группе диагностики"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Позволяет приложению считывать и записывать данные в любые ресурсы, принадлежащие группе диагностики (например, файлы в каталоге /dev). Это может повлиять на стабильность и безопасность системы. Эта возможность может быть использована ТОЛЬКО производителем или оператором для диагностики аппаратного обеспечения."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"включать или отключать компоненты приложения"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Работа"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Другое"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Создать свой ярлык"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"с помощью <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> с помощью <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Введите PIN-код"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Выбрать все"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Выбрать текст"</string>
+    <string name="selectText" msgid="4862359311088898878">"Выберите слово"</string>
     <string name="cut" msgid="3092569408438626261">"Вырезать"</string>
     <string name="copy" msgid="2681946229533511987">"Копировать"</string>
     <string name="paste" msgid="5629880836805036433">"Вставить"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index b53f219..085cc29 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Tillåter att ett program frigör lagringsutrymme i telefonen genom att ta bort filer i programmets katalog för cachelagring. Åtkomst är mycket begränsad, vanligtvis till systemprocesser."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Flytta programresurser"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Tillåter att ett program flyttar programresurser från interna till externa medier och tvärt om."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"läsa systemets loggfiler"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Tillåter att ett program läser från systemets olika loggfiler. Det innebär att programmet kan upptäcka allmän information om vad du gör med telefonen, men den bör inte innehålla personlig eller privat information."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"läsa systemets loggfiler"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Tillåter att ett program läser från systemets olika loggfiler. Det innebär att programmet kan upptäcka allmän information om vad du gör med telefonen, men den bör inte innehålla personlig eller privat information."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"läsa/skriva till resurser som ägs av diag"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Tillåter att ett program läser och skriver till en resurs som ägs av diag-gruppen; till exempel filer i /dev. Detta kan eventuellt påverka systemets stabilitet och säkerhet. Detta bör ENDAST används av tillverkaren eller operatören för maskinvaruspecifik diagnostik."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"aktivera eller inaktivera programkomponenter"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"Arbete"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Övrigt"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Anpassad"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Ange PIN-kod"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Välj alla"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Markera text"</string>
+    <string name="selectText" msgid="4862359311088898878">"Välj ord"</string>
     <string name="cut" msgid="3092569408438626261">"Klipp ut"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiera"</string>
     <string name="paste" msgid="5629880836805036433">"Klistra in"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index ea46db0..6efa8e0 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Uygulamaların uygulama önbelleği dizinindeki dosyaları silerek telefonda yer açmasına izin verir. Erişim genellikle sistem işlemlerine ve yüksek düzeyde kısıtlı olarak verilir."</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"Uygulama kaynaklarını taşı"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"Bir uygulamanın, uygulama kaynaklarını dahili ve harici ortamlar arasında taşımasına olanak tanır."</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"sistem günlük dosyalarını oku"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"Uygulamaların sistemin çeşitli günlük dosyalarından okumalarına izin verir. Bu, uygulamaların telefon ile neler yaptığınız ile ilgili genel bilgi bulmasına izin verir, ancak bunlar kişisel veya özel bir bilgi içermemelidir."</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"sistem günlük dosyalarını oku"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"Uygulamaların sistemin çeşitli günlük dosyalarından okumalarına izin verir. Bu, uygulamaların telefon ile neler yaptığınız ile ilgili genel bilgi bulmasına izin verir, ancak bunlar kişisel veya özel bir bilgi içermemelidir."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"sahibi tanılama olan kaynakları oku/bunlara yaz"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"Uygulamanın tanılama grubundaki bir kaynağa ait herhangi bir kaynağı; örneğin /dev içindeki dosyaları okumasına ve bunlara yazmasına izin verir. Bu işlevin sistem kararlılığını ve güvenliğini olumsuz etkileme olasılığı vardır. Üretici veya operatör tarafından YALNIZCA donanıma özgü tanılama için kullanılmalıdır."</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"uygulama bileşenlerini etkinleştir veya devre dışı bırak"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"İş"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Diğer"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"Özel"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"<xliff:g id="SOURCE">%1$s</xliff:g> aracılığıyla"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="SOURCE">%2$s</xliff:g> ile <xliff:g id="DATE">%1$s</xliff:g> tarihinde"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN kodunu gir"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Tümünü seç"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"Metin seç"</string>
+    <string name="selectText" msgid="4862359311088898878">"Kelime seçin"</string>
     <string name="cut" msgid="3092569408438626261">"Kes"</string>
     <string name="copy" msgid="2681946229533511987">"Kopyala"</string>
     <string name="paste" msgid="5629880836805036433">"Yapıştır"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index b963612..c8f2893 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"允许应用程序通过删除应用程序缓存目录中的文件释放手机存储空间。通常此权限只适用于系统进程。"</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"移动应用程序资源"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"允许应用程序在内部介质和外部介质之间移动应用程序资源。"</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"读取系统日志文件"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"允许应用程序从系统的各日志文件中读取信息。这样应用程序可以发现您的手机使用情况,但这些信息不应包含任何个人信息或保密信息。"</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"读取系统日志文件"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"允许应用程序从系统的各日志文件中读取信息。这样应用程序可以发现您的手机使用情况,但这些信息不应包含任何个人信息或保密信息。"</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"读取/写入诊断所拥有的资源"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"允许应用程序读取/写入诊断组所拥有的任何资源(例如,/dev 中的文件)。这可能会影响系统稳定性和安全性。此权限仅供制造商或运营商诊断硬件问题。"</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"启用或停用应用程序组件"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"公司"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"其他"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"自定义"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"通过 <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"时间:<xliff:g id="DATE">%1$s</xliff:g>,方式:<xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"输入 PIN 码"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"全选"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"选择文字"</string>
+    <string name="selectText" msgid="4862359311088898878">"选择文字"</string>
     <string name="cut" msgid="3092569408438626261">"剪切"</string>
     <string name="copy" msgid="2681946229533511987">"复制"</string>
     <string name="paste" msgid="5629880836805036433">"粘贴"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 939510c..fcfa464 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -266,8 +266,8 @@
     <string name="permdesc_clearAppCache" msgid="7740465694193671402">"允許應用程式刪除快取目錄裡的檔案,釋放儲存空間。此操作通常受到系統程序嚴格限制。"</string>
     <string name="permlab_movePackage" msgid="728454979946503926">"移動應用程式資源"</string>
     <string name="permdesc_movePackage" msgid="6323049291923925277">"允許應用程式將應用程式資源從內部媒體移到外部媒體,反之亦可。"</string>
-    <string name="permlab_readLogs" msgid="4811921703882532070">"讀取系統記錄檔"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"允許應用程式讀取系統記錄檔。此項操作可讓應用程式了解目前手機操作狀態,但內容應不含任何個人或隱私資訊。"</string>
+    <!-- outdated translation 4811921703882532070 -->     <string name="permlab_readLogs" msgid="6615778543198967614">"讀取系統記錄檔"</string>
+    <!-- outdated translation 2257937955580475902 -->     <string name="permdesc_readLogs" msgid="8896449437464867766">"允許應用程式讀取系統記錄檔。此項操作可讓應用程式了解目前手機操作狀態,但內容應不含任何個人或隱私資訊。"</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"讀寫 diag 擁有的資源"</string>
     <string name="permdesc_diagnostic" msgid="3121238373951637049">"允許應用程式讀寫 diag 群組的資源;例如:/dev 裡的檔案。這可能會影響系統穩定性與安全性。此功能僅供製造商或技術人員用於硬體規格偵測。"</string>
     <string name="permlab_changeComponentState" msgid="79425198834329406">"啟用或停用應用程式元件"</string>
@@ -525,6 +525,14 @@
     <string name="orgTypeWork" msgid="29268870505363872">"公司"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"其他"</string>
     <string name="orgTypeCustom" msgid="225523415372088322">"自訂"</string>
+    <!-- no translation found for sipAddressTypeCustom (2473580593111590945) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeHome (6093598181069359295) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeWork (6920725730797099047) -->
+    <skip />
+    <!-- no translation found for sipAddressTypeOther (4408436162950119849) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="5112589886094402795">"透過 <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g>透過「<xliff:g id="SOURCE">%2$s</xliff:g>」"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"輸入 PIN 碼"</string>
@@ -699,7 +707,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"全部選取"</string>
-    <!-- outdated translation 3889149123626888637 -->     <string name="selectText" msgid="4862359311088898878">"選取文字"</string>
+    <string name="selectText" msgid="4862359311088898878">"選取字詞"</string>
     <string name="cut" msgid="3092569408438626261">"剪下"</string>
     <string name="copy" msgid="2681946229533511987">"複製"</string>
     <string name="paste" msgid="5629880836805036433">"貼上"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 808b371..ebccfb6 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1619,6 +1619,15 @@
 
     <!-- Title of an application permission, listed so the user can choose whether
         they want to allow the application to do this. -->
+    <string name="permlab_setAlarm">set alarm in alarm clock</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_setAlarm">Allows the application to set an alarm in
+      an installed alarm clock application. Some alarm clock applications may
+      not implement this feature.</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_writeGeolocationPermissions">Modify Browser geolocation permissions</string>
     <!-- Description of an application permission, listed so the user can choose whether
         they want to allow the application to do this. -->
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 6bcaaaf..1fd7bba 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -44,5 +44,5 @@
         <name>monaco</name>
     </font>
     <fallback ttf="DroidSansFallback" />
-    <fallback ttf="DroidSansJapanese" />
-</fonts>
\ No newline at end of file
+    <fallback ttf="MTLmr3m" />
+</fonts>
diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h
index 97d31f4..9668bde 100644
--- a/include/utils/ZipFileRO.h
+++ b/include/utils/ZipFileRO.h
@@ -24,8 +24,9 @@
 #ifndef __LIBS_ZIPFILERO_H
 #define __LIBS_ZIPFILERO_H
 
-#include "Errors.h"
-#include "FileMap.h"
+#include <utils/Errors.h>
+#include <utils/FileMap.h>
+#include <utils/threads.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -211,6 +212,9 @@
     /* open Zip archive */
     int         mFd;
 
+    /* Lock for handling the file descriptor (seeks, etc) */
+    mutable Mutex mFdLock;
+
     /* zip file name */
     char*       mFileName;
 
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index 2d53136..9fcae72 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -22,6 +22,7 @@
 #include <utils/ZipFileRO.h>
 #include <utils/Log.h>
 #include <utils/misc.h>
+#include <utils/threads.h>
 
 #include <zlib.h>
 
@@ -195,7 +196,7 @@
             free(scanBuf);
             return false;
         } else if (header != kLFHSignature) {
-            LOGV("Not a Zip archive (found 0x%08x)\n", val);
+            LOGV("Not a Zip archive (found 0x%08x)\n", header);
             free(scanBuf);
             return false;
         }
@@ -496,15 +497,21 @@
         }
 
         unsigned char lfhBuf[kLFHLen];
-        if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
-            LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
-            return false;
-        }
-        ssize_t actual =
-            TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
-        if (actual != sizeof(lfhBuf)) {
-            LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
-            return false;
+
+        {
+            AutoMutex _l(mFdLock);
+
+            if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
+                LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
+                return false;
+            }
+
+            ssize_t actual =
+                TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
+            if (actual != sizeof(lfhBuf)) {
+                LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
+                return false;
+            }
         }
 
         if (get4LE(lfhBuf) != kLFHSignature) {
@@ -636,7 +643,7 @@
         memcpy(buffer, ptr, uncompLen);
     } else {
         if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
-            goto bail;
+            goto unmap;
     }
 
     if (compLen > kSequentialMin)
@@ -644,6 +651,8 @@
 
     result = true;
 
+unmap:
+    file->release();
 bail:
     return result;
 }
@@ -667,7 +676,7 @@
 
     getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
 
-    const FileMap* file = createEntryFileMap(entry);
+    FileMap* file = createEntryFileMap(entry);
     if (file == NULL) {
         goto bail;
     }
@@ -678,21 +687,23 @@
         ssize_t actual = write(fd, ptr, uncompLen);
         if (actual < 0) {
             LOGE("Write failed: %s\n", strerror(errno));
-            goto bail;
+            goto unmap;
         } else if ((size_t) actual != uncompLen) {
             LOGE("Partial write during uncompress (%zd of %zd)\n",
                 (size_t)actual, (size_t)uncompLen);
-            goto bail;
+            goto unmap;
         } else {
             LOGI("+++ successful write\n");
         }
     } else {
         if (!inflateBuffer(fd, ptr, uncompLen, compLen))
-            goto bail;
+            goto unmap;
     }
 
     result = true;
 
+unmap:
+    file->release();
 bail:
     return result;
 }
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 0437263..2d1a278 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -428,19 +428,14 @@
 
 // ----------------------------------------------------------------------------
 
-static void gl_no_context() {
+static int gl_no_context() {
     tls_t* tls = getTLS();
     if (tls->logCallWithNoContext == EGL_TRUE) {
         tls->logCallWithNoContext = EGL_FALSE;
         LOGE("call to OpenGL ES API with no current context "
              "(logged once per thread)");
     }
-}
-
-// Always return GL_INVALID_OPERATION from glGetError() when called from
-// a thread without a bound context.
-static GLenum gl_no_context_glGetError() {
-    return GL_INVALID_OPERATION;
+    return 0;
 }
 
 static void early_egl_init(void) 
@@ -454,8 +449,6 @@
             addr, 
             sizeof(gHooksNoContext));
 
-    gHooksNoContext.gl.glGetError = gl_no_context_glGetError;
-
     setGlThreadSpecific(&gHooksNoContext);
 }
 
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 924737e..18dd483 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -58,6 +58,7 @@
             "ldr   r12, [r12, %[tls]] \n"                       \
             "cmp   r12, #0            \n"                       \
             "ldrne pc,  [r12, %[api]] \n"                       \
+            "mov   r0, #0             \n"                       \
             "bx    lr                 \n"                       \
             :                                                   \
             : [tls] "J"(TLS_SLOT_OPENGL_API*4),                 \
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index d71ff76..ee29f12 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -114,6 +114,7 @@
             "ldr   r12, [r12, %[tls]] \n"                       \
             "cmp   r12, #0            \n"                       \
             "ldrne pc,  [r12, %[api]] \n"                       \
+            "mov   r0, #0             \n"                       \
             "bx    lr                 \n"                       \
             :                                                   \
             : [tls] "J"(TLS_SLOT_OPENGL_API*4),                 \
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 567b270..384f527 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -286,7 +286,8 @@
     // (See Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR.)
     int mIncallPowerBehavior;
 
-    int mLandscapeRotation = -1;
+    int mLandscapeRotation = -1; // default landscape rotation
+    int mSeascapeRotation = -1; // "other" landscape rotation, 180 degrees from mLandscapeRotation
     int mPortraitRotation = -1;
 
     // Nothing to see here, move along...
@@ -356,9 +357,12 @@
             return true;
         }
         // The user preference says we can rotate, and the app is willing to rotate.
+        // Note we include SCREEN_ORIENTATION_LANDSCAPE since we can use the sensor to choose
+        // between the two possible landscape rotations.
         if (mAccelerometerDefault != 0 &&
                 (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER
-                 || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) {
+                 || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+                 || appOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)) {
             return true;
         }
         // We're in a dock that has a rotation affinity, an the app is willing to rotate.
@@ -367,7 +371,8 @@
             // Note we override the nosensor flag here.
             if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER
                     || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
-                    || appOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+                    || appOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
+                    || appOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
                 return true;
             }
         }
@@ -2053,20 +2058,20 @@
             if (d.getWidth() > d.getHeight()) {
                 mPortraitRotation = Surface.ROTATION_90;
                 mLandscapeRotation = Surface.ROTATION_0;
+                mSeascapeRotation = Surface.ROTATION_180;
             } else {
                 mPortraitRotation = Surface.ROTATION_0;
                 mLandscapeRotation = Surface.ROTATION_90;
+                mSeascapeRotation = Surface.ROTATION_270;
             }
         }
 
         synchronized (mLock) {
-            switch (orientation) {
-                case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
-                    //always return landscape if orientation set to landscape
-                    return mLandscapeRotation;
-                case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
-                    //always return portrait if orientation set to portrait
-                    return mPortraitRotation;
+            if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
+                //always return portrait if orientation set to portrait
+                return mPortraitRotation;
+            } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
+                return getCurrentLandscapeRotation(lastRotation);
             }
             // case for nosensor meaning ignore sensor and consider only lid
             // or orientation sensor disabled
@@ -2086,6 +2091,26 @@
         }
     }
 
+    private int getCurrentLandscapeRotation(int lastRotation) {
+        // landscape-only apps can take either landscape rotation
+        if (useSensorForOrientationLp(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)) {
+            int sensorRotation = mOrientationListener.getCurrentRotation(lastRotation);
+            if (isLandscapeOrSeascape(sensorRotation)) {
+                return sensorRotation;
+            }
+        }
+        // try to preserve the old rotation if it was landscape
+        if (isLandscapeOrSeascape(lastRotation)) {
+            return lastRotation;
+        }
+        // default to one of the two landscape rotations
+        return mLandscapeRotation;
+    }
+
+    private boolean isLandscapeOrSeascape(int sensorRotation) {
+        return sensorRotation == mLandscapeRotation || sensorRotation == mSeascapeRotation;
+    }
+
     public boolean detectSafeMode() {
         try {
             int menuState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_MENU);
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 56de765..8a732ed 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -4653,29 +4653,44 @@
         goto Exit;
     }
 
+    // check audio settings permission for global effects
+    if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && !settingsAllowed()) {
+        lStatus = PERMISSION_DENIED;
+        goto Exit;
+    }
+
+    // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
+    // that can only be created by audio policy manager (running in same process)
+    if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE && getpid() != pid) {
+        lStatus = PERMISSION_DENIED;
+        goto Exit;
+    }
+
+    // check recording permission for visualizer
+    if ((memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 ||
+         memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) &&
+        !recordingAllowed()) {
+        lStatus = PERMISSION_DENIED;
+        goto Exit;
+    }
+
+    if (output == 0) {
+        if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE) {
+            // output must be specified by AudioPolicyManager when using session
+            // AudioSystem::SESSION_OUTPUT_STAGE
+            lStatus = BAD_VALUE;
+            goto Exit;
+        } else if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
+            // if the output returned by getOutputForEffect() is removed before we lock the
+            // mutex below, the call to checkPlaybackThread_l(output) below will detect it
+            // and we will exit safely
+            output = AudioSystem::getOutputForEffect(&desc);
+        }
+    }
+
     {
         Mutex::Autolock _l(mLock);
 
-        // check audio settings permission for global effects
-        if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && !settingsAllowed()) {
-            lStatus = PERMISSION_DENIED;
-            goto Exit;
-        }
-
-        // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
-        // that can only be created by audio policy manager (running in same process)
-        if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE && getpid() != pid) {
-            lStatus = PERMISSION_DENIED;
-            goto Exit;
-        }
-
-        // check recording permission for visualizer
-        if ((memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 ||
-             memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) &&
-            !recordingAllowed()) {
-            lStatus = PERMISSION_DENIED;
-            goto Exit;
-        }
 
         if (!EffectIsNullUuid(&pDesc->uuid)) {
             // if uuid is specified, request effect descriptor
@@ -4744,32 +4759,24 @@
 
         // If output is not specified try to find a matching audio session ID in one of the
         // output threads.
-        // TODO: allow attachment of effect to inputs
+        // If output is 0 here, sessionId is neither SESSION_OUTPUT_STAGE nor SESSION_OUTPUT_MIX
+        // because of code checking output when entering the function.
         if (output == 0) {
-            if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE) {
-                // output must be specified by AudioPolicyManager when using session
-                // AudioSystem::SESSION_OUTPUT_STAGE
-                lStatus = BAD_VALUE;
-                goto Exit;
-            } else if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
-                output = AudioSystem::getOutputForEffect(&desc);
-                LOGV("createEffect() got output %d for effect %s", output, desc.name);
-            } else {
-                 // look for the thread where the specified audio session is present
-                for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
-                    if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) {
-                        output = mPlaybackThreads.keyAt(i);
-                        break;
-                    }
-                }
-                // If no output thread contains the requested session ID, default to
-                // first output. The effect chain will be moved to the correct output
-                // thread when a track with the same session ID is created
-                if (output == 0 && mPlaybackThreads.size()) {
-                    output = mPlaybackThreads.keyAt(0);
+             // look for the thread where the specified audio session is present
+            for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+                if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) {
+                    output = mPlaybackThreads.keyAt(i);
+                    break;
                 }
             }
+            // If no output thread contains the requested session ID, default to
+            // first output. The effect chain will be moved to the correct output
+            // thread when a track with the same session ID is created
+            if (output == 0 && mPlaybackThreads.size()) {
+                output = mPlaybackThreads.keyAt(0);
+            }
         }
+        LOGV("createEffect() got output %d for effect %s", output, desc.name);
         PlaybackThread *thread = checkPlaybackThread_l(output);
         if (thread == NULL) {
             LOGE("createEffect() unknown output thread");
@@ -4777,6 +4784,8 @@
             goto Exit;
         }
 
+        // TODO: allow attachment of effect to inputs
+
         wclient = mClients.valueFor(pid);
 
         if (wclient != NULL) {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index f38748b..b8b9e53 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -101,6 +101,64 @@
     private boolean mTestMode;
     private static ConnectivityService sServiceInstance;
 
+    private static final int ENABLED  = 1;
+    private static final int DISABLED = 0;
+
+    // Share the event space with NetworkStateTracker (which can't see this
+    // internal class but sends us events).  If you change these, change
+    // NetworkStateTracker.java too.
+    private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
+    private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
+
+    /**
+     * used internally as a delayed event to make us switch back to the
+     * default network
+     */
+    private static final int EVENT_RESTORE_DEFAULT_NETWORK =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 1;
+
+    /**
+     * used internally to change our mobile data enabled flag
+     */
+    private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 2;
+
+    /**
+     * used internally to change our network preference setting
+     * arg1 = networkType to prefer
+     */
+    private static final int EVENT_SET_NETWORK_PREFERENCE =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 3;
+
+    /**
+     * used internally to synchronize inet condition reports
+     * arg1 = networkType
+     * arg2 = condition (0 bad, 100 good)
+     */
+    private static final int EVENT_INET_CONDITION_CHANGE =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 4;
+
+    /**
+     * used internally to mark the end of inet condition hold periods
+     * arg1 = networkType
+     */
+    private static final int EVENT_INET_CONDITION_HOLD_END =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 5;
+
+    /**
+     * used internally to set the background data preference
+     * arg1 = TRUE for enabled, FALSE for disabled
+     */
+    private static final int EVENT_SET_BACKGROUND_DATA =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 6;
+
+    /**
+     * used internally to set enable/disable cellular data
+     * arg1 = ENBALED or DISABLED
+     */
+    private static final int EVENT_SET_MOBILE_DATA =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 7;
+
     private Handler mHandler;
 
     // list of DeathRecipients used to make sure features are turned off when
@@ -344,28 +402,34 @@
      * Sets the preferred network.
      * @param preference the new preference
      */
-    public synchronized void setNetworkPreference(int preference) {
+    public void setNetworkPreference(int preference) {
         enforceChangePermission();
-        if (ConnectivityManager.isNetworkTypeValid(preference) &&
-                mNetAttributes[preference] != null &&
-                mNetAttributes[preference].isDefault()) {
-            if (mNetworkPreference != preference) {
-                persistNetworkPreference(preference);
-                mNetworkPreference = preference;
-                enforcePreference();
-            }
-        }
+
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0));
     }
 
     public int getNetworkPreference() {
         enforceAccessPermission();
-        return mNetworkPreference;
+        int preference;
+        synchronized(this) {
+            preference = mNetworkPreference;
+        }
+        return preference;
     }
 
-    private void persistNetworkPreference(int networkPreference) {
-        final ContentResolver cr = mContext.getContentResolver();
-        Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE,
-                networkPreference);
+    private void handleSetNetworkPreference(int preference) {
+        if (ConnectivityManager.isNetworkTypeValid(preference) &&
+                mNetAttributes[preference] != null &&
+                mNetAttributes[preference].isDefault()) {
+            if (mNetworkPreference != preference) {
+                final ContentResolver cr = mContext.getContentResolver();
+                Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, preference);
+                synchronized(this) {
+                    mNetworkPreference = preference;
+                }
+                enforcePreference();
+            }
+        }
     }
 
     private int getPersistedNetworkPreference() {
@@ -586,8 +650,7 @@
                         mNetRequestersPids[usedNetworkType].add(currentPid);
                     }
                 }
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(
-                        NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK,
                         f), getRestoreDefaultNetworkDelay());
 
 
@@ -613,8 +676,7 @@
                 synchronized(this) {
                     mFeatureUsers.add(f);
                 }
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(
-                        NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK,
                         f), getRestoreDefaultNetworkDelay());
 
                 return network.startUsingNetworkFeature(feature,
@@ -784,15 +846,18 @@
                 android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
                 "ConnectivityService");
 
-        if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_BACKGROUND_DATA,
+                (allowBackgroundDataUsage ? ENABLED : DISABLED), 0));
+    }
 
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.BACKGROUND_DATA,
-                allowBackgroundDataUsage ? 1 : 0);
-
-        Intent broadcast = new Intent(
-                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
-        mContext.sendBroadcast(broadcast);
+    private void handleSetBackgroundData(boolean enabled) {
+        if (enabled != getBackgroundDataSetting()) {
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.BACKGROUND_DATA, enabled ? 1 : 0);
+            Intent broadcast = new Intent(
+                    ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
+            mContext.sendBroadcast(broadcast);
+        }
     }
 
     /**
@@ -809,10 +874,15 @@
     /**
      * @see ConnectivityManager#setMobileDataEnabled(boolean)
      */
-    public synchronized void setMobileDataEnabled(boolean enabled) {
+    public void setMobileDataEnabled(boolean enabled) {
         enforceChangePermission();
         if (DBG) Slog.d(TAG, "setMobileDataEnabled(" + enabled + ")");
 
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
+            (enabled ? ENABLED : DISABLED), 0));
+    }
+
+    private void handleSetMobileData(boolean enabled) {
         if (getMobileDataEnabled() == enabled) return;
 
         Settings.Secure.putInt(mContext.getContentResolver(),
@@ -820,7 +890,9 @@
 
         if (enabled) {
             if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
-                if (DBG) Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
+                if (DBG) {
+                    Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
+                }
                 mNetTrackers[ConnectivityManager.TYPE_MOBILE].reconnect();
             }
         } else {
@@ -1486,74 +1558,42 @@
                 case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
                     // fill me in
                     break;
-                case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK:
+                case EVENT_RESTORE_DEFAULT_NETWORK:
                     FeatureUser u = (FeatureUser)msg.obj;
                     u.expire();
                     break;
-                case NetworkStateTracker.EVENT_INET_CONDITION_CHANGE:
-                    if (DBG) {
-                        Slog.d(TAG, "Inet connectivity change, net=" +
-                                msg.arg1 + ", condition=" + msg.arg2 +
-                                ",mActiveDefaultNetwork=" + mActiveDefaultNetwork);
-                    }
-                    if (mActiveDefaultNetwork == -1) {
-                        if (DBG) Slog.d(TAG, "no active default network - aborting");
-                        break;
-                    }
-                    if (mActiveDefaultNetwork != msg.arg1) {
-                        if (DBG) Slog.d(TAG, "given net not default - aborting");
-                        break;
-                    }
-                    mDefaultInetCondition = msg.arg2;
-                    int delay;
-                    if (mInetConditionChangeInFlight == false) {
-                        if (DBG) Slog.d(TAG, "starting a change hold");
-                        // setup a new hold to debounce this
-                        if (mDefaultInetCondition > 50) {
-                            delay = Settings.Secure.getInt(mContext.getContentResolver(),
-                                    Settings.Secure.INET_CONDITION_DEBOUNCE_UP_DELAY, 500);
-                        } else {
-                            delay = Settings.Secure.getInt(mContext.getContentResolver(),
-                                    Settings.Secure.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000);
-                        }
-                        mInetConditionChangeInFlight = true;
-                        sendMessageDelayed(obtainMessage(
-                                NetworkStateTracker.EVENT_INET_CONDITION_HOLD_END,
-                                mActiveDefaultNetwork, mDefaultConnectionSequence), delay);
-                    } else {
-                        // we've set the new condition, when this hold ends that will get
-                        // picked up
-                        if (DBG) Slog.d(TAG, "currently in hold - not setting new end evt");
-                    }
+                case EVENT_INET_CONDITION_CHANGE:
+                {
+                    int netType = msg.arg1;
+                    int condition = msg.arg2;
+                    handleInetConditionChange(netType, condition);
                     break;
-                case NetworkStateTracker.EVENT_INET_CONDITION_HOLD_END:
-                    if (DBG) {
-                        Slog.d(TAG, "Inet hold end, net=" + msg.arg1 +
-                                ", condition =" + mDefaultInetCondition +
-                                ", published condition =" + mDefaultInetConditionPublished);
-                    }
-                    mInetConditionChangeInFlight = false;
-
-                    if (mActiveDefaultNetwork == -1) {
-                        if (DBG) Slog.d(TAG, "no active default network - aborting");
-                        break;
-                    }
-                    if (mDefaultConnectionSequence != msg.arg2) {
-                        if (DBG) Slog.d(TAG, "event hold for obsolete network - aborting");
-                        break;
-                    }
-                    if (mDefaultInetConditionPublished == mDefaultInetCondition) {
-                        if (DBG) Slog.d(TAG, "no change in condition - aborting");
-                        break;
-                    }
-                    NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
-                    if (networkInfo.isConnected() == false) {
-                        if (DBG) Slog.d(TAG, "default network not connected - aborting");
-                        break;
-                    }
-                    mDefaultInetConditionPublished = mDefaultInetCondition;
-                    sendInetConditionBroadcast(networkInfo);
+                }
+                case EVENT_INET_CONDITION_HOLD_END:
+                {
+                    int netType = msg.arg1;
+                    int sequence = msg.arg2;
+                    handleInetConditionHoldEnd(netType, sequence);
                     break;
+                }
+                case EVENT_SET_NETWORK_PREFERENCE:
+                {
+                    int preference = msg.arg1;
+                    handleSetNetworkPreference(preference);
+                    break;
+                }
+                case EVENT_SET_BACKGROUND_DATA:
+                {
+                    boolean enabled = (msg.arg1 == ENABLED);
+                    handleSetBackgroundData(enabled);
+                    break;
+                }
+                case EVENT_SET_MOBILE_DATA:
+                {
+                    boolean enabled = (msg.arg1 == ENABLED);
+                    handleSetMobileData(enabled);
+                    break;
+                }
             }
         }
     }
@@ -1657,6 +1697,72 @@
             }
         }
         mHandler.sendMessage(mHandler.obtainMessage(
-            NetworkStateTracker.EVENT_INET_CONDITION_CHANGE, networkType, percentage));
+            EVENT_INET_CONDITION_CHANGE, networkType, percentage));
+    }
+
+    private void handleInetConditionChange(int netType, int condition) {
+        if (DBG) {
+            Slog.d(TAG, "Inet connectivity change, net=" +
+                    netType + ", condition=" + condition +
+                    ",mActiveDefaultNetwork=" + mActiveDefaultNetwork);
+        }
+        if (mActiveDefaultNetwork == -1) {
+            if (DBG) Slog.d(TAG, "no active default network - aborting");
+            return;
+        }
+        if (mActiveDefaultNetwork != netType) {
+            if (DBG) Slog.d(TAG, "given net not default - aborting");
+            return;
+        }
+        mDefaultInetCondition = condition;
+        int delay;
+        if (mInetConditionChangeInFlight == false) {
+            if (DBG) Slog.d(TAG, "starting a change hold");
+            // setup a new hold to debounce this
+            if (mDefaultInetCondition > 50) {
+                delay = Settings.Secure.getInt(mContext.getContentResolver(),
+                        Settings.Secure.INET_CONDITION_DEBOUNCE_UP_DELAY, 500);
+            } else {
+                delay = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000);
+            }
+            mInetConditionChangeInFlight = true;
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_INET_CONDITION_HOLD_END,
+                    mActiveDefaultNetwork, mDefaultConnectionSequence), delay);
+        } else {
+            // we've set the new condition, when this hold ends that will get
+            // picked up
+            if (DBG) Slog.d(TAG, "currently in hold - not setting new end evt");
+        }
+    }
+
+    private void handleInetConditionHoldEnd(int netType, int sequence) {
+        if (DBG) {
+            Slog.d(TAG, "Inet hold end, net=" + netType +
+                    ", condition =" + mDefaultInetCondition +
+                    ", published condition =" + mDefaultInetConditionPublished);
+        }
+        mInetConditionChangeInFlight = false;
+
+        if (mActiveDefaultNetwork == -1) {
+            if (DBG) Slog.d(TAG, "no active default network - aborting");
+            return;
+        }
+        if (mDefaultConnectionSequence != sequence) {
+            if (DBG) Slog.d(TAG, "event hold for obsolete network - aborting");
+            return;
+        }
+        if (mDefaultInetConditionPublished == mDefaultInetCondition) {
+            if (DBG) Slog.d(TAG, "no change in condition - aborting");
+            return;
+        }
+        NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
+        if (networkInfo.isConnected() == false) {
+            if (DBG) Slog.d(TAG, "default network not connected - aborting");
+            return;
+        }
+        mDefaultInetConditionPublished = mDefaultInetCondition;
+        sendInetConditionBroadcast(networkInfo);
+        return;
     }
 }
diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java
index f1dcd5a..3f43e1c 100644
--- a/services/java/com/android/server/sip/SipService.java
+++ b/services/java/com/android/server/sip/SipService.java
@@ -30,8 +30,8 @@
 import android.net.sip.SipErrorCode;
 import android.net.sip.SipManager;
 import android.net.sip.SipProfile;
+import android.net.sip.SipSession;
 import android.net.sip.SipSessionAdapter;
-import android.net.sip.SipSessionState;
 import android.net.wifi.WifiManager;
 import android.os.Binder;
 import android.os.Bundle;
@@ -143,7 +143,7 @@
     }
 
     private void openToReceiveCalls(SipProfile localProfile) {
-        open3(localProfile, SipManager.SIP_INCOMING_CALL_ACTION, null);
+        open3(localProfile, SipManager.ACTION_SIP_INCOMING_CALL, null);
     }
 
     public synchronized void open3(SipProfile localProfile,
@@ -255,15 +255,15 @@
 
     private void notifyProfileAdded(SipProfile localProfile) {
         if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
-        Intent intent = new Intent(SipManager.SIP_ADD_PHONE_ACTION);
-        intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString());
+        Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
+        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
         mContext.sendBroadcast(intent);
     }
 
     private void notifyProfileRemoved(SipProfile localProfile) {
         if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
-        Intent intent = new Intent(SipManager.SIP_REMOVE_PHONE_ACTION);
-        intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString());
+        Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
+        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
         mContext.sendBroadcast(intent);
     }
 
@@ -474,8 +474,8 @@
                     // send out incoming call broadcast
                     addPendingSession(session);
                     Intent intent = SipManager.createIncomingCallBroadcast(
-                            mIncomingCallBroadcastAction, session.getCallId(),
-                            sessionDescription);
+                            session.getCallId(), sessionDescription)
+                            .setAction(mIncomingCallBroadcastAction);
                     if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
                             + caller.getUri() + ": " + session.getCallId()
                             + " " + mIncomingCallBroadcastAction);
@@ -613,10 +613,10 @@
 
                 try {
                     int state = (mSession == null)
-                            ? SipSessionState.READY_TO_CALL
+                            ? SipSession.State.READY_TO_CALL
                             : mSession.getState();
-                    if ((state == SipSessionState.REGISTERING)
-                            || (state == SipSessionState.DEREGISTERING)) {
+                    if ((state == SipSession.State.REGISTERING)
+                            || (state == SipSession.State.DEREGISTERING)) {
                         mProxy.onRegistering(mSession);
                     } else if (mRegistered) {
                         int duration = (int)
@@ -1138,7 +1138,8 @@
                 event.mTriggerTime += event.mPeriod;
 
                 // run the callback in a new thread to prevent deadlock
-                new Thread(event.mCallback).start();
+                new Thread(event.mCallback, "SipServiceTimerCallbackThread")
+                        .start();
             }
             if (DEBUG_TIMER) {
                 Log.d(TAG, "after timeout execution");
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java
index 03a076c..66a2c05 100644
--- a/services/java/com/android/server/sip/SipSessionGroup.java
+++ b/services/java/com/android/server/sip/SipSessionGroup.java
@@ -25,11 +25,10 @@
 
 import android.net.sip.ISipSession;
 import android.net.sip.ISipSessionListener;
-import android.net.sip.SessionDescription;
 import android.net.sip.SipErrorCode;
 import android.net.sip.SipProfile;
+import android.net.sip.SipSession;
 import android.net.sip.SipSessionAdapter;
-import android.net.sip.SipSessionState;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -121,7 +120,7 @@
         reset(localIp);
     }
 
-    void reset(String localIp) throws SipException, IOException {
+    synchronized void reset(String localIp) throws SipException, IOException {
         mLocalIp = localIp;
         if (localIp == null) return;
 
@@ -301,7 +300,7 @@
             boolean processed = (session != null) && session.process(event);
             if (isLoggable && processed) {
                 Log.d(TAG, "new state after: "
-                        + SipSessionState.toString(session.mState));
+                        + SipSession.State.toString(session.mState));
             }
         } catch (Throwable e) {
             Log.w(TAG, "event process error: " + event, e);
@@ -332,7 +331,7 @@
 
         public boolean process(EventObject evt) throws SipException {
             if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
-                    + SipSessionState.toString(mState) + ": processing "
+                    + SipSession.State.toString(mState) + ": processing "
                     + log(evt));
             if (isRequestEvent(Request.INVITE, evt)) {
                 RequestEvent event = (RequestEvent) evt;
@@ -342,7 +341,7 @@
                 newSession.mDialog = newSession.mServerTransaction.getDialog();
                 newSession.mInviteReceived = event;
                 newSession.mPeerProfile = createPeerProfile(event.getRequest());
-                newSession.mState = SipSessionState.INCOMING_CALL;
+                newSession.mState = SipSession.State.INCOMING_CALL;
                 newSession.mPeerSessionDescription =
                         extractContent(event.getRequest());
                 addSipSession(newSession);
@@ -361,7 +360,7 @@
     class SipSessionImpl extends ISipSession.Stub {
         SipProfile mPeerProfile;
         SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
-        int mState = SipSessionState.READY_TO_CALL;
+        int mState = SipSession.State.READY_TO_CALL;
         RequestEvent mInviteReceived;
         Dialog mDialog;
         ServerTransaction mServerTransaction;
@@ -381,7 +380,7 @@
                         sleep(timeout);
                         if (mRunning) timeout();
                     }
-                }).start();
+                }, "SipSessionTimerThread").start();
             }
 
             synchronized void cancel() {
@@ -416,7 +415,7 @@
             mInCall = false;
             removeSipSession(this);
             mPeerProfile = null;
-            mState = SipSessionState.READY_TO_CALL;
+            mState = SipSession.State.READY_TO_CALL;
             mInviteReceived = null;
             mDialog = null;
             mServerTransaction = null;
@@ -473,7 +472,7 @@
                             onError(e);
                         }
                     }
-            }).start();
+            }, "SipSessionAsyncCmdThread").start();
         }
 
         public void makeCall(SipProfile peerProfile, String sessionDescription,
@@ -523,10 +522,10 @@
         }
 
         public void sendKeepAlive() {
-            mState = SipSessionState.PINGING;
+            mState = SipSession.State.PINGING;
             try {
                 processCommand(new OptionsCommand());
-                while (SipSessionState.PINGING == mState) {
+                while (SipSession.State.PINGING == mState) {
                     Thread.sleep(1000);
                 }
             } catch (SipException e) {
@@ -553,7 +552,7 @@
             try {
                 String s = super.toString();
                 return s.substring(s.indexOf("@")) + ":"
-                        + SipSessionState.toString(mState);
+                        + SipSession.State.toString(mState);
             } catch (Throwable e) {
                 return super.toString();
             }
@@ -561,7 +560,7 @@
 
         public boolean process(EventObject evt) throws SipException {
             if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
-                    + SipSessionState.toString(mState) + ": processing "
+                    + SipSession.State.toString(mState) + ": processing "
                     + log(evt));
             synchronized (SipSessionGroup.this) {
                 if (isClosed()) return false;
@@ -577,30 +576,30 @@
                 boolean processed;
 
                 switch (mState) {
-                case SipSessionState.REGISTERING:
-                case SipSessionState.DEREGISTERING:
+                case SipSession.State.REGISTERING:
+                case SipSession.State.DEREGISTERING:
                     processed = registeringToReady(evt);
                     break;
-                case SipSessionState.PINGING:
+                case SipSession.State.PINGING:
                     processed = keepAliveProcess(evt);
                     break;
-                case SipSessionState.READY_TO_CALL:
+                case SipSession.State.READY_TO_CALL:
                     processed = readyForCall(evt);
                     break;
-                case SipSessionState.INCOMING_CALL:
+                case SipSession.State.INCOMING_CALL:
                     processed = incomingCall(evt);
                     break;
-                case SipSessionState.INCOMING_CALL_ANSWERING:
+                case SipSession.State.INCOMING_CALL_ANSWERING:
                     processed = incomingCallToInCall(evt);
                     break;
-                case SipSessionState.OUTGOING_CALL:
-                case SipSessionState.OUTGOING_CALL_RING_BACK:
+                case SipSession.State.OUTGOING_CALL:
+                case SipSession.State.OUTGOING_CALL_RING_BACK:
                     processed = outgoingCall(evt);
                     break;
-                case SipSessionState.OUTGOING_CALL_CANCELING:
+                case SipSession.State.OUTGOING_CALL_CANCELING:
                     processed = outgoingCallToReady(evt);
                     break;
-                case SipSessionState.IN_CALL:
+                case SipSession.State.IN_CALL:
                     processed = inCall(evt);
                     break;
                 default:
@@ -650,8 +649,8 @@
         private void processTransactionTerminated(
                 TransactionTerminatedEvent event) {
             switch (mState) {
-                case SipSessionState.IN_CALL:
-                case SipSessionState.READY_TO_CALL:
+                case SipSession.State.IN_CALL:
+                case SipSession.State.READY_TO_CALL:
                     Log.d(TAG, "Transaction terminated; do nothing");
                     break;
                 default:
@@ -670,27 +669,27 @@
                     ? event.getServerTransaction()
                     : event.getClientTransaction();
 
-            if ((current != target) && (mState != SipSessionState.PINGING)) {
+            if ((current != target) && (mState != SipSession.State.PINGING)) {
                 Log.d(TAG, "not the current transaction; current=" + current
                         + ", timed out=" + target);
                 return;
             }
             switch (mState) {
-                case SipSessionState.REGISTERING:
-                case SipSessionState.DEREGISTERING:
+                case SipSession.State.REGISTERING:
+                case SipSession.State.DEREGISTERING:
                     reset();
                     mProxy.onRegistrationTimeout(this);
                     break;
-                case SipSessionState.INCOMING_CALL:
-                case SipSessionState.INCOMING_CALL_ANSWERING:
-                case SipSessionState.OUTGOING_CALL:
-                case SipSessionState.OUTGOING_CALL_CANCELING:
+                case SipSession.State.INCOMING_CALL:
+                case SipSession.State.INCOMING_CALL_ANSWERING:
+                case SipSession.State.OUTGOING_CALL:
+                case SipSession.State.OUTGOING_CALL_CANCELING:
                     onError(SipErrorCode.TIME_OUT, event.toString());
                     break;
-                case SipSessionState.PINGING:
+                case SipSession.State.PINGING:
                     reset();
                     mReRegisterFlag = true;
-                    mState = SipSessionState.READY_TO_CALL;
+                    mState = SipSession.State.READY_TO_CALL;
                     break;
 
                 default:
@@ -764,7 +763,7 @@
                 switch (statusCode) {
                 case Response.OK:
                     int state = mState;
-                    onRegistrationDone((state == SipSessionState.REGISTERING)
+                    onRegistrationDone((state == SipSession.State.REGISTERING)
                             ? getExpiryTime(((ResponseEvent) evt).getResponse())
                             : -1);
                     mLastNonce = null;
@@ -851,7 +850,7 @@
                         generateTag());
                 mDialog = mClientTransaction.getDialog();
                 addSipSession(this);
-                mState = SipSessionState.OUTGOING_CALL;
+                mState = SipSession.State.OUTGOING_CALL;
                 mProxy.onCalling(this);
                 startSessionTimer(cmd.getTimeout());
                 return true;
@@ -861,7 +860,7 @@
                         generateTag(), duration);
                 mDialog = mClientTransaction.getDialog();
                 addSipSession(this);
-                mState = SipSessionState.REGISTERING;
+                mState = SipSession.State.REGISTERING;
                 mProxy.onRegistering(this);
                 return true;
             } else if (DEREGISTER == evt) {
@@ -869,7 +868,7 @@
                         generateTag(), 0);
                 mDialog = mClientTransaction.getDialog();
                 addSipSession(this);
-                mState = SipSessionState.DEREGISTERING;
+                mState = SipSession.State.DEREGISTERING;
                 mProxy.onRegistering(this);
                 return true;
             }
@@ -884,7 +883,7 @@
                         mLocalProfile,
                         ((MakeCallCommand) evt).getSessionDescription(),
                         mServerTransaction);
-                mState = SipSessionState.INCOMING_CALL_ANSWERING;
+                mState = SipSession.State.INCOMING_CALL_ANSWERING;
                 startSessionTimer(((MakeCallCommand) evt).getTimeout());
                 return true;
             } else if (END_CALL == evt) {
@@ -925,8 +924,8 @@
                 int statusCode = response.getStatusCode();
                 switch (statusCode) {
                 case Response.RINGING:
-                    if (mState == SipSessionState.OUTGOING_CALL) {
-                        mState = SipSessionState.OUTGOING_CALL_RING_BACK;
+                    if (mState == SipSession.State.OUTGOING_CALL) {
+                        mState = SipSession.State.OUTGOING_CALL_RING_BACK;
                         mProxy.onRingingBack(this);
                         cancelSessionTimer();
                     }
@@ -969,7 +968,7 @@
                 // response comes back yet. We are cheating for not checking
                 // response.
                 mSipHelper.sendCancel(mClientTransaction);
-                mState = SipSessionState.OUTGOING_CALL_CANCELING;
+                mState = SipSession.State.OUTGOING_CALL_CANCELING;
                 startSessionTimer(CANCEL_CALL_TIMER);
                 return true;
             }
@@ -1025,7 +1024,7 @@
             } else if (isRequestEvent(Request.INVITE, evt)) {
                 // got Re-INVITE
                 RequestEvent event = mInviteReceived = (RequestEvent) evt;
-                mState = SipSessionState.INCOMING_CALL;
+                mState = SipSession.State.INCOMING_CALL;
                 mPeerSessionDescription = extractContent(event.getRequest());
                 mServerTransaction = null;
                 mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
@@ -1038,7 +1037,7 @@
                 // to change call
                 mClientTransaction = mSipHelper.sendReinvite(mDialog,
                         ((MakeCallCommand) evt).getSessionDescription());
-                mState = SipSessionState.OUTGOING_CALL;
+                mState = SipSession.State.OUTGOING_CALL;
                 startSessionTimer(((MakeCallCommand) evt).getTimeout());
                 return true;
             }
@@ -1066,14 +1065,14 @@
         }
 
         private void establishCall() {
-            mState = SipSessionState.IN_CALL;
+            mState = SipSession.State.IN_CALL;
             mInCall = true;
             cancelSessionTimer();
             mProxy.onCallEstablished(this, mPeerSessionDescription);
         }
 
         private void fallbackToPreviousInCall(int errorCode, String message) {
-            mState = SipSessionState.IN_CALL;
+            mState = SipSession.State.IN_CALL;
             mProxy.onCallChangeFailed(this, errorCode, message);
         }
 
@@ -1095,8 +1094,8 @@
         private void onError(int errorCode, String message) {
             cancelSessionTimer();
             switch (mState) {
-                case SipSessionState.REGISTERING:
-                case SipSessionState.DEREGISTERING:
+                case SipSession.State.REGISTERING:
+                case SipSession.State.DEREGISTERING:
                     onRegistrationFailed(errorCode, message);
                     break;
                 default:
@@ -1270,7 +1269,7 @@
     private static boolean isLoggable(SipSessionImpl s) {
         if (s != null) {
             switch (s.mState) {
-                case SipSessionState.PINGING:
+                case SipSession.State.PINGING:
                     return DEBUG_PING;
             }
         }
diff --git a/services/java/com/android/server/sip/SipSessionListenerProxy.java b/services/java/com/android/server/sip/SipSessionListenerProxy.java
index a4cd102..f8be0a8 100644
--- a/services/java/com/android/server/sip/SipSessionListenerProxy.java
+++ b/services/java/com/android/server/sip/SipSessionListenerProxy.java
@@ -40,7 +40,7 @@
         // One thread for each calling back.
         // Note: Guarantee ordering if the issue becomes important. Currently,
         // the chance of handling two callback events at a time is none.
-        new Thread(runnable).start();
+        new Thread(runnable, "SipSessionCallbackThread").start();
     }
 
     public void onCalling(final ISipSession session) {
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 35aa3b3..372acc2 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -27,7 +27,7 @@
 import android.net.sip.SipException;
 import android.net.sip.SipManager;
 import android.net.sip.SipProfile;
-import android.net.sip.SipSessionState;
+import android.net.sip.SipSession;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
@@ -92,7 +92,7 @@
         foregroundCall = new SipCall();
         backgroundCall = new SipCall();
         mProfile = profile;
-        mSipManager = SipManager.getInstance(context);
+        mSipManager = SipManager.newInstance(context);
 
         // FIXME: what's this for SIP?
         //Change the system property
@@ -707,8 +707,8 @@
 
         void dial() throws SipException {
             setState(Call.State.DIALING);
-            mSipAudioCall = mSipManager.makeAudioCall(mContext, mProfile,
-                    mPeer, null, SESSION_TIMEOUT);
+            mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null,
+                    SESSION_TIMEOUT);
             mSipAudioCall.setRingbackToneEnabled(false);
             mSipAudioCall.setListener(mAdapter);
         }
@@ -808,20 +808,20 @@
         if (sipAudioCall.isOnHold()) return Call.State.HOLDING;
         int sessionState = sipAudioCall.getState();
         switch (sessionState) {
-            case SipSessionState.READY_TO_CALL:            return Call.State.IDLE;
-            case SipSessionState.INCOMING_CALL:
-            case SipSessionState.INCOMING_CALL_ANSWERING:  return Call.State.INCOMING;
-            case SipSessionState.OUTGOING_CALL:            return Call.State.DIALING;
-            case SipSessionState.OUTGOING_CALL_RING_BACK:  return Call.State.ALERTING;
-            case SipSessionState.OUTGOING_CALL_CANCELING:  return Call.State.DISCONNECTING;
-            case SipSessionState.IN_CALL:                  return Call.State.ACTIVE;
+            case SipSession.State.READY_TO_CALL:            return Call.State.IDLE;
+            case SipSession.State.INCOMING_CALL:
+            case SipSession.State.INCOMING_CALL_ANSWERING:  return Call.State.INCOMING;
+            case SipSession.State.OUTGOING_CALL:            return Call.State.DIALING;
+            case SipSession.State.OUTGOING_CALL_RING_BACK:  return Call.State.ALERTING;
+            case SipSession.State.OUTGOING_CALL_CANCELING:  return Call.State.DISCONNECTING;
+            case SipSession.State.IN_CALL:                  return Call.State.ACTIVE;
             default:
                 Log.w(LOG_TAG, "illegal connection state: " + sessionState);
                 return Call.State.DISCONNECTED;
         }
     }
 
-    private abstract class SipAudioCallAdapter extends SipAudioCall.Adapter {
+    private abstract class SipAudioCallAdapter extends SipAudioCall.Listener {
         protected abstract void onCallEnded(Connection.DisconnectCause cause);
         protected abstract void onError(Connection.DisconnectCause cause);
 
diff --git a/voip/java/android/net/sip/SdpSessionDescription.java b/voip/java/android/net/sip/SdpSessionDescription.java
deleted file mode 100644
index f6ae837..0000000
--- a/voip/java/android/net/sip/SdpSessionDescription.java
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import gov.nist.javax.sdp.SessionDescriptionImpl;
-import gov.nist.javax.sdp.fields.AttributeField;
-import gov.nist.javax.sdp.fields.ConnectionField;
-import gov.nist.javax.sdp.fields.MediaField;
-import gov.nist.javax.sdp.fields.OriginField;
-import gov.nist.javax.sdp.fields.ProtoVersionField;
-import gov.nist.javax.sdp.fields.SessionNameField;
-import gov.nist.javax.sdp.fields.TimeField;
-import gov.nist.javax.sdp.parser.SDPAnnounceParser;
-
-import android.util.Log;
-
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Vector;
-import javax.sdp.Connection;
-import javax.sdp.MediaDescription;
-import javax.sdp.SdpException;
-
-/**
- * A session description that follows SDP (Session Description Protocol).
- * Refer to <a href="http://tools.ietf.org/html/rfc4566">RFC 4566</a>.
- * @hide
- */
-public class SdpSessionDescription extends SessionDescription {
-    private static final String TAG = "SDP";
-    private static final String AUDIO = "audio";
-    private static final String RTPMAP = "rtpmap";
-    private static final String PTIME = "ptime";
-    private static final String SENDONLY = "sendonly";
-    private static final String RECVONLY = "recvonly";
-    private static final String INACTIVE = "inactive";
-
-    private SessionDescriptionImpl mSessionDescription;
-
-    /**
-     * The audio codec information parsed from "rtpmap".
-     */
-    public static class AudioCodec {
-        public final int payloadType;
-        public final String name;
-        public final int sampleRate;
-        public final int sampleCount;
-
-        public AudioCodec(int payloadType, String name, int sampleRate,
-                int sampleCount) {
-            this.payloadType = payloadType;
-            this.name = name;
-            this.sampleRate = sampleRate;
-            this.sampleCount = sampleCount;
-        }
-    }
-
-    /**
-     * The builder class used to create an {@link SdpSessionDescription} object.
-     */
-    public static class Builder {
-        private SdpSessionDescription mSdp = new SdpSessionDescription();
-        private SessionDescriptionImpl mSessionDescription;
-
-        public Builder(String sessionName) throws SdpException {
-            mSessionDescription = new SessionDescriptionImpl();
-            mSdp.mSessionDescription = mSessionDescription;
-            try {
-                ProtoVersionField proto = new ProtoVersionField();
-                proto.setVersion(0);
-                mSessionDescription.addField(proto);
-
-                TimeField time = new TimeField();
-                time.setZero();
-                mSessionDescription.addField(time);
-
-                SessionNameField session = new SessionNameField();
-                session.setValue(sessionName);
-                mSessionDescription.addField(session);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-        }
-
-        public Builder setConnectionInfo(String networkType, String addressType,
-                String addr) throws SdpException {
-            try {
-                ConnectionField connection = new ConnectionField();
-                connection.setNetworkType(networkType);
-                connection.setAddressType(addressType);
-                connection.setAddress(addr);
-                mSessionDescription.addField(connection);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-            return this;
-        }
-
-        public Builder setOrigin(SipProfile user, long sessionId,
-                long sessionVersion, String networkType, String addressType,
-                String address) throws SdpException {
-            try {
-                OriginField origin = new OriginField();
-                origin.setUsername(user.getUserName());
-                origin.setSessionId(sessionId);
-                origin.setSessionVersion(sessionVersion);
-                origin.setAddressType(addressType);
-                origin.setNetworkType(networkType);
-                origin.setAddress(address);
-                mSessionDescription.addField(origin);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-            return this;
-        }
-
-        public Builder addMedia(String media, int port, int numPorts,
-                String transport, Integer... types) throws SdpException {
-            MediaField field = new MediaField();
-            Vector<Integer> typeVector = new Vector<Integer>();
-            Collections.addAll(typeVector, types);
-            try {
-                field.setMediaType(media);
-                field.setMediaPort(port);
-                field.setPortCount(numPorts);
-                field.setProtocol(transport);
-                field.setMediaFormats(typeVector);
-                mSessionDescription.addField(field);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-           return this;
-        }
-
-        public Builder addMediaAttribute(String type, String name, String value)
-                throws SdpException {
-            try {
-                MediaDescription md = mSdp.getMediaDescription(type);
-                if (md == null) {
-                    throw new SdpException("Should add media first!");
-                }
-                AttributeField attribute = new AttributeField();
-                attribute.setName(name);
-                attribute.setValueAllowNull(value);
-                mSessionDescription.addField(attribute);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-            return this;
-        }
-
-        public Builder addSessionAttribute(String name, String value)
-                throws SdpException {
-            try {
-                AttributeField attribute = new AttributeField();
-                attribute.setName(name);
-                attribute.setValueAllowNull(value);
-                mSessionDescription.addField(attribute);
-            } catch (Exception e) {
-                throwSdpException(e);
-            }
-            return this;
-        }
-
-        private void throwSdpException(Exception e) throws SdpException {
-            if (e instanceof SdpException) {
-                throw (SdpException) e;
-            } else {
-                throw new SdpException(e.toString(), e);
-            }
-        }
-
-        public String build() {
-            return mSdp.toString();
-        }
-    }
-
-    private SdpSessionDescription() {
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param sdpString an SDP session description to parse
-     */
-    public SdpSessionDescription(String sdpString) throws SdpException {
-        try {
-            mSessionDescription = new SDPAnnounceParser(sdpString).parse();
-        } catch (ParseException e) {
-            throw new SdpException(e.toString(), e);
-        }
-        verify();
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param content a raw SDP session description to parse
-     */
-    public SdpSessionDescription(byte[] content) throws SdpException {
-        this(new String(content));
-    }
-
-    private void verify() throws SdpException {
-        // make sure the syntax is correct over the fields we're interested in
-        Vector<MediaDescription> descriptions = (Vector<MediaDescription>)
-                mSessionDescription.getMediaDescriptions(false);
-        for (MediaDescription md : descriptions) {
-            md.getMedia().getMediaPort();
-            Connection connection = md.getConnection();
-            if (connection != null) connection.getAddress();
-            md.getMedia().getFormats();
-        }
-        Connection connection = mSessionDescription.getConnection();
-        if (connection != null) connection.getAddress();
-    }
-
-    /**
-     * Gets the connection address of the media.
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return the media connection address of the peer
-     */
-    public String getPeerMediaAddress(String type) {
-        try {
-            MediaDescription md = getMediaDescription(type);
-            Connection connection = md.getConnection();
-            if (connection == null) {
-                connection = mSessionDescription.getConnection();
-            }
-            return ((connection == null) ? null : connection.getAddress());
-        } catch (SdpException e) {
-            // should not occur
-            return null;
-        }
-    }
-
-    /**
-     * Gets the connection port number of the media.
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return the media connection port number of the peer
-     */
-    public int getPeerMediaPort(String type) {
-        try {
-            MediaDescription md = getMediaDescription(type);
-            return md.getMedia().getMediaPort();
-        } catch (SdpException e) {
-            // should not occur
-            return -1;
-        }
-    }
-
-    private boolean containsAttribute(String type, String name) {
-        if (name == null) return false;
-        MediaDescription md = getMediaDescription(type);
-        Vector<AttributeField> v = (Vector<AttributeField>)
-                md.getAttributeFields();
-        for (AttributeField field : v) {
-            if (name.equals(field.getAttribute().getName())) return true;
-        }
-        return false;
-    }
-
-    /**
-     * Checks if the media is "sendonly".
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return true if the media is "sendonly"
-     */
-    public boolean isSendOnly(String type) {
-        boolean answer = containsAttribute(type, SENDONLY);
-        Log.d(TAG, "   sendonly? " + answer);
-        return answer;
-    }
-
-    /**
-     * Checks if the media is "recvonly".
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return true if the media is "recvonly"
-     */
-    public boolean isReceiveOnly(String type) {
-        boolean answer = containsAttribute(type, RECVONLY);
-        Log.d(TAG, "   recvonly? " + answer);
-        return answer;
-    }
-
-    /**
-     * Checks if the media is in sending; i.e., not "recvonly" and not
-     * "inactive".
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return true if the media is sending
-     */
-    public boolean isSending(String type) {
-        boolean answer = !containsAttribute(type, RECVONLY)
-                && !containsAttribute(type, INACTIVE);
-
-        Log.d(TAG, "   sending? " + answer);
-        return answer;
-    }
-
-    /**
-     * Checks if the media is in receiving; i.e., not "sendonly" and not
-     * "inactive".
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return true if the media is receiving
-     */
-    public boolean isReceiving(String type) {
-        boolean answer = !containsAttribute(type, SENDONLY)
-                && !containsAttribute(type, INACTIVE);
-        Log.d(TAG, "   receiving? " + answer);
-        return answer;
-    }
-
-    private AudioCodec parseAudioCodec(String rtpmap, int ptime) {
-        String[] ss = rtpmap.split(" ");
-        int payloadType = Integer.parseInt(ss[0]);
-
-        ss = ss[1].split("/");
-        String name = ss[0];
-        int sampleRate = Integer.parseInt(ss[1]);
-        int channelCount = 1;
-        if (ss.length > 2) channelCount = Integer.parseInt(ss[2]);
-        int sampleCount = sampleRate / (1000 / ptime) * channelCount;
-        return new AudioCodec(payloadType, name, sampleRate, sampleCount);
-    }
-
-    /**
-     * Gets the list of audio codecs in this session description.
-     *
-     * @return the list of audio codecs in this session description
-     */
-    public List<AudioCodec> getAudioCodecs() {
-        MediaDescription md = getMediaDescription(AUDIO);
-        if (md == null) return new ArrayList<AudioCodec>();
-
-        // FIXME: what happens if ptime is missing
-        int ptime = 20;
-        try {
-            String value = md.getAttribute(PTIME);
-            if (value != null) ptime = Integer.parseInt(value);
-        } catch (Throwable t) {
-            Log.w(TAG, "getCodecs(): ignored: " + t);
-        }
-
-        List<AudioCodec> codecs = new ArrayList<AudioCodec>();
-        Vector<AttributeField> v = (Vector<AttributeField>)
-                md.getAttributeFields();
-        for (AttributeField field : v) {
-            try {
-                if (RTPMAP.equals(field.getName())) {
-                    AudioCodec codec = parseAudioCodec(field.getValue(), ptime);
-                    if (codec != null) codecs.add(codec);
-                }
-            } catch (Throwable t) {
-                Log.w(TAG, "getCodecs(): ignored: " + t);
-            }
-        }
-        return codecs;
-    }
-
-    /**
-     * Gets the media description of the specified type.
-     *
-     * @param type the media type; e.g., "AUDIO"
-     * @return the media description of the specified type
-     */
-    public MediaDescription getMediaDescription(String type) {
-        MediaDescription[] all = getMediaDescriptions();
-        if ((all == null) || (all.length == 0)) return null;
-        for (MediaDescription md : all) {
-            String t = md.getMedia().getMedia();
-            if (t.equalsIgnoreCase(type)) return md;
-        }
-        return null;
-    }
-
-    /**
-     * Gets all the media descriptions in this session description.
-     *
-     * @return all the media descriptions in this session description
-     */
-    public MediaDescription[] getMediaDescriptions() {
-        try {
-            Vector<MediaDescription> descriptions = (Vector<MediaDescription>)
-                    mSessionDescription.getMediaDescriptions(false);
-            MediaDescription[] all = new MediaDescription[descriptions.size()];
-            return descriptions.toArray(all);
-        } catch (SdpException e) {
-            Log.e(TAG, "getMediaDescriptions", e);
-        }
-        return null;
-    }
-
-    @Override
-    public String getType() {
-        return "sdp";
-    }
-
-    @Override
-    public byte[] getContent() {
-          return mSessionDescription.toString().getBytes();
-    }
-
-    @Override
-    public String toString() {
-        return mSessionDescription.toString();
-    }
-}
diff --git a/voip/java/android/net/sip/SessionDescription.aidl b/voip/java/android/net/sip/SessionDescription.aidl
deleted file mode 100644
index a120d16..0000000
--- a/voip/java/android/net/sip/SessionDescription.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2010, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-parcelable SessionDescription;
diff --git a/voip/java/android/net/sip/SessionDescription.java b/voip/java/android/net/sip/SessionDescription.java
deleted file mode 100644
index d476f0b..0000000
--- a/voip/java/android/net/sip/SessionDescription.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Abstract class of a session description.
- * @hide
- */
-public abstract class SessionDescription implements Parcelable {
-    /** @hide */
-    public static final Parcelable.Creator<SessionDescription> CREATOR =
-            new Parcelable.Creator<SessionDescription>() {
-                public SessionDescription createFromParcel(Parcel in) {
-                    return new SessionDescriptionImpl(in);
-                }
-
-                public SessionDescription[] newArray(int size) {
-                    return new SessionDescriptionImpl[size];
-                }
-            };
-
-    /**
-     * Gets the type of the session description; e.g., "SDP".
-     *
-     * @return the session description type
-     */
-    public abstract String getType();
-
-    /**
-     * Gets the raw content of the session description.
-     *
-     * @return the content of the session description
-     */
-    public abstract byte[] getContent();
-
-    /** @hide */
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(getType());
-        out.writeByteArray(getContent());
-    }
-
-    /** @hide */
-    public int describeContents() {
-        return 0;
-    }
-
-    private static class SessionDescriptionImpl extends SessionDescription {
-        private String mType;
-        private byte[] mContent;
-
-        SessionDescriptionImpl(Parcel in) {
-            mType = in.readString();
-            mContent = in.createByteArray();
-        }
-
-        @Override
-        public String getType() {
-            return mType;
-        }
-
-        @Override
-        public byte[] getContent() {
-            return mContent;
-        }
-    }
-}
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index 0069fe0..2f4fd90 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -16,120 +16,184 @@
 
 package android.net.sip;
 
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.media.ToneGenerator;
+import android.net.Uri;
+import android.net.rtp.AudioCodec;
 import android.net.rtp.AudioGroup;
 import android.net.rtp.AudioStream;
+import android.net.rtp.RtpStream;
+import android.net.sip.SimpleSessionDescription.Media;
+import android.net.wifi.WifiManager;
 import android.os.Message;
+import android.os.RemoteException;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
- * Interface for making audio calls over SIP.
- * @hide
+ * Class that handles an audio call over SIP.
  */
-public interface SipAudioCall {
+/** @hide */
+public class SipAudioCall extends SipSessionAdapter {
+    private static final String TAG = SipAudioCall.class.getSimpleName();
+    private static final boolean RELEASE_SOCKET = true;
+    private static final boolean DONT_RELEASE_SOCKET = false;
+    private static final int SESSION_TIMEOUT = 5; // in seconds
+
     /** Listener class for all event callbacks. */
-    public interface Listener {
+    public static class Listener {
         /**
          * Called when the call object is ready to make another call.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that is ready to make another call
          */
-        void onReadyToCall(SipAudioCall call);
+        public void onReadyToCall(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when a request is sent out to initiate a new call.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCalling(SipAudioCall call);
+        public void onCalling(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when a new call comes in.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          * @param caller the SIP profile of the caller
          */
-        void onRinging(SipAudioCall call, SipProfile caller);
+        public void onRinging(SipAudioCall call, SipProfile caller) {
+            onChanged(call);
+        }
 
         /**
-         * Called when a RINGING response is received for the INVITE request sent
+         * Called when a RINGING response is received for the INVITE request
+         * sent. The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onRingingBack(SipAudioCall call);
+        public void onRingingBack(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when the session is established.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCallEstablished(SipAudioCall call);
+        public void onCallEstablished(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when the session is terminated.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCallEnded(SipAudioCall call);
+        public void onCallEnded(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when the peer is busy during session initialization.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCallBusy(SipAudioCall call);
+        public void onCallBusy(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
          * Called when the call is on hold.
+         * The default implementation calls {@link #onChange}.
          *
          * @param call the call object that carries out the audio call
          */
-        void onCallHeld(SipAudioCall call);
+        public void onCallHeld(SipAudioCall call) {
+            onChanged(call);
+        }
 
         /**
-         * Called when an error occurs.
+         * Called when an error occurs. The default implementation is no op.
          *
          * @param call the call object that carries out the audio call
          * @param errorCode error code of this error
          * @param errorMessage error message
          * @see SipErrorCode
          */
-        void onError(SipAudioCall call, int errorCode, String errorMessage);
-    }
-
-    /**
-     * The adapter class for {@link Listener}. The default implementation of
-     * all callback methods is no-op.
-     */
-    public class Adapter implements Listener {
-        protected void onChanged(SipAudioCall call) {
-        }
-        public void onReadyToCall(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCalling(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onRinging(SipAudioCall call, SipProfile caller) {
-            onChanged(call);
-        }
-        public void onRingingBack(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCallEstablished(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCallEnded(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCallBusy(SipAudioCall call) {
-            onChanged(call);
-        }
-        public void onCallHeld(SipAudioCall call) {
-            onChanged(call);
-        }
         public void onError(SipAudioCall call, int errorCode,
                 String errorMessage) {
-            onChanged(call);
+            // no-op
         }
+
+        /**
+         * Called when an event occurs and the corresponding callback is not
+         * overridden. The default implementation is no op. Error events are
+         * not re-directed to this callback and are handled in {@link #onError}.
+         */
+        public void onChanged(SipAudioCall call) {
+            // no-op
+        }
+    }
+
+    private Context mContext;
+    private SipProfile mLocalProfile;
+    private SipAudioCall.Listener mListener;
+    private SipSession mSipSession;
+
+    private long mSessionId = System.currentTimeMillis();
+    private String mPeerSd;
+
+    private AudioStream mAudioStream;
+    private AudioGroup mAudioGroup;
+
+    private boolean mInCall = false;
+    private boolean mMuted = false;
+    private boolean mHold = false;
+
+    private boolean mRingbackToneEnabled = true;
+    private boolean mRingtoneEnabled = true;
+    private Ringtone mRingtone;
+    private ToneGenerator mRingbackTone;
+
+    private SipProfile mPendingCallRequest;
+    private WifiManager mWm;
+    private WifiManager.WifiLock mWifiHighPerfLock;
+
+    private int mErrorCode = SipErrorCode.NO_ERROR;
+    private String mErrorMessage;
+
+    /**
+     * Creates a call object with the local SIP profile.
+     * @param context the context for accessing system services such as
+     *        ringtone, audio, WIFI etc
+     */
+    public SipAudioCall(Context context, SipProfile localProfile) {
+        mContext = context;
+        mLocalProfile = localProfile;
+        mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
     }
 
     /**
@@ -139,7 +203,9 @@
      * @param listener to listen to the audio call events of this object
      * @see #setListener(Listener, boolean)
      */
-    void setListener(Listener listener);
+    public void setListener(SipAudioCall.Listener listener) {
+        setListener(listener, false);
+    }
 
     /**
      * Sets the listener to listen to the audio call events. A
@@ -150,12 +216,312 @@
      * @param callbackImmediately set to true if the caller wants to be called
      *      back immediately on the current state
      */
-    void setListener(Listener listener, boolean callbackImmediately);
+    public void setListener(SipAudioCall.Listener listener,
+            boolean callbackImmediately) {
+        mListener = listener;
+        try {
+            if ((listener == null) || !callbackImmediately) {
+                // do nothing
+            } else if (mErrorCode != SipErrorCode.NO_ERROR) {
+                listener.onError(this, mErrorCode, mErrorMessage);
+            } else if (mInCall) {
+                if (mHold) {
+                    listener.onCallHeld(this);
+                } else {
+                    listener.onCallEstablished(this);
+                }
+            } else {
+                int state = getState();
+                switch (state) {
+                    case SipSession.State.READY_TO_CALL:
+                        listener.onReadyToCall(this);
+                        break;
+                    case SipSession.State.INCOMING_CALL:
+                        listener.onRinging(this, getPeerProfile());
+                        break;
+                    case SipSession.State.OUTGOING_CALL:
+                        listener.onCalling(this);
+                        break;
+                    case SipSession.State.OUTGOING_CALL_RING_BACK:
+                        listener.onRingingBack(this);
+                        break;
+                }
+            }
+        } catch (Throwable t) {
+            Log.e(TAG, "setListener()", t);
+        }
+    }
+
+    /**
+     * Checks if the call is established.
+     *
+     * @return true if the call is established
+     */
+    public synchronized boolean isInCall() {
+        return mInCall;
+    }
+
+    /**
+     * Checks if the call is on hold.
+     *
+     * @return true if the call is on hold
+     */
+    public synchronized boolean isOnHold() {
+        return mHold;
+    }
 
     /**
      * Closes this object. This object is not usable after being closed.
      */
-    void close();
+    public void close() {
+        close(true);
+    }
+
+    private synchronized void close(boolean closeRtp) {
+        if (closeRtp) stopCall(RELEASE_SOCKET);
+        stopRingbackTone();
+        stopRinging();
+
+        mInCall = false;
+        mHold = false;
+        mSessionId = System.currentTimeMillis();
+        mErrorCode = SipErrorCode.NO_ERROR;
+        mErrorMessage = null;
+
+        if (mSipSession != null) {
+            mSipSession.setListener(null);
+            mSipSession = null;
+        }
+    }
+
+    /**
+     * Gets the local SIP profile.
+     *
+     * @return the local SIP profile
+     */
+    public synchronized SipProfile getLocalProfile() {
+        return mLocalProfile;
+    }
+
+    /**
+     * Gets the peer's SIP profile.
+     *
+     * @return the peer's SIP profile
+     */
+    public synchronized SipProfile getPeerProfile() {
+        return (mSipSession == null) ? null : mSipSession.getPeerProfile();
+    }
+
+    /**
+     * Gets the state of the {@link SipSession} that carries this call.
+     * The value returned must be one of the states in {@link SipSession.State}.
+     *
+     * @return the session state
+     */
+    public synchronized int getState() {
+        if (mSipSession == null) return SipSession.State.READY_TO_CALL;
+        return mSipSession.getState();
+    }
+
+
+    /**
+     * Gets the {@link SipSession} that carries this call.
+     *
+     * @return the session object that carries this call
+     * @hide
+     */
+    public synchronized SipSession getSipSession() {
+        return mSipSession;
+    }
+
+    private SipSession.Listener createListener() {
+        return new SipSession.Listener() {
+            @Override
+            public void onCalling(SipSession session) {
+                Log.d(TAG, "calling... " + session);
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onCalling(SipAudioCall.this);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCalling(): " + t);
+                    }
+                }
+            }
+
+            @Override
+            public void onRingingBack(SipSession session) {
+                Log.d(TAG, "sip call ringing back: " + session);
+                if (!mInCall) startRingbackTone();
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onRingingBack(SipAudioCall.this);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onRingingBack(): " + t);
+                    }
+                }
+            }
+
+            @Override
+            public synchronized void onRinging(SipSession session,
+                    SipProfile peerProfile, String sessionDescription) {
+                if ((mSipSession == null) || !mInCall
+                        || !session.getCallId().equals(mSipSession.getCallId())) {
+                    // should not happen
+                    session.endCall();
+                    return;
+                }
+
+                // session changing request
+                try {
+                    String answer = createAnswer(sessionDescription).encode();
+                    mSipSession.answerCall(answer, SESSION_TIMEOUT);
+                } catch (Throwable e) {
+                    Log.e(TAG, "onRinging()", e);
+                    session.endCall();
+                }
+            }
+
+            @Override
+            public void onCallEstablished(SipSession session,
+                    String sessionDescription) {
+                stopRingbackTone();
+                stopRinging();
+                mPeerSd = sessionDescription;
+                Log.v(TAG, "onCallEstablished()" + mPeerSd);
+
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        if (mHold) {
+                            listener.onCallHeld(SipAudioCall.this);
+                        } else {
+                            listener.onCallEstablished(SipAudioCall.this);
+                        }
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCallEstablished(): " + t);
+                    }
+                }
+            }
+
+            @Override
+            public void onCallEnded(SipSession session) {
+                Log.d(TAG, "sip call ended: " + session);
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onCallEnded(SipAudioCall.this);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCallEnded(): " + t);
+                    }
+                }
+                close();
+            }
+
+            @Override
+            public void onCallBusy(SipSession session) {
+                Log.d(TAG, "sip call busy: " + session);
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onCallBusy(SipAudioCall.this);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCallBusy(): " + t);
+                    }
+                }
+                close(false);
+            }
+
+            @Override
+            public void onCallChangeFailed(SipSession session, int errorCode,
+                    String message) {
+                Log.d(TAG, "sip call change failed: " + message);
+                mErrorCode = errorCode;
+                mErrorMessage = message;
+                Listener listener = mListener;
+                if (listener != null) {
+                    try {
+                        listener.onError(SipAudioCall.this, mErrorCode,
+                                message);
+                    } catch (Throwable t) {
+                        Log.i(TAG, "onCallBusy(): " + t);
+                    }
+                }
+            }
+
+            @Override
+            public void onError(SipSession session, int errorCode,
+                    String message) {
+                SipAudioCall.this.onError(errorCode, message);
+            }
+
+            @Override
+            public void onRegistering(SipSession session) {
+                // irrelevant
+            }
+
+            @Override
+            public void onRegistrationTimeout(SipSession session) {
+                // irrelevant
+            }
+
+            @Override
+            public void onRegistrationFailed(SipSession session, int errorCode,
+                    String message) {
+                // irrelevant
+            }
+
+            @Override
+            public void onRegistrationDone(SipSession session, int duration) {
+                // irrelevant
+            }
+        };
+    }
+
+    private void onError(int errorCode, String message) {
+        Log.d(TAG, "sip session error: "
+                + SipErrorCode.toString(errorCode) + ": " + message);
+        mErrorCode = errorCode;
+        mErrorMessage = message;
+        Listener listener = mListener;
+        if (listener != null) {
+            try {
+                listener.onError(this, errorCode, message);
+            } catch (Throwable t) {
+                Log.i(TAG, "onError(): " + t);
+            }
+        }
+        synchronized (this) {
+            if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST)
+                    || !isInCall()) {
+                close(true);
+            }
+        }
+    }
+
+    /**
+     * Attaches an incoming call to this call object.
+     *
+     * @param session the session that receives the incoming call
+     * @param sessionDescription the session description of the incoming call
+     * @throws SipException if the SIP service fails to attach this object to
+     *        the session
+     */
+    public synchronized void attachCall(SipSession session,
+            String sessionDescription) throws SipException {
+        mSipSession = session;
+        mPeerSd = sessionDescription;
+        Log.v(TAG, "attachCall()" + mPeerSd);
+        try {
+            session.setListener(createListener());
+
+            if (getState() == SipSession.State.INCOMING_CALL) startRinging();
+        } catch (Throwable e) {
+            Log.e(TAG, "attachCall()", e);
+            throwSipException(e);
+        }
+    }
 
     /**
      * Initiates an audio call to the specified profile. The attempt will be
@@ -165,29 +531,40 @@
      *
      * @param callee the SIP profile to make the call to
      * @param sipManager the {@link SipManager} object to help make call with
-     * @param timeout the timeout value in seconds
+     * @param timeout the timeout value in seconds. Default value (defined by
+     *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener.onError
+     * @throws SipException if the SIP service fails to create a session for the
+     *        call
      */
-    void makeCall(SipProfile callee, SipManager sipManager, int timeout)
-            throws SipException;
+    public synchronized void makeCall(SipProfile peerProfile,
+        SipManager sipManager, int timeout) throws SipException {
+        SipSession s = mSipSession = sipManager.createSipSession(
+                mLocalProfile, createListener());
+        if (s == null) {
+            throw new SipException(
+                    "Failed to create SipSession; network available?");
+        }
+        try {
+            mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
+            s.makeCall(peerProfile, createOffer().encode(), timeout);
+        } catch (IOException e) {
+            throw new SipException("makeCall()", e);
+        }
+    }
 
     /**
-     * Starts the audio for the established call. This method should be called
-     * after {@link Listener#onCallEstablished} is called.
+     * Ends a call.
+     * @throws SipException if the SIP service fails to end the call
      */
-    void startAudio();
+    public synchronized void endCall() throws SipException {
+        stopRinging();
+        stopCall(RELEASE_SOCKET);
+        mInCall = false;
 
-    /**
-     * Attaches an incoming call to this call object.
-     *
-     * @param session the session that receives the incoming call
-     * @param sessionDescription the session description of the incoming call
-     */
-    void attachCall(ISipSession session, String sessionDescription)
-            throws SipException;
-
-    /** Ends a call. */
-    void endCall() throws SipException;
+        // perform the above local ops first and then network op
+        if (mSipSession != null) mSipSession.endCall();
+    }
 
     /**
      * Puts a call on hold.  When succeeds, {@link Listener#onCallHeld} is
@@ -196,10 +573,19 @@
      * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param timeout the timeout value in seconds
+     * @param timeout the timeout value in seconds. Default value (defined by
+     *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener.onError
+     * @throws SipException if the SIP service fails to hold the call
      */
-    void holdCall(int timeout) throws SipException;
+    public synchronized void holdCall(int timeout) throws SipException {
+        if (mHold) return;
+        mSipSession.changeCall(createHoldOffer().encode(), timeout);
+        mHold = true;
+
+        AudioGroup audioGroup = getAudioGroup();
+        if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+    }
 
     /**
      * Answers a call. The attempt will be timed out if the call is not
@@ -207,10 +593,20 @@
      * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param timeout the timeout value in seconds
+     * @param timeout the timeout value in seconds. Default value (defined by
+     *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener.onError
+     * @throws SipException if the SIP service fails to answer the call
      */
-    void answerCall(int timeout) throws SipException;
+    public synchronized void answerCall(int timeout) throws SipException {
+        stopRinging();
+        try {
+            mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
+            mSipSession.answerCall(createAnswer(mPeerSd).encode(), timeout);
+        } catch (IOException e) {
+            throw new SipException("answerCall()", e);
+        }
+    }
 
     /**
      * Continues a call that's on hold. When succeeds,
@@ -219,45 +615,189 @@
      * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param timeout the timeout value in seconds
+     * @param timeout the timeout value in seconds. Default value (defined by
+     *        SIP protocol) is used if {@code timeout} is zero or negative.
      * @see Listener.onError
+     * @throws SipException if the SIP service fails to unhold the call
      */
-    void continueCall(int timeout) throws SipException;
+    public synchronized void continueCall(int timeout) throws SipException {
+        if (!mHold) return;
+        mSipSession.changeCall(createContinueOffer().encode(), timeout);
+        mHold = false;
+        AudioGroup audioGroup = getAudioGroup();
+        if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL);
+    }
 
-    /** Puts the device to speaker mode. */
-    void setSpeakerMode(boolean speakerMode);
+    private SimpleSessionDescription createOffer() {
+        SimpleSessionDescription offer =
+                new SimpleSessionDescription(mSessionId, getLocalIp());
+        AudioCodec[] codecs = AudioCodec.getCodecs();
+        Media media = offer.newMedia(
+                "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
+        for (AudioCodec codec : AudioCodec.getCodecs()) {
+            media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
+        }
+        media.setRtpPayload(127, "telephone-event/8000", "0-15");
+        return offer;
+    }
+
+    private SimpleSessionDescription createAnswer(String offerSd) {
+        SimpleSessionDescription offer =
+                new SimpleSessionDescription(offerSd);
+        SimpleSessionDescription answer =
+                new SimpleSessionDescription(mSessionId, getLocalIp());
+        AudioCodec codec = null;
+        for (Media media : offer.getMedia()) {
+            if ((codec == null) && (media.getPort() > 0)
+                    && "audio".equals(media.getType())
+                    && "RTP/AVP".equals(media.getProtocol())) {
+                // Find the first audio codec we supported.
+                for (int type : media.getRtpPayloadTypes()) {
+                    codec = AudioCodec.getCodec(type, media.getRtpmap(type),
+                            media.getFmtp(type));
+                    if (codec != null) {
+                        break;
+                    }
+                }
+                if (codec != null) {
+                    Media reply = answer.newMedia(
+                            "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
+                    reply.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
+
+                    // Check if DTMF is supported in the same media.
+                    for (int type : media.getRtpPayloadTypes()) {
+                        String rtpmap = media.getRtpmap(type);
+                        if ((type != codec.type) && (rtpmap != null)
+                                && rtpmap.startsWith("telephone-event")) {
+                            reply.setRtpPayload(
+                                    type, rtpmap, media.getFmtp(type));
+                        }
+                    }
+
+                    // Handle recvonly and sendonly.
+                    if (media.getAttribute("recvonly") != null) {
+                        answer.setAttribute("sendonly", "");
+                    } else if(media.getAttribute("sendonly") != null) {
+                        answer.setAttribute("recvonly", "");
+                    } else if(offer.getAttribute("recvonly") != null) {
+                        answer.setAttribute("sendonly", "");
+                    } else if(offer.getAttribute("sendonly") != null) {
+                        answer.setAttribute("recvonly", "");
+                    }
+                    continue;
+                }
+            }
+            // Reject the media.
+            Media reply = answer.newMedia(
+                    media.getType(), 0, 1, media.getProtocol());
+            for (String format : media.getFormats()) {
+                reply.setFormat(format, null);
+            }
+        }
+        if (codec == null) {
+            throw new IllegalStateException("Reject SDP: no suitable codecs");
+        }
+        return answer;
+    }
+
+    private SimpleSessionDescription createHoldOffer() {
+        SimpleSessionDescription offer = createContinueOffer();
+        offer.setAttribute("sendonly", "");
+        return offer;
+    }
+
+    private SimpleSessionDescription createContinueOffer() {
+        SimpleSessionDescription offer =
+                new SimpleSessionDescription(mSessionId, getLocalIp());
+        Media media = offer.newMedia(
+                "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
+        AudioCodec codec = mAudioStream.getCodec();
+        media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
+        int dtmfType = mAudioStream.getDtmfType();
+        if (dtmfType != -1) {
+            media.setRtpPayload(dtmfType, "telephone-event/8000", "0-15");
+        }
+        return offer;
+    }
+
+    private void grabWifiHighPerfLock() {
+        if (mWifiHighPerfLock == null) {
+            Log.v(TAG, "acquire wifi high perf lock");
+            mWifiHighPerfLock = ((WifiManager)
+                    mContext.getSystemService(Context.WIFI_SERVICE))
+                    .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG);
+            mWifiHighPerfLock.acquire();
+        }
+    }
+
+    private void releaseWifiHighPerfLock() {
+        if (mWifiHighPerfLock != null) {
+            Log.v(TAG, "release wifi high perf lock");
+            mWifiHighPerfLock.release();
+            mWifiHighPerfLock = null;
+        }
+    }
+
+    private boolean isWifiOn() {
+        return (mWm.getConnectionInfo().getBSSID() == null) ? false : true;
+    }
 
     /** Toggles mute. */
-    void toggleMute();
-
-    /**
-     * Checks if the call is on hold.
-     *
-     * @return true if the call is on hold
-     */
-    boolean isOnHold();
+    public synchronized void toggleMute() {
+        AudioGroup audioGroup = getAudioGroup();
+        if (audioGroup != null) {
+            audioGroup.setMode(
+                    mMuted ? AudioGroup.MODE_NORMAL : AudioGroup.MODE_MUTED);
+            mMuted = !mMuted;
+        }
+    }
 
     /**
      * Checks if the call is muted.
      *
      * @return true if the call is muted
      */
-    boolean isMuted();
+    public synchronized boolean isMuted() {
+        return mMuted;
+    }
+
+    /** Puts the device to speaker mode. */
+    public synchronized void setSpeakerMode(boolean speakerMode) {
+        ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
+                .setSpeakerphoneOn(speakerMode);
+    }
 
     /**
-     * Sends a DTMF code.
+     * Sends a DTMF code. According to RFC2833, event 0--9 maps to decimal
+     * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
+     * flash to 16. Currently, event flash is not supported.
      *
-     * @param code the DTMF code to send
+     * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
+     *        inputs.
+     * @see http://tools.ietf.org/html/rfc2833
      */
-    void sendDtmf(int code);
+    public void sendDtmf(int code) {
+        sendDtmf(code, null);
+    }
 
     /**
-     * Sends a DTMF code.
+     * Sends a DTMF code. According to RFC2833, event 0--9 maps to decimal
+     * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
+     * flash to 16. Currently, event flash is not supported.
      *
-     * @param code the DTMF code to send
+     * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
+     *        inputs.
      * @param result the result message to send when done
      */
-    void sendDtmf(int code, Message result);
+    public synchronized void sendDtmf(int code, Message result) {
+        AudioGroup audioGroup = getAudioGroup();
+        if ((audioGroup != null) && (mSipSession != null)
+                && (SipSession.State.IN_CALL == getState())) {
+            Log.v(TAG, "send DTMF: " + code);
+            audioGroup.sendDtmf(code);
+        }
+        if (result != null) result.sendToTarget();
+    }
 
     /**
      * Gets the {@link AudioStream} object used in this call. The object
@@ -268,8 +808,11 @@
      *
      * @return the {@link AudioStream} object or null if the RTP stream has not
      *      yet been set up
+     * @hide
      */
-    AudioStream getAudioStream();
+    public synchronized AudioStream getAudioStream() {
+        return mAudioStream;
+    }
 
     /**
      * Gets the {@link AudioGroup} object which the {@link AudioStream} object
@@ -283,8 +826,12 @@
      * @return the {@link AudioGroup} object or null if the RTP stream has not
      *      yet been set up
      * @see #getAudioStream
+     * @hide
      */
-    AudioGroup getAudioGroup();
+    public synchronized AudioGroup getAudioGroup() {
+        if (mAudioGroup != null) return mAudioGroup;
+        return ((mAudioStream == null) ? null : mAudioStream.getGroup());
+    }
 
     /**
      * Sets the {@link AudioGroup} object which the {@link AudioStream} object
@@ -292,56 +839,214 @@
      * will be dynamically created when needed.
      *
      * @see #getAudioStream
+     * @hide
      */
-    void setAudioGroup(AudioGroup audioGroup);
+    public synchronized void setAudioGroup(AudioGroup group) {
+        if ((mAudioStream != null) && (mAudioStream.getGroup() != null)) {
+            mAudioStream.join(group);
+        }
+        mAudioGroup = group;
+    }
 
     /**
-     * Checks if the call is established.
-     *
-     * @return true if the call is established
+     * Starts the audio for the established call. This method should be called
+     * after {@link Listener#onCallEstablished} is called.
      */
-    boolean isInCall();
+    public void startAudio() {
+        try {
+            startAudioInternal();
+        } catch (UnknownHostException e) {
+            onError(SipErrorCode.PEER_NOT_REACHABLE, e.getMessage());
+        } catch (Throwable e) {
+            onError(SipErrorCode.CLIENT_ERROR, e.getMessage());
+        }
+    }
 
-    /**
-     * Gets the local SIP profile.
-     *
-     * @return the local SIP profile
-     */
-    SipProfile getLocalProfile();
+    private synchronized void startAudioInternal() throws UnknownHostException {
+        if (mPeerSd == null) {
+            Log.v(TAG, "startAudioInternal() mPeerSd = null");
+            throw new IllegalStateException("mPeerSd = null");
+        }
 
-    /**
-     * Gets the peer's SIP profile.
-     *
-     * @return the peer's SIP profile
-     */
-    SipProfile getPeerProfile();
+        stopCall(DONT_RELEASE_SOCKET);
+        mInCall = true;
 
-    /**
-     * Gets the state of the {@link ISipSession} that carries this call.
-     * The value returned must be one of the states in {@link SipSessionState}.
-     *
-     * @return the session state
-     */
-    int getState();
+        // Run exact the same logic in createAnswer() to setup mAudioStream.
+        SimpleSessionDescription offer =
+                new SimpleSessionDescription(mPeerSd);
+        AudioStream stream = mAudioStream;
+        AudioCodec codec = null;
+        for (Media media : offer.getMedia()) {
+            if ((codec == null) && (media.getPort() > 0)
+                    && "audio".equals(media.getType())
+                    && "RTP/AVP".equals(media.getProtocol())) {
+                // Find the first audio codec we supported.
+                for (int type : media.getRtpPayloadTypes()) {
+                    codec = AudioCodec.getCodec(
+                            type, media.getRtpmap(type), media.getFmtp(type));
+                    if (codec != null) {
+                        break;
+                    }
+                }
 
-    /**
-     * Gets the {@link ISipSession} that carries this call.
-     *
-     * @return the session object that carries this call
-     */
-    ISipSession getSipSession();
+                if (codec != null) {
+                    // Associate with the remote host.
+                    String address = media.getAddress();
+                    if (address == null) {
+                        address = offer.getAddress();
+                    }
+                    stream.associate(InetAddress.getByName(address),
+                            media.getPort());
+
+                    stream.setDtmfType(-1);
+                    stream.setCodec(codec);
+                    // Check if DTMF is supported in the same media.
+                    for (int type : media.getRtpPayloadTypes()) {
+                        String rtpmap = media.getRtpmap(type);
+                        if ((type != codec.type) && (rtpmap != null)
+                                && rtpmap.startsWith("telephone-event")) {
+                            stream.setDtmfType(type);
+                        }
+                    }
+
+                    // Handle recvonly and sendonly.
+                    if (mHold) {
+                        stream.setMode(RtpStream.MODE_NORMAL);
+                    } else if (media.getAttribute("recvonly") != null) {
+                        stream.setMode(RtpStream.MODE_SEND_ONLY);
+                    } else if(media.getAttribute("sendonly") != null) {
+                        stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
+                    } else if(offer.getAttribute("recvonly") != null) {
+                        stream.setMode(RtpStream.MODE_SEND_ONLY);
+                    } else if(offer.getAttribute("sendonly") != null) {
+                        stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
+                    } else {
+                        stream.setMode(RtpStream.MODE_NORMAL);
+                    }
+                    break;
+                }
+            }
+        }
+        if (codec == null) {
+            throw new IllegalStateException("Reject SDP: no suitable codecs");
+        }
+
+        if (isWifiOn()) grabWifiHighPerfLock();
+
+        if (!mHold) {
+            /* The recorder volume will be very low if the device is in
+             * IN_CALL mode. Therefore, we have to set the mode to NORMAL
+             * in order to have the normal microphone level.
+             */
+            ((AudioManager) mContext.getSystemService
+                    (Context.AUDIO_SERVICE))
+                    .setMode(AudioManager.MODE_NORMAL);
+        }
+
+        // AudioGroup logic:
+        AudioGroup audioGroup = getAudioGroup();
+        if (mHold) {
+            if (audioGroup != null) {
+                audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+            }
+            // don't create an AudioGroup here; doing so will fail if
+            // there's another AudioGroup out there that's active
+        } else {
+            if (audioGroup == null) audioGroup = new AudioGroup();
+            stream.join(audioGroup);
+            if (mMuted) {
+                audioGroup.setMode(AudioGroup.MODE_MUTED);
+            } else {
+                audioGroup.setMode(AudioGroup.MODE_NORMAL);
+            }
+        }
+    }
+
+    private void stopCall(boolean releaseSocket) {
+        Log.d(TAG, "stop audiocall");
+        releaseWifiHighPerfLock();
+        if (mAudioStream != null) {
+            mAudioStream.join(null);
+
+            if (releaseSocket) {
+                mAudioStream.release();
+                mAudioStream = null;
+            }
+        }
+    }
+
+    private String getLocalIp() {
+        return mSipSession.getLocalIp();
+    }
+
 
     /**
      * Enables/disables the ring-back tone.
      *
      * @param enabled true to enable; false to disable
      */
-    void setRingbackToneEnabled(boolean enabled);
+    public synchronized void setRingbackToneEnabled(boolean enabled) {
+        mRingbackToneEnabled = enabled;
+    }
 
     /**
      * Enables/disables the ring tone.
      *
      * @param enabled true to enable; false to disable
      */
-    void setRingtoneEnabled(boolean enabled);
+    public synchronized void setRingtoneEnabled(boolean enabled) {
+        mRingtoneEnabled = enabled;
+    }
+
+    private void startRingbackTone() {
+        if (!mRingbackToneEnabled) return;
+        if (mRingbackTone == null) {
+            // The volume relative to other sounds in the stream
+            int toneVolume = 80;
+            mRingbackTone = new ToneGenerator(
+                    AudioManager.STREAM_VOICE_CALL, toneVolume);
+        }
+        mRingbackTone.startTone(ToneGenerator.TONE_CDMA_LOW_PBX_L);
+    }
+
+    private void stopRingbackTone() {
+        if (mRingbackTone != null) {
+            mRingbackTone.stopTone();
+            mRingbackTone.release();
+            mRingbackTone = null;
+        }
+    }
+
+    private void startRinging() {
+        if (!mRingtoneEnabled) return;
+        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
+                .vibrate(new long[] {0, 1000, 1000}, 1);
+        AudioManager am = (AudioManager)
+                mContext.getSystemService(Context.AUDIO_SERVICE);
+        if (am.getStreamVolume(AudioManager.STREAM_RING) > 0) {
+            String ringtoneUri =
+                    Settings.System.DEFAULT_RINGTONE_URI.toString();
+            mRingtone = RingtoneManager.getRingtone(mContext,
+                    Uri.parse(ringtoneUri));
+            mRingtone.play();
+        }
+    }
+
+    private void stopRinging() {
+        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
+                .cancel();
+        if (mRingtone != null) mRingtone.stop();
+    }
+
+    private void throwSipException(Throwable throwable) throws SipException {
+        if (throwable instanceof SipException) {
+            throw (SipException) throwable;
+        } else {
+            throw new SipException("", throwable);
+        }
+    }
+
+    private SipProfile getPeerProfile(SipSession session) {
+        return session.getPeerProfile();
+    }
 }
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
deleted file mode 100644
index 2f8d175..0000000
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ /dev/null
@@ -1,766 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.media.ToneGenerator;
-import android.net.Uri;
-import android.net.rtp.AudioCodec;
-import android.net.rtp.AudioGroup;
-import android.net.rtp.AudioStream;
-import android.net.rtp.RtpStream;
-import android.net.sip.SimpleSessionDescription.Media;
-import android.net.wifi.WifiManager;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.util.Log;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Class that handles an audio call over SIP.
- */
-/** @hide */
-public class SipAudioCallImpl extends SipSessionAdapter
-        implements SipAudioCall {
-    private static final String TAG = SipAudioCallImpl.class.getSimpleName();
-    private static final boolean RELEASE_SOCKET = true;
-    private static final boolean DONT_RELEASE_SOCKET = false;
-    private static final int SESSION_TIMEOUT = 5; // in seconds
-
-    private Context mContext;
-    private SipProfile mLocalProfile;
-    private SipAudioCall.Listener mListener;
-    private ISipSession mSipSession;
-
-    private long mSessionId = System.currentTimeMillis();
-    private String mPeerSd;
-
-    private AudioStream mAudioStream;
-    private AudioGroup mAudioGroup;
-
-    private boolean mInCall = false;
-    private boolean mMuted = false;
-    private boolean mHold = false;
-
-    private boolean mRingbackToneEnabled = true;
-    private boolean mRingtoneEnabled = true;
-    private Ringtone mRingtone;
-    private ToneGenerator mRingbackTone;
-
-    private SipProfile mPendingCallRequest;
-    private WifiManager mWm;
-    private WifiManager.WifiLock mWifiHighPerfLock;
-
-    private int mErrorCode = SipErrorCode.NO_ERROR;
-    private String mErrorMessage;
-
-    public SipAudioCallImpl(Context context, SipProfile localProfile) {
-        mContext = context;
-        mLocalProfile = localProfile;
-        mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-    }
-
-    public void setListener(SipAudioCall.Listener listener) {
-        setListener(listener, false);
-    }
-
-    public void setListener(SipAudioCall.Listener listener,
-            boolean callbackImmediately) {
-        mListener = listener;
-        try {
-            if ((listener == null) || !callbackImmediately) {
-                // do nothing
-            } else if (mErrorCode != SipErrorCode.NO_ERROR) {
-                listener.onError(this, mErrorCode, mErrorMessage);
-            } else if (mInCall) {
-                if (mHold) {
-                    listener.onCallHeld(this);
-                } else {
-                    listener.onCallEstablished(this);
-                }
-            } else {
-                int state = getState();
-                switch (state) {
-                    case SipSessionState.READY_TO_CALL:
-                        listener.onReadyToCall(this);
-                        break;
-                    case SipSessionState.INCOMING_CALL:
-                        listener.onRinging(this, getPeerProfile(mSipSession));
-                        break;
-                    case SipSessionState.OUTGOING_CALL:
-                        listener.onCalling(this);
-                        break;
-                    case SipSessionState.OUTGOING_CALL_RING_BACK:
-                        listener.onRingingBack(this);
-                        break;
-                }
-            }
-        } catch (Throwable t) {
-            Log.e(TAG, "setListener()", t);
-        }
-    }
-
-    public synchronized boolean isInCall() {
-        return mInCall;
-    }
-
-    public synchronized boolean isOnHold() {
-        return mHold;
-    }
-
-    public void close() {
-        close(true);
-    }
-
-    private synchronized void close(boolean closeRtp) {
-        if (closeRtp) stopCall(RELEASE_SOCKET);
-        stopRingbackTone();
-        stopRinging();
-
-        mInCall = false;
-        mHold = false;
-        mSessionId = System.currentTimeMillis();
-        mErrorCode = SipErrorCode.NO_ERROR;
-        mErrorMessage = null;
-
-        if (mSipSession != null) {
-            try {
-                mSipSession.setListener(null);
-            } catch (RemoteException e) {
-                // don't care
-            }
-            mSipSession = null;
-        }
-    }
-
-    public synchronized SipProfile getLocalProfile() {
-        return mLocalProfile;
-    }
-
-    public synchronized SipProfile getPeerProfile() {
-        try {
-            return (mSipSession == null) ? null : mSipSession.getPeerProfile();
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-
-    public synchronized int getState() {
-        if (mSipSession == null) return SipSessionState.READY_TO_CALL;
-        try {
-            return mSipSession.getState();
-        } catch (RemoteException e) {
-            return SipSessionState.REMOTE_ERROR;
-        }
-    }
-
-
-    public synchronized ISipSession getSipSession() {
-        return mSipSession;
-    }
-
-    @Override
-    public void onCalling(ISipSession session) {
-        Log.d(TAG, "calling... " + session);
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onCalling(this);
-            } catch (Throwable t) {
-                Log.e(TAG, "onCalling()", t);
-            }
-        }
-    }
-
-    @Override
-    public void onRingingBack(ISipSession session) {
-        Log.d(TAG, "sip call ringing back: " + session);
-        if (!mInCall) startRingbackTone();
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onRingingBack(this);
-            } catch (Throwable t) {
-                Log.e(TAG, "onRingingBack()", t);
-            }
-        }
-    }
-
-    @Override
-    public synchronized void onRinging(ISipSession session,
-            SipProfile peerProfile, String sessionDescription) {
-        try {
-            if ((mSipSession == null) || !mInCall
-                    || !session.getCallId().equals(mSipSession.getCallId())) {
-                // should not happen
-                session.endCall();
-                return;
-            }
-
-            // session changing request
-            try {
-                String answer = createAnswer(sessionDescription).encode();
-                mSipSession.answerCall(answer, SESSION_TIMEOUT);
-            } catch (Throwable e) {
-                Log.e(TAG, "onRinging()", e);
-                session.endCall();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "onRinging()", e);
-        }
-    }
-
-    @Override
-    public void onCallEstablished(ISipSession session,
-            String sessionDescription) {
-        stopRingbackTone();
-        stopRinging();
-        mPeerSd = sessionDescription;
-        Log.v(TAG, "onCallEstablished()" + mPeerSd);
-
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                if (mHold) {
-                    listener.onCallHeld(this);
-                } else {
-                    listener.onCallEstablished(this);
-                }
-            } catch (Throwable t) {
-                Log.e(TAG, "onCallEstablished()", t);
-            }
-        }
-    }
-
-    @Override
-    public void onCallEnded(ISipSession session) {
-        Log.d(TAG, "sip call ended: " + session);
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onCallEnded(this);
-            } catch (Throwable t) {
-                Log.e(TAG, "onCallEnded()", t);
-            }
-        }
-        close();
-    }
-
-    @Override
-    public void onCallBusy(ISipSession session) {
-        Log.d(TAG, "sip call busy: " + session);
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onCallBusy(this);
-            } catch (Throwable t) {
-                Log.e(TAG, "onCallBusy()", t);
-            }
-        }
-        close(false);
-    }
-
-    @Override
-    public void onCallChangeFailed(ISipSession session, int errorCode,
-            String message) {
-        Log.d(TAG, "sip call change failed: " + message);
-        mErrorCode = errorCode;
-        mErrorMessage = message;
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onError(this, mErrorCode, message);
-            } catch (Throwable t) {
-                Log.e(TAG, "onCallBusy()", t);
-            }
-        }
-    }
-
-    @Override
-    public void onError(ISipSession session, int errorCode, String message) {
-        Log.d(TAG, "sip session error: " + SipErrorCode.toString(errorCode)
-                + ": " + message);
-        mErrorCode = errorCode;
-        mErrorMessage = message;
-        Listener listener = mListener;
-        if (listener != null) {
-            try {
-                listener.onError(this, errorCode, message);
-            } catch (Throwable t) {
-                Log.e(TAG, "onError()", t);
-            }
-        }
-        synchronized (this) {
-            if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST)
-                    || !isInCall()) {
-                close(true);
-            }
-        }
-    }
-
-    public synchronized void attachCall(ISipSession session,
-            String sessionDescription) throws SipException {
-        mSipSession = session;
-        mPeerSd = sessionDescription;
-        Log.v(TAG, "attachCall()" + mPeerSd);
-        try {
-            session.setListener(this);
-            if (getState() == SipSessionState.INCOMING_CALL) startRinging();
-        } catch (Throwable e) {
-            Log.e(TAG, "attachCall()", e);
-            throwSipException(e);
-        }
-    }
-
-    public synchronized void makeCall(SipProfile peerProfile,
-            SipManager sipManager, int timeout) throws SipException {
-        try {
-            mSipSession = sipManager.createSipSession(mLocalProfile, this);
-            if (mSipSession == null) {
-                throw new SipException(
-                        "Failed to create SipSession; network available?");
-            }
-            mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
-            mSipSession.makeCall(peerProfile, createOffer().encode(), timeout);
-        } catch (Throwable e) {
-            if (e instanceof SipException) {
-                throw (SipException) e;
-            } else {
-                throwSipException(e);
-            }
-        }
-    }
-
-    public synchronized void endCall() throws SipException {
-        try {
-            stopRinging();
-            stopCall(RELEASE_SOCKET);
-            mInCall = false;
-
-            // perform the above local ops first and then network op
-            if (mSipSession != null) mSipSession.endCall();
-        } catch (Throwable e) {
-            throwSipException(e);
-        }
-    }
-
-    public synchronized void answerCall(int timeout) throws SipException {
-        try {
-            stopRinging();
-            mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
-            mSipSession.answerCall(createAnswer(mPeerSd).encode(), timeout);
-        } catch (Throwable e) {
-            Log.e(TAG, "answerCall()", e);
-            throwSipException(e);
-        }
-    }
-
-    public synchronized void holdCall(int timeout) throws SipException {
-        if (mHold) return;
-        try {
-            mSipSession.changeCall(createHoldOffer().encode(), timeout);
-        } catch (Throwable e) {
-            throwSipException(e);
-        }
-        mHold = true;
-        AudioGroup audioGroup = getAudioGroup();
-        if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
-    }
-
-    public synchronized void continueCall(int timeout) throws SipException {
-        if (!mHold) return;
-        try {
-            mSipSession.changeCall(createContinueOffer().encode(), timeout);
-        } catch (Throwable e) {
-            throwSipException(e);
-        }
-        mHold = false;
-        AudioGroup audioGroup = getAudioGroup();
-        if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL);
-    }
-
-    private SimpleSessionDescription createOffer() {
-        SimpleSessionDescription offer =
-                new SimpleSessionDescription(mSessionId, getLocalIp());
-        AudioCodec[] codecs = AudioCodec.getCodecs();
-        Media media = offer.newMedia(
-                "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
-        for (AudioCodec codec : AudioCodec.getCodecs()) {
-            media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
-        }
-        media.setRtpPayload(127, "telephone-event/8000", "0-15");
-        return offer;
-    }
-
-    private SimpleSessionDescription createAnswer(String offerSd) {
-        SimpleSessionDescription offer =
-                new SimpleSessionDescription(offerSd);
-        SimpleSessionDescription answer =
-                new SimpleSessionDescription(mSessionId, getLocalIp());
-        AudioCodec codec = null;
-        for (Media media : offer.getMedia()) {
-            if ((codec == null) && (media.getPort() > 0)
-                    && "audio".equals(media.getType())
-                    && "RTP/AVP".equals(media.getProtocol())) {
-                // Find the first audio codec we supported.
-                for (int type : media.getRtpPayloadTypes()) {
-                    codec = AudioCodec.getCodec(type, media.getRtpmap(type),
-                            media.getFmtp(type));
-                    if (codec != null) {
-                        break;
-                    }
-                }
-                if (codec != null) {
-                    Media reply = answer.newMedia(
-                            "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
-                    reply.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
-
-                    // Check if DTMF is supported in the same media.
-                    for (int type : media.getRtpPayloadTypes()) {
-                        String rtpmap = media.getRtpmap(type);
-                        if ((type != codec.type) && (rtpmap != null)
-                                && rtpmap.startsWith("telephone-event")) {
-                            reply.setRtpPayload(
-                                    type, rtpmap, media.getFmtp(type));
-                        }
-                    }
-
-                    // Handle recvonly and sendonly.
-                    if (media.getAttribute("recvonly") != null) {
-                        answer.setAttribute("sendonly", "");
-                    } else if(media.getAttribute("sendonly") != null) {
-                        answer.setAttribute("recvonly", "");
-                    } else if(offer.getAttribute("recvonly") != null) {
-                        answer.setAttribute("sendonly", "");
-                    } else if(offer.getAttribute("sendonly") != null) {
-                        answer.setAttribute("recvonly", "");
-                    }
-                    continue;
-                }
-            }
-            // Reject the media.
-            Media reply = answer.newMedia(
-                    media.getType(), 0, 1, media.getProtocol());
-            for (String format : media.getFormats()) {
-                reply.setFormat(format, null);
-            }
-        }
-        if (codec == null) {
-            throw new IllegalStateException("Reject SDP: no suitable codecs");
-        }
-        return answer;
-    }
-
-    private SimpleSessionDescription createHoldOffer() {
-        SimpleSessionDescription offer = createContinueOffer();
-        offer.setAttribute("sendonly", "");
-        return offer;
-    }
-
-    private SimpleSessionDescription createContinueOffer() {
-        SimpleSessionDescription offer =
-                new SimpleSessionDescription(mSessionId, getLocalIp());
-        Media media = offer.newMedia(
-                "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
-        AudioCodec codec = mAudioStream.getCodec();
-        media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
-        int dtmfType = mAudioStream.getDtmfType();
-        if (dtmfType != -1) {
-            media.setRtpPayload(dtmfType, "telephone-event/8000", "0-15");
-        }
-        return offer;
-    }
-
-    private void grabWifiHighPerfLock() {
-        if (mWifiHighPerfLock == null) {
-            Log.v(TAG, "acquire wifi high perf lock");
-            mWifiHighPerfLock = ((WifiManager)
-                    mContext.getSystemService(Context.WIFI_SERVICE))
-                    .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG);
-            mWifiHighPerfLock.acquire();
-        }
-    }
-
-    private void releaseWifiHighPerfLock() {
-        if (mWifiHighPerfLock != null) {
-            Log.v(TAG, "release wifi high perf lock");
-            mWifiHighPerfLock.release();
-            mWifiHighPerfLock = null;
-        }
-    }
-
-    private boolean isWifiOn() {
-        return (mWm.getConnectionInfo().getBSSID() == null) ? false : true;
-    }
-
-    public synchronized void toggleMute() {
-        AudioGroup audioGroup = getAudioGroup();
-        if (audioGroup != null) {
-            audioGroup.setMode(
-                    mMuted ? AudioGroup.MODE_NORMAL : AudioGroup.MODE_MUTED);
-            mMuted = !mMuted;
-        }
-    }
-
-    public synchronized boolean isMuted() {
-        return mMuted;
-    }
-
-    public synchronized void setSpeakerMode(boolean speakerMode) {
-        ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
-                .setSpeakerphoneOn(speakerMode);
-    }
-
-    public void sendDtmf(int code) {
-        sendDtmf(code, null);
-    }
-
-    public synchronized void sendDtmf(int code, Message result) {
-        AudioGroup audioGroup = getAudioGroup();
-        if ((audioGroup != null) && (mSipSession != null)
-                && (SipSessionState.IN_CALL == getState())) {
-            Log.v(TAG, "send DTMF: " + code);
-            audioGroup.sendDtmf(code);
-        }
-        if (result != null) result.sendToTarget();
-    }
-
-    public synchronized AudioStream getAudioStream() {
-        return mAudioStream;
-    }
-
-    public synchronized AudioGroup getAudioGroup() {
-        if (mAudioGroup != null) return mAudioGroup;
-        return ((mAudioStream == null) ? null : mAudioStream.getGroup());
-    }
-
-    public synchronized void setAudioGroup(AudioGroup group) {
-        if ((mAudioStream != null) && (mAudioStream.getGroup() != null)) {
-            mAudioStream.join(group);
-        }
-        mAudioGroup = group;
-    }
-
-    public void startAudio() {
-        try {
-            startAudioInternal();
-        } catch (UnknownHostException e) {
-            onError(mSipSession, SipErrorCode.PEER_NOT_REACHABLE,
-                    e.getMessage());
-        } catch (Throwable e) {
-            onError(mSipSession, SipErrorCode.CLIENT_ERROR,
-                    e.getMessage());
-        }
-    }
-
-    private synchronized void startAudioInternal() throws UnknownHostException {
-        if (mPeerSd == null) {
-            Log.v(TAG, "startAudioInternal() mPeerSd = null");
-            throw new IllegalStateException("mPeerSd = null");
-        }
-
-        stopCall(DONT_RELEASE_SOCKET);
-        mInCall = true;
-
-        // Run exact the same logic in createAnswer() to setup mAudioStream.
-        SimpleSessionDescription offer =
-                new SimpleSessionDescription(mPeerSd);
-        AudioStream stream = mAudioStream;
-        AudioCodec codec = null;
-        for (Media media : offer.getMedia()) {
-            if ((codec == null) && (media.getPort() > 0)
-                    && "audio".equals(media.getType())
-                    && "RTP/AVP".equals(media.getProtocol())) {
-                // Find the first audio codec we supported.
-                for (int type : media.getRtpPayloadTypes()) {
-                    codec = AudioCodec.getCodec(
-                            type, media.getRtpmap(type), media.getFmtp(type));
-                    if (codec != null) {
-                        break;
-                    }
-                }
-
-                if (codec != null) {
-                    // Associate with the remote host.
-                    String address = media.getAddress();
-                    if (address == null) {
-                        address = offer.getAddress();
-                    }
-                    stream.associate(InetAddress.getByName(address),
-                            media.getPort());
-
-                    stream.setDtmfType(-1);
-                    stream.setCodec(codec);
-                    // Check if DTMF is supported in the same media.
-                    for (int type : media.getRtpPayloadTypes()) {
-                        String rtpmap = media.getRtpmap(type);
-                        if ((type != codec.type) && (rtpmap != null)
-                                && rtpmap.startsWith("telephone-event")) {
-                            stream.setDtmfType(type);
-                        }
-                    }
-
-                    // Handle recvonly and sendonly.
-                    if (mHold) {
-                        stream.setMode(RtpStream.MODE_NORMAL);
-                    } else if (media.getAttribute("recvonly") != null) {
-                        stream.setMode(RtpStream.MODE_SEND_ONLY);
-                    } else if(media.getAttribute("sendonly") != null) {
-                        stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
-                    } else if(offer.getAttribute("recvonly") != null) {
-                        stream.setMode(RtpStream.MODE_SEND_ONLY);
-                    } else if(offer.getAttribute("sendonly") != null) {
-                        stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
-                    } else {
-                        stream.setMode(RtpStream.MODE_NORMAL);
-                    }
-                    break;
-                }
-            }
-        }
-        if (codec == null) {
-            throw new IllegalStateException("Reject SDP: no suitable codecs");
-        }
-
-        if (isWifiOn()) grabWifiHighPerfLock();
-
-        if (!mHold) {
-            /* The recorder volume will be very low if the device is in
-             * IN_CALL mode. Therefore, we have to set the mode to NORMAL
-             * in order to have the normal microphone level.
-             */
-            ((AudioManager) mContext.getSystemService
-                    (Context.AUDIO_SERVICE))
-                    .setMode(AudioManager.MODE_NORMAL);
-        }
-
-        // AudioGroup logic:
-        AudioGroup audioGroup = getAudioGroup();
-        if (mHold) {
-            if (audioGroup != null) {
-                audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
-            }
-            // don't create an AudioGroup here; doing so will fail if
-            // there's another AudioGroup out there that's active
-        } else {
-            if (audioGroup == null) audioGroup = new AudioGroup();
-            mAudioStream.join(audioGroup);
-            if (mMuted) {
-                audioGroup.setMode(AudioGroup.MODE_MUTED);
-            } else {
-                audioGroup.setMode(AudioGroup.MODE_NORMAL);
-            }
-        }
-    }
-
-    private void stopCall(boolean releaseSocket) {
-        Log.d(TAG, "stop audiocall");
-        releaseWifiHighPerfLock();
-        if (mAudioStream != null) {
-            mAudioStream.join(null);
-
-            if (releaseSocket) {
-                mAudioStream.release();
-                mAudioStream = null;
-            }
-        }
-    }
-
-    private String getLocalIp() {
-        try {
-            return mSipSession.getLocalIp();
-        } catch (RemoteException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    public synchronized void setRingbackToneEnabled(boolean enabled) {
-        mRingbackToneEnabled = enabled;
-    }
-
-    public synchronized void setRingtoneEnabled(boolean enabled) {
-        mRingtoneEnabled = enabled;
-    }
-
-    private void startRingbackTone() {
-        if (!mRingbackToneEnabled) return;
-        if (mRingbackTone == null) {
-            // The volume relative to other sounds in the stream
-            int toneVolume = 80;
-            mRingbackTone = new ToneGenerator(
-                    AudioManager.STREAM_VOICE_CALL, toneVolume);
-        }
-        mRingbackTone.startTone(ToneGenerator.TONE_CDMA_LOW_PBX_L);
-    }
-
-    private void stopRingbackTone() {
-        if (mRingbackTone != null) {
-            mRingbackTone.stopTone();
-            mRingbackTone.release();
-            mRingbackTone = null;
-        }
-    }
-
-    private void startRinging() {
-        if (!mRingtoneEnabled) return;
-        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
-                .vibrate(new long[] {0, 1000, 1000}, 1);
-        AudioManager am = (AudioManager)
-                mContext.getSystemService(Context.AUDIO_SERVICE);
-        if (am.getStreamVolume(AudioManager.STREAM_RING) > 0) {
-            String ringtoneUri =
-                    Settings.System.DEFAULT_RINGTONE_URI.toString();
-            mRingtone = RingtoneManager.getRingtone(mContext,
-                    Uri.parse(ringtoneUri));
-            mRingtone.play();
-        }
-    }
-
-    private void stopRinging() {
-        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
-                .cancel();
-        if (mRingtone != null) mRingtone.stop();
-    }
-
-    private void throwSipException(Throwable throwable) throws SipException {
-        if (throwable instanceof SipException) {
-            throw (SipException) throwable;
-        } else {
-            throw new SipException("", throwable);
-        }
-    }
-
-    private SipProfile getPeerProfile(ISipSession session) {
-        try {
-            return session.getPeerProfile();
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-}
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 31768d7..5976a04 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -30,8 +30,9 @@
  * The class provides API for various SIP related tasks. Specifically, the API
  * allows an application to:
  * <ul>
- * <li>register a {@link SipProfile} to have the background SIP service listen
- *      to incoming calls and broadcast them with registered command string. See
+ * <li>open a {@link SipProfile} to get ready for making outbound calls or have
+ *      the background SIP service listen to incoming calls and broadcast them
+ *      with registered command string. See
  *      {@link #open(SipProfile, String, SipRegistrationListener)},
  *      {@link #open(SipProfile)}, {@link #close}, {@link #isOpened} and
  *      {@link #isRegistered}. It also facilitates handling of the incoming call
@@ -40,39 +41,59 @@
  *      {@link #getOfferSessionDescription} and {@link #takeAudioCall}.</li>
  * <li>make/take SIP-based audio calls. See
  *      {@link #makeAudioCall} and {@link #takeAudioCall}.</li>
- * <li>register/unregister with a SIP service provider. See
+ * <li>register/unregister with a SIP service provider manually. See
  *      {@link #register} and {@link #unregister}.</li>
- * <li>process SIP events directly with a {@link ISipSession} created by
+ * <li>process SIP events directly with a {@link SipSession} created by
  *      {@link #createSipSession}.</li>
  * </ul>
  * @hide
  */
 public class SipManager {
-    /** @hide */
-    public static final String SIP_INCOMING_CALL_ACTION =
+    /**
+     * Action string for the incoming call intent for the Phone app.
+     * Internal use only.
+     * @hide
+     */
+    public static final String ACTION_SIP_INCOMING_CALL =
             "com.android.phone.SIP_INCOMING_CALL";
-    /** @hide */
-    public static final String SIP_ADD_PHONE_ACTION =
+    /**
+     * Action string for the add-phone intent.
+     * Internal use only.
+     * @hide
+     */
+    public static final String ACTION_SIP_ADD_PHONE =
             "com.android.phone.SIP_ADD_PHONE";
-    /** @hide */
-    public static final String SIP_REMOVE_PHONE_ACTION =
+    /**
+     * Action string for the remove-phone intent.
+     * Internal use only.
+     * @hide
+     */
+    public static final String ACTION_SIP_REMOVE_PHONE =
             "com.android.phone.SIP_REMOVE_PHONE";
-    /** @hide */
-    public static final String LOCAL_URI_KEY = "LOCAL SIPURI";
+    /**
+     * Part of the ACTION_SIP_ADD_PHONE and ACTION_SIP_REMOVE_PHONE intents.
+     * Internal use only.
+     * @hide
+     */
+    public static final String EXTRA_LOCAL_URI = "android:localSipUri";
 
-    private static final String CALL_ID_KEY = "CallID";
-    private static final String OFFER_SD_KEY = "OfferSD";
+    /** Part of the incoming call intent. */
+    public static final String EXTRA_CALL_ID = "android:sipCallID";
+
+    /** Part of the incoming call intent. */
+    public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
 
     private ISipService mSipService;
+    private Context mContext;
 
     /**
-     * Gets a manager instance. Returns null if SIP API is not supported.
+     * Creates a manager instance. Returns null if SIP API is not supported.
      *
-     * @param context application context for checking if SIP API is supported
+     * @param context application context for creating the manager object
      * @return the manager instance or null if SIP API is not supported
      */
-    public static SipManager getInstance(Context context) {
-        return (isApiSupported(context) ? new SipManager() : null);
+    public static SipManager newInstance(Context context) {
+        return (isApiSupported(context) ? new SipManager(context) : null);
     }
 
     /**
@@ -80,7 +101,7 @@
      */
     public static boolean isApiSupported(Context context) {
         return true;
-        /* 
+        /* TODO: uncomment this before ship
         return context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_SIP);
          */
@@ -91,7 +112,7 @@
      */
     public static boolean isVoipSupported(Context context) {
         return true;
-        /* 
+        /* TODO: uncomment this before ship
         return context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context);
          */
@@ -105,23 +126,21 @@
                 com.android.internal.R.bool.config_sip_wifi_only);
     }
 
-    private SipManager() {
+    private SipManager(Context context) {
+        mContext = context;
         createSipService();
     }
 
     private void createSipService() {
-        if (mSipService != null) return;
         IBinder b = ServiceManager.getService(Context.SIP_SERVICE);
         mSipService = ISipService.Stub.asInterface(b);
     }
 
     /**
-     * Opens the profile for making calls and/or receiving calls. Subsequent
-     * SIP calls can be made through the default phone UI. The caller may also
-     * make subsequent calls through {@link #makeAudioCall}.
-     * If the receiving-call option is enabled in the profile, the SIP service
-     * will register the profile to the corresponding server periodically in
-     * order to receive calls from the server.
+     * Opens the profile for making calls. The caller may make subsequent calls
+     * through {@link #makeAudioCall}. If one also wants to receive calls on the
+     * profile, use {@link #open(SipProfile, String, SipRegistrationListener)}
+     * instead.
      *
      * @param localProfile the SIP profile to make calls from
      * @throws SipException if the profile contains incorrect settings or
@@ -136,12 +155,11 @@
     }
 
     /**
-     * Opens the profile for making calls and/or receiving calls. Subsequent
-     * SIP calls can be made through the default phone UI. The caller may also
-     * make subsequent calls through {@link #makeAudioCall}.
-     * If the receiving-call option is enabled in the profile, the SIP service
-     * will register the profile to the corresponding server periodically in
-     * order to receive calls from the server.
+     * Opens the profile for making calls and/or receiving calls. The caller may
+     * make subsequent calls through {@link #makeAudioCall}. If the
+     * auto-registration option is enabled in the profile, the SIP service
+     * will register the profile to the corresponding SIP provider periodically
+     * in order to receive calls from the provider.
      *
      * @param localProfile the SIP profile to receive incoming calls for
      * @param incomingCallBroadcastAction the action to be broadcast when an
@@ -195,7 +213,8 @@
     }
 
     /**
-     * Checks if the specified profile is enabled to receive calls.
+     * Checks if the specified profile is opened in the SIP service for
+     * making and/or receiving calls.
      *
      * @param localProfileUri the URI of the profile in question
      * @return true if the profile is enabled to receive calls
@@ -210,11 +229,16 @@
     }
 
     /**
-     * Checks if the specified profile is registered to the server for
-     * receiving calls.
+     * Checks if the SIP service has successfully registered the profile to the
+     * SIP provider (specified in the profile) for receiving calls. Returning
+     * true from this method also implies the profile is opened
+     * ({@link #isOpened}).
      *
      * @param localProfileUri the URI of the profile in question
-     * @return true if the profile is registered to the server
+     * @return true if the profile is registered to the SIP provider; false if
+     *        the profile has not been opened in the SIP service or the SIP
+     *        service has not yet successfully registered the profile to the SIP
+     *        provider
      * @throws SipException if calling the SIP service results in an error
      */
     public boolean isRegistered(String localProfileUri) throws SipException {
@@ -231,7 +255,6 @@
      * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param context context to create a {@link SipAudioCall} object
      * @param localProfile the SIP profile to make the call from
      * @param peerProfile the SIP profile to make the call to
      * @param listener to listen to the call events from {@link SipAudioCall};
@@ -241,10 +264,10 @@
      * @throws SipException if calling the SIP service results in an error
      * @see SipAudioCall.Listener.onError
      */
-    public SipAudioCall makeAudioCall(Context context, SipProfile localProfile,
+    public SipAudioCall makeAudioCall(SipProfile localProfile,
             SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
             throws SipException {
-        SipAudioCall call = new SipAudioCallImpl(context, localProfile);
+        SipAudioCall call = new SipAudioCall(mContext, localProfile);
         call.setListener(listener);
         call.makeCall(peerProfile, this, timeout);
         return call;
@@ -257,7 +280,6 @@
      * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
-     * @param context context to create a {@link SipAudioCall} object
      * @param localProfileUri URI of the SIP profile to make the call from
      * @param peerProfileUri URI of the SIP profile to make the call to
      * @param listener to listen to the call events from {@link SipAudioCall};
@@ -267,11 +289,11 @@
      * @throws SipException if calling the SIP service results in an error
      * @see SipAudioCall.Listener.onError
      */
-    public SipAudioCall makeAudioCall(Context context, String localProfileUri,
+    public SipAudioCall makeAudioCall(String localProfileUri,
             String peerProfileUri, SipAudioCall.Listener listener, int timeout)
             throws SipException {
         try {
-            return makeAudioCall(context,
+            return makeAudioCall(
                     new SipProfile.Builder(localProfileUri).build(),
                     new SipProfile.Builder(peerProfileUri).build(), listener,
                     timeout);
@@ -281,15 +303,14 @@
     }
 
     /**
-     * The method calls {@code takeAudioCall(context, incomingCallIntent,
+     * The method calls {@code takeAudioCall(incomingCallIntent,
      * listener, true}.
      *
-     * @see #takeAudioCall(Context, Intent, SipAudioCall.Listener, boolean)
+     * @see #takeAudioCall(Intent, SipAudioCall.Listener, boolean)
      */
-    public SipAudioCall takeAudioCall(Context context,
-            Intent incomingCallIntent, SipAudioCall.Listener listener)
-            throws SipException {
-        return takeAudioCall(context, incomingCallIntent, listener, true);
+    public SipAudioCall takeAudioCall(Intent incomingCallIntent,
+            SipAudioCall.Listener listener) throws SipException {
+        return takeAudioCall(incomingCallIntent, listener, true);
     }
 
     /**
@@ -298,16 +319,15 @@
      * {@link SipAudioCall.Listener#onRinging}
      * callback.
      *
-     * @param context context to create a {@link SipAudioCall} object
      * @param incomingCallIntent the incoming call broadcast intent
      * @param listener to listen to the call events from {@link SipAudioCall};
      *      can be null
      * @return a {@link SipAudioCall} object
      * @throws SipException if calling the SIP service results in an error
      */
-    public SipAudioCall takeAudioCall(Context context,
-            Intent incomingCallIntent, SipAudioCall.Listener listener,
-            boolean ringtoneEnabled) throws SipException {
+    public SipAudioCall takeAudioCall(Intent incomingCallIntent,
+            SipAudioCall.Listener listener, boolean ringtoneEnabled)
+            throws SipException {
         if (incomingCallIntent == null) return null;
 
         String callId = getCallId(incomingCallIntent);
@@ -324,10 +344,10 @@
         try {
             ISipSession session = mSipService.getPendingSession(callId);
             if (session == null) return null;
-            SipAudioCall call = new SipAudioCallImpl(
-                    context, session.getLocalProfile());
+            SipAudioCall call = new SipAudioCall(
+                    mContext, session.getLocalProfile());
             call.setRingtoneEnabled(ringtoneEnabled);
-            call.attachCall(session, offerSd);
+            call.attachCall(new SipSession(session), offerSd);
             call.setListener(listener);
             return call;
         } catch (Throwable t) {
@@ -355,7 +375,7 @@
      * @return the call ID or null if the intent does not contain it
      */
     public static String getCallId(Intent incomingCallIntent) {
-        return incomingCallIntent.getStringExtra(CALL_ID_KEY);
+        return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
     }
 
     /**
@@ -367,30 +387,30 @@
      *      have it
      */
     public static String getOfferSessionDescription(Intent incomingCallIntent) {
-        return incomingCallIntent.getStringExtra(OFFER_SD_KEY);
+        return incomingCallIntent.getStringExtra(EXTRA_OFFER_SD);
     }
 
     /**
      * Creates an incoming call broadcast intent.
      *
-     * @param action the action string to broadcast
      * @param callId the call ID of the incoming call
      * @param sessionDescription the session description of the incoming call
      * @return the incoming call intent
      * @hide
      */
-    public static Intent createIncomingCallBroadcast(String action,
-            String callId, String sessionDescription) {
-        Intent intent = new Intent(action);
-        intent.putExtra(CALL_ID_KEY, callId);
-        intent.putExtra(OFFER_SD_KEY, sessionDescription);
+    public static Intent createIncomingCallBroadcast(String callId,
+            String sessionDescription) {
+        Intent intent = new Intent();
+        intent.putExtra(EXTRA_CALL_ID, callId);
+        intent.putExtra(EXTRA_OFFER_SD, sessionDescription);
         return intent;
     }
 
     /**
-     * Registers the profile to the corresponding server for receiving calls.
-     * {@link #open} is still needed to be called at least once in order for
-     * the SIP service to broadcast an intent when an incoming call is received.
+     * Manually registers the profile to the corresponding SIP provider for
+     * receiving calls. {@link #open(SipProfile, String, SipRegistrationListener)}
+     * is still needed to be called at least once in order for the SIP service
+     * to broadcast an intent when an incoming call is received.
      *
      * @param localProfile the SIP profile to register with
      * @param expiryTime registration expiration time (in seconds)
@@ -409,8 +429,10 @@
     }
 
     /**
-     * Unregisters the profile from the corresponding server for not receiving
-     * further calls.
+     * Manually unregisters the profile from the corresponding SIP provider for
+     * stop receiving further calls. This may interference with the auto
+     * registration process in the SIP service if the auto-registration option
+     * in the profile is enabled.
      *
      * @param localProfile the SIP profile to register with
      * @param listener to listen to the registration events
@@ -460,10 +482,11 @@
      * @param localProfile the SIP profile the session is associated with
      * @param listener to listen to SIP session events
      */
-    public ISipSession createSipSession(SipProfile localProfile,
-            ISipSessionListener listener) throws SipException {
+    public SipSession createSipSession(SipProfile localProfile,
+            SipSession.Listener listener) throws SipException {
         try {
-            return mSipService.createSession(localProfile, listener);
+            ISipSession s = mSipService.createSession(localProfile, null);
+            return new SipSession(s, listener);
         } catch (RemoteException e) {
             throw new SipException("createSipSession()", e);
         }
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
index 88bfba9..6d5cb3c 100644
--- a/voip/java/android/net/sip/SipProfile.java
+++ b/voip/java/android/net/sip/SipProfile.java
@@ -48,7 +48,6 @@
     private boolean mAutoRegistration = true;
     private transient int mCallingUid = 0;
 
-    /** @hide */
     public static final Parcelable.Creator<SipProfile> CREATOR =
             new Parcelable.Creator<SipProfile>() {
                 public SipProfile createFromParcel(Parcel in) {
@@ -287,7 +286,7 @@
         mCallingUid = in.readInt();
     }
 
-    /** @hide */
+    @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeSerializable(mAddress);
         out.writeString(mProxyAddress);
@@ -300,7 +299,7 @@
         out.writeInt(mCallingUid);
     }
 
-    /** @hide */
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/voip/java/android/net/sip/SipSession.java b/voip/java/android/net/sip/SipSession.java
new file mode 100644
index 0000000..0cc7206
--- /dev/null
+++ b/voip/java/android/net/sip/SipSession.java
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.sip;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A SIP session that is associated with a SIP dialog or a standalone
+ * transaction not within a dialog.
+ * @hide
+ */
+public final class SipSession {
+    private static final String TAG = "SipSession";
+
+    /**
+     * Defines {@link SipSession} states.
+     * @hide
+     */
+    public static class State {
+        /** When session is ready to initiate a call or transaction. */
+        public static final int READY_TO_CALL = 0;
+
+        /** When the registration request is sent out. */
+        public static final int REGISTERING = 1;
+
+        /** When the unregistration request is sent out. */
+        public static final int DEREGISTERING = 2;
+
+        /** When an INVITE request is received. */
+        public static final int INCOMING_CALL = 3;
+
+        /** When an OK response is sent for the INVITE request received. */
+        public static final int INCOMING_CALL_ANSWERING = 4;
+
+        /** When an INVITE request is sent. */
+        public static final int OUTGOING_CALL = 5;
+
+        /** When a RINGING response is received for the INVITE request sent. */
+        public static final int OUTGOING_CALL_RING_BACK = 6;
+
+        /** When a CANCEL request is sent for the INVITE request sent. */
+        public static final int OUTGOING_CALL_CANCELING = 7;
+
+        /** When a call is established. */
+        public static final int IN_CALL = 8;
+
+        /** When an OPTIONS request is sent. */
+        public static final int PINGING = 9;
+
+        /** Not defined. */
+        public static final int NOT_DEFINED = 101;
+
+        /**
+         * Converts the state to string.
+         */
+        public static String toString(int state) {
+            switch (state) {
+                case READY_TO_CALL:
+                    return "READY_TO_CALL";
+                case REGISTERING:
+                    return "REGISTERING";
+                case DEREGISTERING:
+                    return "DEREGISTERING";
+                case INCOMING_CALL:
+                    return "INCOMING_CALL";
+                case INCOMING_CALL_ANSWERING:
+                    return "INCOMING_CALL_ANSWERING";
+                case OUTGOING_CALL:
+                    return "OUTGOING_CALL";
+                case OUTGOING_CALL_RING_BACK:
+                    return "OUTGOING_CALL_RING_BACK";
+                case OUTGOING_CALL_CANCELING:
+                    return "OUTGOING_CALL_CANCELING";
+                case IN_CALL:
+                    return "IN_CALL";
+                case PINGING:
+                    return "PINGING";
+                default:
+                    return "NOT_DEFINED";
+            }
+        }
+
+        private State() {
+        }
+    }
+
+    /**
+     * Listener class that listens to {@link SipSession} events.
+     * @hide
+     */
+    public static class Listener {
+        /**
+         * Called when an INVITE request is sent to initiate a new call.
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onCalling(SipSession session) {
+        }
+
+        /**
+         * Called when an INVITE request is received.
+         *
+         * @param session the session object that carries out the transaction
+         * @param caller the SIP profile of the caller
+         * @param sessionDescription the caller's session description
+         */
+        public void onRinging(SipSession session, SipProfile caller,
+                String sessionDescription) {
+        }
+
+        /**
+         * Called when a RINGING response is received for the INVITE request sent
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onRingingBack(SipSession session) {
+        }
+
+        /**
+         * Called when the session is established.
+         *
+         * @param session the session object that is associated with the dialog
+         * @param sessionDescription the peer's session description
+         */
+        public void onCallEstablished(SipSession session,
+                String sessionDescription) {
+        }
+
+        /**
+         * Called when the session is terminated.
+         *
+         * @param session the session object that is associated with the dialog
+         */
+        public void onCallEnded(SipSession session) {
+        }
+
+        /**
+         * Called when the peer is busy during session initialization.
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onCallBusy(SipSession session) {
+        }
+
+        /**
+         * Called when an error occurs during session initialization and
+         * termination.
+         *
+         * @param session the session object that carries out the transaction
+         * @param errorCode error code defined in {@link SipErrorCode}
+         * @param errorMessage error message
+         */
+        public void onError(SipSession session, int errorCode,
+                String errorMessage) {
+        }
+
+        /**
+         * Called when an error occurs during session modification negotiation.
+         *
+         * @param session the session object that carries out the transaction
+         * @param errorCode error code defined in {@link SipErrorCode}
+         * @param errorMessage error message
+         */
+        public void onCallChangeFailed(SipSession session, int errorCode,
+                String errorMessage) {
+        }
+
+        /**
+         * Called when a registration request is sent.
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onRegistering(SipSession session) {
+        }
+
+        /**
+         * Called when registration is successfully done.
+         *
+         * @param session the session object that carries out the transaction
+         * @param duration duration in second before the registration expires
+         */
+        public void onRegistrationDone(SipSession session, int duration) {
+        }
+
+        /**
+         * Called when the registration fails.
+         *
+         * @param session the session object that carries out the transaction
+         * @param errorCode error code defined in {@link SipErrorCode}
+         * @param errorMessage error message
+         */
+        public void onRegistrationFailed(SipSession session, int errorCode,
+                String errorMessage) {
+        }
+
+        /**
+         * Called when the registration gets timed out.
+         *
+         * @param session the session object that carries out the transaction
+         */
+        public void onRegistrationTimeout(SipSession session) {
+        }
+    }
+
+    private final ISipSession mSession;
+    private Listener mListener;
+
+    SipSession(ISipSession realSession) {
+        mSession = realSession;
+        if (realSession != null) {
+            try {
+                realSession.setListener(createListener());
+            } catch (RemoteException e) {
+                Log.e(TAG, "SipSession.setListener(): " + e);
+            }
+        }
+    }
+
+    SipSession(ISipSession realSession, Listener listener) {
+        this(realSession);
+        setListener(listener);
+    }
+
+    /**
+     * Gets the IP address of the local host on which this SIP session runs.
+     *
+     * @return the IP address of the local host
+     */
+    public String getLocalIp() {
+        try {
+            return mSession.getLocalIp();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getLocalIp(): " + e);
+            return "127.0.0.1";
+        }
+    }
+
+    /**
+     * Gets the SIP profile that this session is associated with.
+     *
+     * @return the SIP profile that this session is associated with
+     */
+    public SipProfile getLocalProfile() {
+        try {
+            return mSession.getLocalProfile();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getLocalProfile(): " + e);
+            return null;
+        }
+    }
+
+    /**
+     * Gets the SIP profile that this session is connected to. Only available
+     * when the session is associated with a SIP dialog.
+     *
+     * @return the SIP profile that this session is connected to
+     */
+    public SipProfile getPeerProfile() {
+        try {
+            return mSession.getPeerProfile();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getPeerProfile(): " + e);
+            return null;
+        }
+    }
+
+    /**
+     * Gets the session state. The value returned must be one of the states in
+     * {@link SipSessionState}.
+     *
+     * @return the session state
+     */
+    public int getState() {
+        try {
+            return mSession.getState();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getState(): " + e);
+            return State.NOT_DEFINED;
+        }
+    }
+
+    /**
+     * Checks if the session is in a call.
+     *
+     * @return true if the session is in a call
+     */
+    public boolean isInCall() {
+        try {
+            return mSession.isInCall();
+        } catch (RemoteException e) {
+            Log.e(TAG, "isInCall(): " + e);
+            return false;
+        }
+    }
+
+    /**
+     * Gets the call ID of the session.
+     *
+     * @return the call ID
+     */
+    public String getCallId() {
+        try {
+            return mSession.getCallId();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getCallId(): " + e);
+            return null;
+        }
+    }
+
+
+    /**
+     * Sets the listener to listen to the session events. A {@code SipSession}
+     * can only hold one listener at a time. Subsequent calls to this method
+     * override the previous listener.
+     *
+     * @param listener to listen to the session events of this object
+     */
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
+
+    /**
+     * Performs registration to the server specified by the associated local
+     * profile. The session listener is called back upon success or failure of
+     * registration. The method is only valid to call when the session state is
+     * in {@link SipSessionState#READY_TO_CALL}.
+     *
+     * @param duration duration in second before the registration expires
+     * @see Listener
+     */
+    public void register(int duration) {
+        try {
+            mSession.register(duration);
+        } catch (RemoteException e) {
+            Log.e(TAG, "register(): " + e);
+        }
+    }
+
+    /**
+     * Performs unregistration to the server specified by the associated local
+     * profile. Unregistration is technically the same as registration with zero
+     * expiration duration. The session listener is called back upon success or
+     * failure of unregistration. The method is only valid to call when the
+     * session state is in {@link SipSessionState#READY_TO_CALL}.
+     *
+     * @see Listener
+     */
+    public void unregister() {
+        try {
+            mSession.unregister();
+        } catch (RemoteException e) {
+            Log.e(TAG, "unregister(): " + e);
+        }
+    }
+
+    /**
+     * Initiates a call to the specified profile. The session listener is called
+     * back upon defined session events. The method is only valid to call when
+     * the session state is in {@link SipSessionState#READY_TO_CALL}.
+     *
+     * @param callee the SIP profile to make the call to
+     * @param sessionDescription the session description of this call
+     * @param timeout the session will be timed out if the call is not
+     *        established within {@code timeout} seconds. Default value (defined
+     *        by SIP protocol) is used if {@code timeout} is zero or negative.
+     * @see Listener
+     */
+    public void makeCall(SipProfile callee, String sessionDescription,
+            int timeout) {
+        try {
+            mSession.makeCall(callee, sessionDescription, timeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "makeCall(): " + e);
+        }
+    }
+
+    /**
+     * Answers an incoming call with the specified session description. The
+     * method is only valid to call when the session state is in
+     * {@link SipSessionState#INCOMING_CALL}.
+     *
+     * @param sessionDescription the session description to answer this call
+     * @param timeout the session will be timed out if the call is not
+     *        established within {@code timeout} seconds. Default value (defined
+     *        by SIP protocol) is used if {@code timeout} is zero or negative.
+     */
+    public void answerCall(String sessionDescription, int timeout) {
+        try {
+            mSession.answerCall(sessionDescription, timeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "answerCall(): " + e);
+        }
+    }
+
+    /**
+     * Ends an established call, terminates an outgoing call or rejects an
+     * incoming call. The method is only valid to call when the session state is
+     * in {@link SipSessionState#IN_CALL},
+     * {@link SipSessionState#INCOMING_CALL},
+     * {@link SipSessionState#OUTGOING_CALL} or
+     * {@link SipSessionState#OUTGOING_CALL_RING_BACK}.
+     */
+    public void endCall() {
+        try {
+            mSession.endCall();
+        } catch (RemoteException e) {
+            Log.e(TAG, "endCall(): " + e);
+        }
+    }
+
+    /**
+     * Changes the session description during a call. The method is only valid
+     * to call when the session state is in {@link SipSessionState#IN_CALL}.
+     *
+     * @param sessionDescription the new session description
+     * @param timeout the session will be timed out if the call is not
+     *        established within {@code timeout} seconds. Default value (defined
+     *        by SIP protocol) is used if {@code timeout} is zero or negative.
+     */
+    public void changeCall(String sessionDescription, int timeout) {
+        try {
+            mSession.changeCall(sessionDescription, timeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "changeCall(): " + e);
+        }
+    }
+
+    ISipSession getRealSession() {
+        return mSession;
+    }
+
+    private ISipSessionListener createListener() {
+        return new ISipSessionListener.Stub() {
+            public void onCalling(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onCalling(SipSession.this);
+                }
+            }
+
+            public void onRinging(ISipSession session, SipProfile caller,
+                    String sessionDescription) {
+                if (mListener != null) {
+                    mListener.onRinging(SipSession.this, caller,
+                            sessionDescription);
+                }
+            }
+
+            public void onRingingBack(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onRingingBack(SipSession.this);
+                }
+            }
+
+            public void onCallEstablished(ISipSession session,
+                    String sessionDescription) {
+                if (mListener != null) {
+                    mListener.onCallEstablished(SipSession.this,
+                            sessionDescription);
+                }
+            }
+
+            public void onCallEnded(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onCallEnded(SipSession.this);
+                }
+            }
+
+            public void onCallBusy(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onCallBusy(SipSession.this);
+                }
+            }
+
+            public void onCallChangeFailed(ISipSession session, int errorCode,
+                    String message) {
+                if (mListener != null) {
+                    mListener.onCallChangeFailed(SipSession.this, errorCode,
+                            message);
+                }
+            }
+
+            public void onError(ISipSession session, int errorCode, String message) {
+                if (mListener != null) {
+                    mListener.onError(SipSession.this, errorCode, message);
+                }
+            }
+
+            public void onRegistering(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onRegistering(SipSession.this);
+                }
+            }
+
+            public void onRegistrationDone(ISipSession session, int duration) {
+                if (mListener != null) {
+                    mListener.onRegistrationDone(SipSession.this, duration);
+                }
+            }
+
+            public void onRegistrationFailed(ISipSession session, int errorCode,
+                    String message) {
+                if (mListener != null) {
+                    mListener.onRegistrationFailed(SipSession.this, errorCode,
+                            message);
+                }
+            }
+
+            public void onRegistrationTimeout(ISipSession session) {
+                if (mListener != null) {
+                    mListener.onRegistrationTimeout(SipSession.this);
+                }
+            }
+        };
+    }
+}
diff --git a/voip/java/android/net/sip/SipSessionState.java b/voip/java/android/net/sip/SipSessionState.java
deleted file mode 100644
index 31e9d3f..0000000
--- a/voip/java/android/net/sip/SipSessionState.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-/**
- * Defines {@link ISipSession} states.
- * @hide
- */
-public class SipSessionState {
-    /** When session is ready to initiate a call or transaction. */
-    public static final int READY_TO_CALL = 0;
-
-    /** When the registration request is sent out. */
-    public static final int REGISTERING = 1;
-
-    /** When the unregistration request is sent out. */
-    public static final int DEREGISTERING = 2;
-
-    /** When an INVITE request is received. */
-    public static final int INCOMING_CALL = 3;
-
-    /** When an OK response is sent for the INVITE request received. */
-    public static final int INCOMING_CALL_ANSWERING = 4;
-
-    /** When an INVITE request is sent. */
-    public static final int OUTGOING_CALL = 5;
-
-    /** When a RINGING response is received for the INVITE request sent. */
-    public static final int OUTGOING_CALL_RING_BACK = 6;
-
-    /** When a CANCEL request is sent for the INVITE request sent. */
-    public static final int OUTGOING_CALL_CANCELING = 7;
-
-    /** When a call is established. */
-    public static final int IN_CALL = 8;
-
-    /** Some error occurs when making a remote call to {@link ISipSession}. */
-    public static final int REMOTE_ERROR = 9;
-
-    /** When an OPTIONS request is sent. */
-    public static final int PINGING = 10;
-
-    /** Not defined. */
-    public static final int NOT_DEFINED = 101;
-
-    /**
-     * Converts the state to string.
-     */
-    public static String toString(int state) {
-        switch (state) {
-            case READY_TO_CALL:
-                return "READY_TO_CALL";
-            case REGISTERING:
-                return "REGISTERING";
-            case DEREGISTERING:
-                return "DEREGISTERING";
-            case INCOMING_CALL:
-                return "INCOMING_CALL";
-            case INCOMING_CALL_ANSWERING:
-                return "INCOMING_CALL_ANSWERING";
-            case OUTGOING_CALL:
-                return "OUTGOING_CALL";
-            case OUTGOING_CALL_RING_BACK:
-                return "OUTGOING_CALL_RING_BACK";
-            case OUTGOING_CALL_CANCELING:
-                return "OUTGOING_CALL_CANCELING";
-            case IN_CALL:
-                return "IN_CALL";
-            case REMOTE_ERROR:
-                return "REMOTE_ERROR";
-            case PINGING:
-                return "PINGING";
-            default:
-                return "NOT_DEFINED";
-        }
-    }
-
-    private SipSessionState() {
-    }
-}