diff options
| -rw-r--r-- | core/java/android/server/BluetoothA2dpService.java | 5 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 1 | ||||
| -rwxr-xr-x | core/res/res/values/strings.xml | 64 | ||||
| -rw-r--r-- | docs/html/guide/topics/testing/index.jd | 18 | ||||
| -rw-r--r-- | libs/ui/EventHub.cpp | 11 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 17 | ||||
| -rw-r--r-- | tools/obbtool/Android.mk | 17 | ||||
| -rw-r--r-- | voip/java/com/android/server/sip/SipService.java | 190 | ||||
| -rw-r--r-- | voip/java/com/android/server/sip/SipSessionGroup.java | 10 |
9 files changed, 202 insertions, 131 deletions
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index 5cbfe744b03d..6e221c884989 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -367,6 +367,11 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { 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 6dcf4e63ed91..9080f96b7dc4 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4447,6 +4447,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } 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 5c60fd5293b5..037a3627eefc 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 92ed5a703b9a..42a9db59f1f7 100644 --- a/docs/html/guide/topics/testing/index.jd +++ b/docs/html/guide/topics/testing/index.jd @@ -13,14 +13,6 @@ page.title=Testing <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 @@ page.title=Testing 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 @@ page.title=Testing <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 c0be3a022c94..944731dba672 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -439,11 +439,10 @@ bool EventHub::getEvent(RawEvent* outEvent) // 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 @@ bool EventHub::getEvent(RawEvent* outEvent) } } +#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 @@ bool EventHub::getEvent(RawEvent* outEvent) 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 373496971d14..073ce01ac094 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1576,6 +1576,7 @@ status_t SurfaceFlinger::renderScreenToTextureLocked(DisplayID dpy, 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 @@ status_t SurfaceFlinger::turnElectronBeamOffImplLocked() // 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 @@ status_t SurfaceFlinger::turnElectronBeamOnImplLocked() // 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 9300bb71b810..98668764a36b 100644 --- a/tools/obbtool/Android.mk +++ b/tools/obbtool/Android.mk @@ -29,18 +29,21 @@ LOCAL_MODULE := obbtool 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 1a17d3823668..f41f156c1a83 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.Handler; 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.ArrayList; 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 @@ public final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { } // 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 @@ public final class SipService extends ISipService.Stub { // 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 @@ public final class SipService extends ISipService.Stub { // 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 final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { private boolean notCurrentSession(ISipSession session) { if (session != mSession) { ((SipSessionGroup.SipSessionImpl) session).setListener(null); + mMyWakeLock.release(session); return true; } return !mRunning; @@ -842,6 +837,7 @@ public final class SipService extends ISipService.Stub { mKeepAliveProcess.start(); } } + mMyWakeLock.release(session); } else { mRegistered = false; mExpiryTime = -1L; @@ -872,6 +868,7 @@ public final class SipService extends ISipService.Stub { mErrorCode = errorCode; mErrorMessage = message; mProxy.onRegistrationFailed(session, errorCode, message); + mMyWakeLock.release(session); } } @@ -884,6 +881,7 @@ public final class SipService extends ISipService.Stub { mErrorCode = SipErrorCode.TIME_OUT; mProxy.onRegistrationTimeout(session); restartLater(); + mMyWakeLock.release(session); } } @@ -902,7 +900,16 @@ public final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { @Override public void run() { // delegate to mExecutor - getExecutor().addTask(new Runnable() { + getExecutor().execute(new Runnable() { public void run() { realRun(); } @@ -1012,6 +1021,7 @@ public final class SipService extends ISipService.Stub { if (DEBUG) Log.d(TAG, " deliver change for " + mNetworkType + (mConnected ? " CONNECTED" : "DISCONNECTED")); onConnectivityChanged(mNetworkType, mConnected); + mMyWakeLock.release(this); } } } @@ -1019,7 +1029,6 @@ public final class SipService extends ISipService.Stub { // 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 @@ public final class SipService extends ISipService.Stub { } @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 @@ public final class SipService extends ISipService.Stub { } } - 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 @@ public final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { } } - // 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 b5f8d39c6edb..2b8058feda43 100644 --- a/voip/java/com/android/server/sip/SipSessionGroup.java +++ b/voip/java/com/android/server/sip/SipSessionGroup.java @@ -547,8 +547,14 @@ class SipSessionGroup implements SipListener { 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); |