Merge "MinGW/Cygwin requires open() in O_BINARY mode." into gingerbread
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 5cbfe74..6e221c8 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -367,6 +367,11 @@
         int state = getSinkState(device);
         String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
 
+        switch (state) {
+            case BluetoothA2dp.STATE_DISCONNECTED:
+            case BluetoothA2dp.STATE_DISCONNECTING:
+                return false;
+        }
         // State is CONNECTING or CONNECTED or PLAYING
         handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTING);
         if (!disconnectSinkNative(path)) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6dcf4e6..9080f96 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4447,6 +4447,7 @@
         }
 
         hideControllers();
+        stopTextSelectionMode();
 
         switch (keyCode) {
             case KeyEvent.KEYCODE_DPAD_CENTER:
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5c60fd5..037a362 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -382,7 +382,7 @@
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_storage">Storage</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this.   [CHAR LIMIT=30] -->
-    <string name="permgroupdesc_storage" product="nosdcard">Access the shared storage.</string>
+    <string name="permgroupdesc_storage" product="nosdcard">Access the USB storage.</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_storage" product="default">Access the SD card.</string>
 
@@ -890,29 +890,29 @@
     <string name="permdesc_mount_format_filesystems">Allows the application to format removable storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_asec_access">get information on secure storage</string>
+    <string name="permlab_asec_access">get information on internal storage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_asec_access">Allows the application to get information on secure storage.</string>
+    <string name="permdesc_asec_access">Allows the application to get information on internal storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_asec_create">create secure storage</string>
+    <string name="permlab_asec_create">create internal storage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_asec_create">Allows the application to create secure storage.</string>
+    <string name="permdesc_asec_create">Allows the application to create internal storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_asec_destroy">destroy secure storage</string>
+    <string name="permlab_asec_destroy">destroy internal storage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_asec_destroy">Allows the application to destroy secure storage.</string>
+    <string name="permdesc_asec_destroy">Allows the application to destroy internal storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_asec_mount_unmount">mount / unmount secure storage</string>
+    <string name="permlab_asec_mount_unmount">mount / unmount internal storage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_asec_mount_unmount">Allows the application to mount / unmount secure storage.</string>
+    <string name="permdesc_asec_mount_unmount">Allows the application to mount / unmount internal storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_asec_rename">rename secure storage</string>
+    <string name="permlab_asec_rename">rename internal storage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_asec_rename">Allows the application to rename secure storage.</string>
+    <string name="permdesc_asec_rename">Allows the application to rename internal storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_vibrate">control vibrator</string>
@@ -1227,11 +1227,11 @@
       user dictionary.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permlab_sdcardWrite" product="nosdcard">modify/delete shared storage contents</string>
+    <string name="permlab_sdcardWrite" product="nosdcard">modify/delete USB storage contents</string>
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_sdcardWrite" product="default">modify/delete SD card contents</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permdesc_sdcardWrite" product="nosdcard">Allows an application to write to the shared storage.</string>
+    <string name="permdesc_sdcardWrite" product="nosdcard">Allows an application to write to the USB storage.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_sdcardWrite" product="default">Allows an application to write to the SD card.</string>
 
@@ -2091,15 +2091,15 @@
     <!-- See USB_STORAGE.  USB_STORAGE_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to mount.  This is the title. -->
     <string name="usb_storage_title">USB connected</string>
     <!-- See USB_STORAGE.    This is the message. [CHAR LIMIT=NONE] -->
-    <string name="usb_storage_message" product="nosdcard">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s shared storage.</string>
+    <string name="usb_storage_message" product="nosdcard">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s USB storage.</string>
     <!-- See USB_STORAGE.    This is the message. -->
     <string name="usb_storage_message" product="default">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s SD card.</string>
     <!-- See USB_STORAGE.    This is the button text to mount the phone on the computer. -->
     <string name="usb_storage_button_mount">Turn on USB storage</string>
     <!-- See USB_STORAGE_DIALOG.  If there was an error mounting, this is the text. [CHAR LIMIT=NONE] -->
-    <string name="usb_storage_error_message" product="nosdcard">There is a problem using your shared storage for USB storage.</string>
+    <string name="usb_storage_error_message" product="nosdcard">There is a problem using your USB storage for USB mass storage.</string>
     <!-- See USB_STORAGE_DIALOG.  If there was an error mounting, this is the text. -->
-    <string name="usb_storage_error_message" product="default">There is a problem using your SD card for USB storage.</string>
+    <string name="usb_storage_error_message" product="default">There is a problem using your SD card for USB mass storage.</string>
     <!-- USB_STORAGE: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across.  This is the title -->
     <string name="usb_storage_notification_title">USB connected</string>
     <!-- See USB_STORAGE. This is the message. -->
@@ -2115,7 +2115,7 @@
     <!-- See USB_STORAGE_STOP.  USB_STORAGE_STOP_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to stop usb storage.  This is the title. -->
     <string name="usb_storage_stop_title">USB storage in use</string>
     <!-- See USB_STORAGE_STOP.    This is the message. [CHAR LIMIT=NONE] -->
-    <string name="usb_storage_stop_message" product="nosdcard">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s shared storage from your computer.</string>
+    <string name="usb_storage_stop_message" product="nosdcard">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s USB storage from your computer.</string>
     <!-- See USB_STORAGE_STOP.    This is the message. -->
     <string name="usb_storage_stop_message" product="default">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s SD card from your computer.</string>
     <!-- See USB_STORAGE_STOP.    This is the button text to stop usb storage. -->
@@ -2135,11 +2135,11 @@
     <!-- External media format dialog strings -->
     <!-- This is the label for the activity, and should never be visible to the user. -->
     <!-- See EXTMEDIA_FORMAT.  EXTMEDIA_FORMAT_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to format the SD card.  This is the title. [CHAR LIMIT=20] -->
-    <string name="extmedia_format_title" product="nosdcard">Format shared storage</string>
+    <string name="extmedia_format_title" product="nosdcard">Format USB storage</string>
     <!-- See EXTMEDIA_FORMAT.  EXTMEDIA_FORMAT_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to format the SD card.  This is the title. -->
     <string name="extmedia_format_title" product="default">Format SD card</string>
     <!-- See EXTMEDIA_FORMAT.   This is the message. [CHAR LIMIT=NONE] -->
-    <string name="extmedia_format_message" product="nosdcard">Format shared storage, erasing all files stored there?  Action cannot be reversed!</string>
+    <string name="extmedia_format_message" product="nosdcard">Format USB storage, erasing all files stored there?  Action cannot be reversed!</string>
     <!-- See EXTMEDIA_FORMAT.   This is the message. -->
     <string name="extmedia_format_message" product="default">Are you sure you want to format the SD card? All data on your card will be lost.</string>
     <!-- See EXTMEDIA_FORMAT.    This is the button text to format the sd card. -->
@@ -2167,49 +2167,49 @@
 
     <!-- External media notification strings -->
     <!-- Shown when external media is being checked [CHAR LIMIT=30] -->
-    <string name="ext_media_checking_notification_title" product="nosdcard">Preparing shared storage</string>
+    <string name="ext_media_checking_notification_title" product="nosdcard">Preparing USB storage</string>
     <!-- Shown when external media is being checked -->
     <string name="ext_media_checking_notification_title" product="default">Preparing SD card</string>
     <string name="ext_media_checking_notification_message">Checking for errors.</string>
 
     <!-- Shown when external media is blank (or unsupported filesystem) [CHAR LIMIT=30] -->
-    <string name="ext_media_nofs_notification_title" product="nosdcard">Blank shared storage</string>
+    <string name="ext_media_nofs_notification_title" product="nosdcard">Blank USB storage</string>
     <!-- Shown when external media is blank (or unsupported filesystem) -->
     <string name="ext_media_nofs_notification_title" product="default">Blank SD card</string>
-    <!-- Shown when shared storage cannot be read.  [CHAR LIMIT=NONE] -->
-    <string name="ext_media_nofs_notification_message" product="nosdcard">Shared storage blank or has unsupported filesystem.</string>
+    <!-- Shown when USB storage cannot be read.  [CHAR LIMIT=NONE] -->
+    <string name="ext_media_nofs_notification_message" product="nosdcard">USB storage blank or has unsupported filesystem.</string>
     <string name="ext_media_nofs_notification_message" product="default">SD card blank or has unsupported filesystem.</string>
 
     <!-- Shown when external media is unmountable (corrupt)) [CHAR LIMIT=30] -->
-    <string name="ext_media_unmountable_notification_title" product="nosdcard">Damaged shared storage</string>
+    <string name="ext_media_unmountable_notification_title" product="nosdcard">Damaged USB storage</string>
     <!-- Shown when external media is unmountable (corrupt)) -->
     <string name="ext_media_unmountable_notification_title" product="default">Damaged SD card</string>
-    <!-- Shown when shared storage cannot be read.  [CHAR LIMIT=NONE] -->
-    <string name="ext_media_unmountable_notification_message" product="nosdcard">Shared storage damaged. You may have to reformat it.</string>
+    <!-- Shown when USB storage cannot be read.  [CHAR LIMIT=NONE] -->
+    <string name="ext_media_unmountable_notification_message" product="nosdcard">USB storage damaged. You may have to reformat it.</string>
     <string name="ext_media_unmountable_notification_message" product="default">SD card damaged. You may have to reformat it.</string>
 
     <!-- Shown when external media is unsafely removed [CHAR LIMIT=30] -->
-    <string name="ext_media_badremoval_notification_title" product="nosdcard">Shared storage unexpectedly removed</string>
+    <string name="ext_media_badremoval_notification_title" product="nosdcard">USB storage unexpectedly removed</string>
     <!-- Shown when external media is unsafely removed -->
     <string name="ext_media_badremoval_notification_title" product="default">SD card unexpectedly removed</string>
     <!-- Shown when external media is unsafely removed.  [CHAR LIMIT=NONE] -->
-    <string name="ext_media_badremoval_notification_message" product="nosdcard">Unmount shared storage before removing to avoid data loss.</string>
+    <string name="ext_media_badremoval_notification_message" product="nosdcard">Unmount USB storage before removing to avoid data loss.</string>
     <string name="ext_media_badremoval_notification_message" product="default">Unmount SD card before removing to avoid data loss.</string>
 
     <!-- Shown when external media has been safely removed [CHAR LIMIT=30] -->
-    <string name="ext_media_safe_unmount_notification_title" product="nosdcard">Shared storage safe to remove</string>
+    <string name="ext_media_safe_unmount_notification_title" product="nosdcard">USB storage safe to remove</string>
     <!-- Shown when external media has been safely removed -->
     <string name="ext_media_safe_unmount_notification_title" product="default">SD card safe to remove</string>
     <!-- Shown when external media has been safely removed.  [CHAR LIMIT=NONE] -->
-    <string name="ext_media_safe_unmount_notification_message" product="nosdcard">You can safely remove shared storage.</string>
+    <string name="ext_media_safe_unmount_notification_message" product="nosdcard">You can safely remove USB storage.</string>
     <string name="ext_media_safe_unmount_notification_message" product="default">You can safely remove SD card.</string>
 
     <!-- Shown when external media is missing [CHAR LIMIT=30] -->
-    <string name="ext_media_nomedia_notification_title" product="nosdcard">Removed shared storage</string>
+    <string name="ext_media_nomedia_notification_title" product="nosdcard">Removed USB storage</string>
     <!-- Shown when external media is missing -->
     <string name="ext_media_nomedia_notification_title" product="default">Removed SD card</string>
     <!-- Shown when external media is missing.  [CHAR LIMIT=NONE] -->
-    <string name="ext_media_nomedia_notification_message" product="nosdcard">Shared storage removed. Insert new media.</string>
+    <string name="ext_media_nomedia_notification_message" product="nosdcard">USB storage removed. Insert new media.</string>
     <string name="ext_media_nomedia_notification_message" product="default">SD card removed. Insert a new one.</string>
 
     <!-- Shown in LauncherActivity when the requested target Intent didn't return any matching Activities, leaving the list empty. -->
diff --git a/docs/html/guide/topics/testing/index.jd b/docs/html/guide/topics/testing/index.jd
index 92ed5a7..42a9db5 100644
--- a/docs/html/guide/topics/testing/index.jd
+++ b/docs/html/guide/topics/testing/index.jd
@@ -13,14 +13,6 @@
 <h4>Concepts</h4>
 <ul>
     <li>
-        Testing Tools describes the Eclipse with ADT and command-line tools you use to test
-        Android applications.
-    </li>
-    <li>
-        What to Test is an overview of the types of testing you should do. It focuses on testing
-        system-wide aspects of Android that can affect every component in your application.
-    </li>
-    <li>
         <a href="{@docRoot}guide/topics/testing/activity_testing.html">
         Activity Testing</a> focuses on testing activities. It describes how instrumentation allows
         you to control activities outside the normal application lifecycle. It also lists
@@ -38,6 +30,11 @@
         Service Testing</a> focuses on testing services. It also lists service-specific features
         you should test.
     </li>
+    <li>
+        <a href="{@docRoot}guide/topics/testing/what_to_test.html">What to Test</a>
+        is an overview of the types of testing you should do. It focuses on testing
+        system-wide aspects of Android that can affect every component in your application.
+    </li>
 </ul>
 <h4>Procedures</h4>
 <ul>
@@ -65,9 +62,8 @@
 <h4>Samples</h4>
 <ul>
     <li>
-        <a href="{@docRoot}resources/samples/NotePadTest.html">Note Pad Provider
-        Test</a> is a test package for the
-        <a href="{@docRoot}resources/samples/NotePad.html">Note Pad</a> sample
+        <a href="{@docRoot}resources/samples/NotePadTest.html">Note Pad Test</a> is a test
+        package for the <a href="{@docRoot}resources/samples/NotePad.html">Note Pad</a> sample
         application. It provides a simple example of unit testing
         a {@link android.content.ContentProvider}.
     </li>
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index c0be3a0..944731d 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -439,11 +439,10 @@
             // Since mFDs[0] is used for inotify, we process regular events starting at index 1.
             mInputDeviceIndex += 1;
             if (mInputDeviceIndex >= mFDCount) {
-                mInputDeviceIndex = 0;
                 break;
             }
 
-            const struct pollfd &pfd = mFDs[mInputDeviceIndex];
+            const struct pollfd& pfd = mFDs[mInputDeviceIndex];
             if (pfd.revents & POLLIN) {
                 int32_t readSize = read(pfd.fd, mInputBufferData,
                         sizeof(struct input_event) * INPUT_BUFFER_SIZE);
@@ -460,11 +459,17 @@
             }
         }
 
+#if HAVE_INOTIFY
         // readNotify() will modify mFDs and mFDCount, so this must be done after
         // processing all other events.
         if(mFDs[0].revents & POLLIN) {
             readNotify(mFDs[0].fd);
+            mFDs[0].revents = 0;
+            continue; // report added or removed devices immediately
         }
+#endif
+
+        mInputDeviceIndex = 0;
 
         // Poll for events.  Mind the wake lock dance!
         // We hold a wake lock at all times except during poll().  This works due to some
@@ -482,7 +487,7 @@
 
         if (pollResult <= 0) {
             if (errno != EINTR) {
-                LOGW("select failed (errno=%d)\n", errno);
+                LOGW("poll failed (errno=%d)\n", errno);
                 usleep(100000);
             }
         }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3734969..073ce01 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1576,6 +1576,7 @@
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
             hw_w, hw_h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
     if (glGetError() != GL_NO_ERROR) {
+        while ( glGetError() != GL_NO_ERROR ) ;
         GLint tw = (2 << (31 - clz(hw_w)));
         GLint th = (2 << (31 - clz(hw_h)));
         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
@@ -1907,11 +1908,9 @@
         // we're already off
         return NO_ERROR;
     }
-    status_t result = electronBeamOffAnimationImplLocked();
-    if (result == NO_ERROR) {
-        hw.setCanDraw(false);
-    }
-    return result;
+    electronBeamOffAnimationImplLocked();
+    hw.setCanDraw(false);
+    return NO_ERROR;
 }
 
 status_t SurfaceFlinger::turnElectronBeamOff(int32_t mode)
@@ -1958,11 +1957,9 @@
         // we're already on
         return NO_ERROR;
     }
-    status_t result = electronBeamOnAnimationImplLocked();
-    if (result == NO_ERROR) {
-        hw.setCanDraw(true);
-    }
-    return result;
+    electronBeamOnAnimationImplLocked();
+    hw.setCanDraw(true);
+    return NO_ERROR;
 }
 
 status_t SurfaceFlinger::turnElectronBeamOn(int32_t mode)
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
index 9300bb7..9866876 100644
--- a/tools/obbtool/Android.mk
+++ b/tools/obbtool/Android.mk
@@ -29,18 +29,21 @@
 
 include $(BUILD_HOST_EXECUTABLE)
 
-include $(CLEAR_VARS)
+# Non-Linux hosts might not have OpenSSL libcrypto
+ifeq ($(HOST_OS),linux)
+    include $(CLEAR_VARS)
 
-LOCAL_MODULE := pbkdf2gen
+    LOCAL_MODULE := pbkdf2gen
 
-LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE_TAGS := optional
 
-LOCAL_CFLAGS := -Wall -Werror
+    LOCAL_CFLAGS := -Wall -Werror
 
-LOCAL_SRC_FILES := pbkdf2gen.cpp
+    LOCAL_SRC_FILES := pbkdf2gen.cpp
 
-LOCAL_SHARED_LIBRARIES := libcrypto
+    LOCAL_SHARED_LIBRARIES := libcrypto
 
-include $(BUILD_HOST_EXECUTABLE)
+    include $(BUILD_HOST_EXECUTABLE)
+endif # HOST_OS == linux
 
 endif # TARGET_BUILD_APPS
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 1a17d38..f41f156c 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -39,6 +39,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -54,6 +55,7 @@
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Timer;
@@ -93,6 +95,7 @@
 
     private ConnectivityReceiver mConnectivityReceiver;
     private boolean mWifiEnabled;
+    private MyWakeLock mMyWakeLock;
 
     /**
      * Starts the SIP service. Do nothing if the SIP API is not supported on the
@@ -114,6 +117,8 @@
                 new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
         context.registerReceiver(mWifiStateReceiver,
                 new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
+        mMyWakeLock = new MyWakeLock((PowerManager)
+                context.getSystemService(Context.POWER_SERVICE));
 
         mTimer = new WakeupTimer(context);
         mWifiOnly = SipManager.isSipWifiOnly(context);
@@ -225,7 +230,11 @@
         group = mSipGroups.remove(localProfileUri);
         notifyProfileRemoved(group.getLocalProfile());
         group.close();
-        if (!anyOpened()) releaseWifiLock();
+
+        if (!anyOpened()) {
+            releaseWifiLock();
+            mMyWakeLock.reset(); // in case there's leak
+        }
     }
 
     public synchronized boolean isOpened(String localProfileUri) {
@@ -405,6 +414,8 @@
                 for (SipSessionGroupExt group : mSipGroups.values()) {
                     group.onConnectivityChanged(true);
                 }
+            } else {
+                mMyWakeLock.reset(); // in case there's a leak
             }
         } catch (SipException e) {
             Log.e(TAG, "onConnectivityChanged()", e);
@@ -581,7 +592,7 @@
     }
 
     // KeepAliveProcess is controlled by AutoRegistrationProcess.
-    // All methods will be invoked in sync with SipService.this except realRun()
+    // All methods will be invoked in sync with SipService.this.
     private class KeepAliveProcess implements Runnable {
         private static final String TAG = "\\KEEPALIVE/";
         private static final int INTERVAL = 10;
@@ -600,43 +611,33 @@
 
         // timeout handler
         public void run() {
-            if (!mRunning) return;
-            final SipSessionGroup.SipSessionImpl session = mSession;
-
-            // delegate to mExecutor
-            getExecutor().addTask(new Runnable() {
-                public void run() {
-                    realRun(session);
-                }
-            });
-        }
-
-        // real timeout handler
-        private void realRun(SipSessionGroup.SipSessionImpl session) {
             synchronized (SipService.this) {
-                if (notCurrentSession(session)) return;
+                if (!mRunning) return;
 
-                session = session.duplicate();
-                if (DEBUGV) Log.v(TAG, "~~~ keepalive");
-                mTimer.cancel(this);
-                session.sendKeepAlive();
-                if (session.isReRegisterRequired()) {
-                    mSession.register(EXPIRY_TIME);
-                } else {
-                    mTimer.set(INTERVAL * 1000, this);
+                if (DEBUGV) Log.v(TAG, "~~~ keepalive: "
+                        + mSession.getLocalProfile().getUriString());
+                SipSessionGroup.SipSessionImpl session = mSession.duplicate();
+                try {
+                    session.sendKeepAlive();
+                    if (session.isReRegisterRequired()) {
+                        // Acquire wake lock for the registration process. The
+                        // lock will be released when registration is complete.
+                        mMyWakeLock.acquire(mSession);
+                        mSession.register(EXPIRY_TIME);
+                    }
+                } catch (Throwable t) {
+                    Log.w(TAG, "keepalive error: " + t);
                 }
             }
         }
 
         public void stop() {
+            if (DEBUGV && (mSession != null)) Log.v(TAG, "stop keepalive:"
+                    + mSession.getLocalProfile().getUriString());
             mRunning = false;
             mSession = null;
             mTimer.cancel(this);
         }
-
-        private boolean notCurrentSession(ISipSession session) {
-            return (session != mSession) || !mRunning;
-        }
     }
 
     private class AutoRegistrationProcess extends SipSessionAdapter
@@ -667,6 +668,7 @@
                 // start unregistration to clear up old registration at server
                 // TODO: when rfc5626 is deployed, use reg-id and sip.instance
                 // in registration to avoid adding duplicate entries to server
+                mMyWakeLock.acquire(mSession);
                 mSession.unregister();
                 if (DEBUG) Log.d(TAG, "start AutoRegistrationProcess for "
                         + mSession.getLocalProfile().getUriString());
@@ -676,8 +678,11 @@
         public void stop() {
             if (!mRunning) return;
             mRunning = false;
-            mSession.setListener(null);
-            if (mConnected && mRegistered) mSession.unregister();
+            mMyWakeLock.release(mSession);
+            if (mSession != null) {
+                mSession.setListener(null);
+                if (mConnected && mRegistered) mSession.unregister();
+            }
 
             mTimer.cancel(this);
             if (mKeepAliveProcess != null) {
@@ -734,29 +739,18 @@
             return mRegistered;
         }
 
-        // timeout handler
+        // timeout handler: re-register
         public void run() {
             synchronized (SipService.this) {
                 if (!mRunning) return;
-                final SipSessionGroup.SipSessionImpl session = mSession;
 
-                // delegate to mExecutor
-                getExecutor().addTask(new Runnable() {
-                    public void run() {
-                        realRun(session);
-                    }
-                });
-            }
-        }
-
-        // real timeout handler
-        private void realRun(SipSessionGroup.SipSessionImpl session) {
-            synchronized (SipService.this) {
-                if (notCurrentSession(session)) return;
                 mErrorCode = SipErrorCode.NO_ERROR;
                 mErrorMessage = null;
                 if (DEBUG) Log.d(TAG, "~~~ registering");
-                if (mConnected) session.register(EXPIRY_TIME);
+                if (mConnected) {
+                    mMyWakeLock.acquire(mSession);
+                    mSession.register(EXPIRY_TIME);
+                }
             }
         }
 
@@ -806,6 +800,7 @@
         private boolean notCurrentSession(ISipSession session) {
             if (session != mSession) {
                 ((SipSessionGroup.SipSessionImpl) session).setListener(null);
+                mMyWakeLock.release(session);
                 return true;
             }
             return !mRunning;
@@ -842,6 +837,7 @@
                             mKeepAliveProcess.start();
                         }
                     }
+                    mMyWakeLock.release(session);
                 } else {
                     mRegistered = false;
                     mExpiryTime = -1L;
@@ -872,6 +868,7 @@
                 mErrorCode = errorCode;
                 mErrorMessage = message;
                 mProxy.onRegistrationFailed(session, errorCode, message);
+                mMyWakeLock.release(session);
             }
         }
 
@@ -884,6 +881,7 @@
                 mErrorCode = SipErrorCode.TIME_OUT;
                 mProxy.onRegistrationTimeout(session);
                 restartLater();
+                mMyWakeLock.release(session);
             }
         }
 
@@ -902,7 +900,16 @@
         private MyTimerTask mTask;
 
         @Override
-        public void onReceive(Context context, Intent intent) {
+        public void onReceive(final Context context, final Intent intent) {
+            // Run the handler in MyExecutor to be protected by wake lock
+            getExecutor().execute(new Runnable() {
+                public void run() {
+                    onReceiveInternal(context, intent);
+                }
+            });
+        }
+
+        private void onReceiveInternal(Context context, Intent intent) {
             String action = intent.getAction();
             if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                 Bundle b = intent.getExtras();
@@ -970,11 +977,13 @@
                     if (mTask != null) mTask.cancel();
                     mTask = new MyTimerTask(type, connected);
                     mTimer.schedule(mTask, 2 * 1000L);
-                    // TODO: hold wakup lock so that we can finish change before
-                    // the device goes to sleep
+                    // hold wakup lock so that we can finish changes before the
+                    // device goes to sleep
+                    mMyWakeLock.acquire(mTask);
                 } else {
                     if ((mTask != null) && mTask.mNetworkType.equals(type)) {
                         mTask.cancel();
+                        mMyWakeLock.release(mTask);
                     }
                     onConnectivityChanged(type, false);
                 }
@@ -994,7 +1003,7 @@
             @Override
             public void run() {
                 // delegate to mExecutor
-                getExecutor().addTask(new Runnable() {
+                getExecutor().execute(new Runnable() {
                     public void run() {
                         realRun();
                     }
@@ -1012,6 +1021,7 @@
                     if (DEBUG) Log.d(TAG, " deliver change for " + mNetworkType
                             + (mConnected ? " CONNECTED" : "DISCONNECTED"));
                     onConnectivityChanged(mNetworkType, mConnected);
+                    mMyWakeLock.release(this);
                 }
             }
         }
@@ -1019,7 +1029,6 @@
 
     // TODO: clean up pending SipSession(s) periodically
 
-
     /**
      * Timer that can schedule events to occur even when the device is in sleep.
      * Only used internally in this package.
@@ -1209,7 +1218,8 @@
         }
 
         @Override
-        public synchronized void onReceive(Context context, Intent intent) {
+        public void onReceive(Context context, Intent intent) {
+            // This callback is already protected by AlarmManager's wake lock.
             String action = intent.getAction();
             if (getAction().equals(action)
                     && intent.getExtras().containsKey(TRIGGER_TIME)) {
@@ -1236,7 +1246,7 @@
             }
         }
 
-        private void execute(long triggerTime) {
+        private synchronized void execute(long triggerTime) {
             if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = "
                     + showTime(triggerTime) + ": " + mEventQueue.size());
             if (stopped() || mEventQueue.isEmpty()) return;
@@ -1248,9 +1258,8 @@
                 event.mLastTriggerTime = event.mTriggerTime;
                 event.mTriggerTime += event.mPeriod;
 
-                // run the callback in a new thread to prevent deadlock
-                new Thread(event.mCallback, "SipServiceTimerCallbackThread")
-                        .start();
+                // run the callback in the handler thread to prevent deadlock
+                getExecutor().execute(event.mCallback);
             }
             if (DEBUG_TIMER) {
                 Log.d(TAG, "after timeout execution");
@@ -1314,29 +1323,78 @@
         }
     }
 
-    // Single-threaded executor
-    private static class MyExecutor extends Handler {
+    private static Looper createLooper() {
+        HandlerThread thread = new HandlerThread("SipService.Executor");
+        thread.start();
+        return thread.getLooper();
+    }
+
+    // Executes immediate tasks in a single thread.
+    // Hold/release wake lock for running tasks
+    private class MyExecutor extends Handler {
         MyExecutor() {
             super(createLooper());
         }
 
-        private static Looper createLooper() {
-            HandlerThread thread = new HandlerThread("SipService");
-            thread.start();
-            return thread.getLooper();
-        }
-
-        void addTask(Runnable task) {
+        void execute(Runnable task) {
+            mMyWakeLock.acquire(task);
             Message.obtain(this, 0/* don't care */, task).sendToTarget();
         }
 
         @Override
         public void handleMessage(Message msg) {
             if (msg.obj instanceof Runnable) {
-                ((Runnable) msg.obj).run();
+                executeInternal((Runnable) msg.obj);
             } else {
                 Log.w(TAG, "can't handle msg: " + msg);
             }
         }
+
+        private void executeInternal(Runnable task) {
+            try {
+                task.run();
+            } catch (Throwable t) {
+                Log.e(TAG, "run task: " + task, t);
+            } finally {
+                mMyWakeLock.release(task);
+            }
+        }
+    }
+
+    private static class MyWakeLock {
+        private PowerManager mPowerManager;
+        private PowerManager.WakeLock mWakeLock;
+        private HashSet<Object> mHolders = new HashSet<Object>();
+
+        MyWakeLock(PowerManager powerManager) {
+            mPowerManager = powerManager;
+        }
+
+        synchronized void reset() {
+            mHolders.clear();
+            release(null);
+            if (DEBUGV) Log.v(TAG, "~~~ hard reset wakelock");
+        }
+
+        synchronized void acquire(Object holder) {
+            mHolders.add(holder);
+            if (mWakeLock == null) {
+                mWakeLock = mPowerManager.newWakeLock(
+                        PowerManager.PARTIAL_WAKE_LOCK, "SipWakeLock");
+            }
+            if (!mWakeLock.isHeld()) mWakeLock.acquire();
+            if (DEBUGV) Log.v(TAG, "acquire wakelock: holder count="
+                    + mHolders.size());
+        }
+
+        synchronized void release(Object holder) {
+            mHolders.remove(holder);
+            if ((mWakeLock != null) && mHolders.isEmpty()
+                    && mWakeLock.isHeld()) {
+                mWakeLock.release();
+            }
+            if (DEBUGV) Log.v(TAG, "release wakelock: holder count="
+                    + mHolders.size());
+        }
     }
 }
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index b5f8d39..2b8058f 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -547,8 +547,14 @@
             mState = SipSession.State.PINGING;
             try {
                 processCommand(new OptionsCommand());
-                while (SipSession.State.PINGING == mState) {
-                    Thread.sleep(1000);
+                for (int i = 0; i < 15; i++) {
+                    if (SipSession.State.PINGING != mState) break;
+                    Thread.sleep(200);
+                }
+                if (SipSession.State.PINGING == mState) {
+                    // FIXME: what to do if server doesn't respond
+                    reset();
+                    if (DEBUG) Log.w(TAG, "no response from ping");
                 }
             } catch (SipException e) {
                 Log.e(TAG, "sendKeepAlive failed", e);