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);