summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt7
-rw-r--r--core/java/android/hardware/Camera.java209
-rw-r--r--core/java/android/os/RemoteException.java4
-rw-r--r--core/java/android/os/TransactionTooLargeException.java59
-rw-r--r--core/java/android/provider/Settings.java10
-rwxr-xr-xcore/java/android/provider/Telephony.java10
-rw-r--r--core/java/android/view/View.java16
-rw-r--r--core/java/android/view/textservice/SpellCheckerSession.java28
-rw-r--r--core/java/android/webkit/BrowserFrame.java13
-rw-r--r--core/java/android/webkit/FindActionModeCallback.java15
-rw-r--r--core/java/android/webkit/WebView.java86
-rw-r--r--core/java/android/webkit/WebViewCore.java7
-rw-r--r--core/java/android/widget/ImageView.java24
-rw-r--r--core/java/android/widget/SpellChecker.java7
-rw-r--r--core/java/android/widget/TextView.java63
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java17
-rw-r--r--core/jni/android_util_Binder.cpp34
-rw-r--r--core/res/AndroidManifest.xml7
-rwxr-xr-x[-rw-r--r--]core/res/res/values/config.xml5
-rwxr-xr-x[-rw-r--r--]core/res/res/values/strings.xml6
-rw-r--r--core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java81
-rw-r--r--core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java51
-rwxr-xr-xdata/sounds/AudioPackage5.mk1
-rwxr-xr-xdata/sounds/AudioPackage6.mk1
-rwxr-xr-xdata/sounds/AudioPackage7.mk1
-rw-r--r--data/sounds/effects/ogg/camera_focus.oggbin0 -> 9376 bytes
-rw-r--r--docs/html/resources/resources-data.js2
-rw-r--r--media/java/android/media/AudioService.java25
-rw-r--r--media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp7
-rw-r--r--media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp17
-rw-r--r--media/libstagefright/codecs/on2/h264dec/SoftAVC.h2
-rw-r--r--media/libstagefright/rtsp/ARTPConnection.cpp62
-rw-r--r--media/libstagefright/rtsp/MyHandler.h94
-rw-r--r--opengl/libs/EGL/egl_cache.cpp27
-rw-r--r--opengl/libs/EGL/egl_cache.h7
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml4
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java4
-rw-r--r--packages/SystemUI/res/drawable-hdpi/scrubber_control_disabled_holo.pngbin2203 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/scrubber_control_holo.pngbin1640 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/scrubber_track_holo_dark.9.pngbin250 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.pngbin1311 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.pngbin1403 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/scrubber_track_holo_dark.9.pngbin157 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_normal.9.pngbin3682 -> 3693 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_pressed.9.pngbin3695 -> 3699 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_normal.9.pngbin2183 -> 2193 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_pressed.9.pngbin2196 -> 2199 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_normal.9.pngbin5941 -> 5948 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_pressed.9.pngbin5951 -> 5956 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/scrubber_control_disabled_holo.pngbin3735 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/scrubber_control_holo.pngbin6800 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/scrubber_track_holo_dark.9.pngbin571 -> 0 bytes
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java9
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java42
-rw-r--r--policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java2
-rw-r--r--policy/src/com/android/internal/policy/impl/SimUnlockScreen.java2
-rw-r--r--services/audioflinger/AudioFlinger.cpp9
-rw-r--r--services/java/com/android/server/TextServicesManagerService.java36
-rw-r--r--services/java/com/android/server/pm/Settings.java6
-rw-r--r--services/java/com/android/server/wm/InputManager.java8
-rw-r--r--services/sensorservice/SensorDevice.cpp6
-rw-r--r--services/sensorservice/SensorService.cpp3
-rw-r--r--services/surfaceflinger/Android.mk2
-rw-r--r--telephony/java/android/telephony/PhoneNumberUtils.java231
-rw-r--r--telephony/java/com/android/internal/telephony/DataConnectionTracker.java2
-rw-r--r--telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java12
-rw-r--r--telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java62
-rw-r--r--tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayCoercionTest.java625
-rw-r--r--tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayTest.java177
-rw-r--r--tests/WebViewTests/src/com/android/webviewtests/JavaBridgeCoercionTest.java23
-rw-r--r--tests/WebViewTests/src/com/android/webviewtests/JavaBridgeTestBase.java2
71 files changed, 2016 insertions, 256 deletions
diff --git a/api/current.txt b/api/current.txt
index b72dc9c..37b888e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15133,6 +15133,7 @@ package android.os {
public class RemoteException extends android.util.AndroidException {
ctor public RemoteException();
+ ctor public RemoteException(java.lang.String);
}
public class ResultReceiver implements android.os.Parcelable {
@@ -15227,6 +15228,10 @@ package android.os {
method public abstract void released();
}
+ public class TransactionTooLargeException extends android.os.RemoteException {
+ ctor public TransactionTooLargeException();
+ }
+
public class Vibrator {
method public void cancel();
method public boolean hasVibrator();
@@ -17429,7 +17434,6 @@ package android.provider {
field public static final java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
field public static final java.lang.String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
field public static final deprecated java.lang.String LOGGING_ID = "logging_id";
- field public static final java.lang.String MESSAGING_APP_NOTIFICATIONS = "messaging_app_notifications";
field public static final java.lang.String NETWORK_PREFERENCE = "network_preference";
field public static final java.lang.String PARENTAL_CONTROL_ENABLED = "parental_control_enabled";
field public static final java.lang.String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update";
@@ -24841,6 +24845,7 @@ package android.view.textservice {
}
public class SpellCheckerSession {
+ method public void cancel();
method public void close();
method public android.view.textservice.SpellCheckerInfo getSpellChecker();
method public void getSuggestions(android.view.textservice.TextInfo, int);
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 48adfad..3becec0 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -22,9 +22,12 @@ import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemProperties;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
@@ -154,6 +157,7 @@ public class Camera {
private boolean mOneShot;
private boolean mWithBuffer;
private boolean mFaceDetectionRunning = false;
+ private boolean mReleased = false;
/**
* Broadcast Action: A new picture is taken by the camera, and the entry of
@@ -303,7 +307,7 @@ public class Camera {
}
protected void finalize() {
- native_release();
+ release();
}
private native final void native_setup(Object camera_this, int cameraId);
@@ -318,6 +322,15 @@ public class Camera {
public final void release() {
native_release();
mFaceDetectionRunning = false;
+ if (mCameraSoundPlayers != null) {
+ for (CameraSoundPlayer csp: mCameraSoundPlayers) {
+ if (csp != null) {
+ csp.release();
+ }
+ }
+ mCameraSoundPlayers = null;
+ }
+ mReleased = true;
}
/**
@@ -2354,7 +2367,7 @@ public class Camera {
*
* <p>The reference code is as follows.
*
- * <pre>
+ * <pre>
* public void onOrientationChanged(int orientation) {
* if (orientation == ORIENTATION_UNKNOWN) return;
* android.hardware.Camera.CameraInfo info =
@@ -2369,7 +2382,7 @@ public class Camera {
* }
* mParameters.setRotation(rotation);
* }
- * </pre>
+ * </pre>
*
* @param rotation The rotation angle in degrees relative to the
* orientation of the camera. Rotation can only be 0,
@@ -3452,4 +3465,194 @@ public class Camera {
return result;
}
};
+
+ /**
+ * <p>The set of default system sounds for camera actions. Use this with
+ * {@link #playSound} to play an appropriate sound when implementing a
+ * custom still or video recording mechanism through the preview
+ * callbacks.</p>
+ *
+ * <p>There is no need to play sounds when using {@link #takePicture} or
+ * {@link android.media.MediaRecorder} for still images or video,
+ * respectively, as these play their own sounds when needed.</p>
+ *
+ * @see #playSound
+ * @hide
+ */
+ public static class Sound {
+ /**
+ * The sound used by {@link android.hardware.Camera#takePicture} to
+ * indicate still image capture.
+ */
+ public static final int SHUTTER_CLICK = 0;
+
+ /**
+ * A sound to indicate that focusing has completed. Because deciding
+ * when this occurs is application-dependent, this sound is not used by
+ * any methods in the Camera class.
+ */
+ public static final int FOCUS_COMPLETE = 1;
+
+ /**
+ * The sound used by {@link android.media.MediaRecorder#start} to
+ * indicate the start of video recording.
+ */
+ public static final int START_VIDEO_RECORDING = 2;
+
+ /**
+ * The sound used by {@link android.media.MediaRecorder#stop} to
+ * indicate the end of video recording.
+ */
+ public static final int STOP_VIDEO_RECORDING = 3;
+
+ private static final int NUM_SOUNDS = 4;
+ };
+
+ /**
+ * <p>Play one of the predefined platform sounds for camera actions.</p>
+ *
+ * <p>Use this method to play a platform-specific sound for various camera
+ * actions. The sound playing is done asynchronously, with the same behavior
+ * and content as the sounds played by {@link #takePicture takePicture},
+ * {@link android.media.MediaRecorder#start MediaRecorder.start}, and
+ * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p>
+ *
+ * <p>Using this method makes it easy to match the default device sounds
+ * when recording or capturing data through the preview callbacks
+ * ({@link #setPreviewCallback setPreviewCallback},
+ * {@link #setPreviewTexture setPreviewTexture}).</p>
+ *
+ * @param soundId The type of sound to play, selected from the options in
+ * {@link android.hardware.Camera.Sound}
+ * @see android.hardware.Camera.Sound
+ * @see #takePicture
+ * @see android.media.MediaRecorder
+ * @hide
+ */
+ public void playSound(int soundId) {
+ if (mReleased) return;
+ if (mCameraSoundPlayers == null) {
+ mCameraSoundPlayers = new CameraSoundPlayer[Sound.NUM_SOUNDS];
+ }
+ if (mCameraSoundPlayers[soundId] == null) {
+ mCameraSoundPlayers[soundId] = new CameraSoundPlayer(soundId);
+ }
+ mCameraSoundPlayers[soundId].play();
+ }
+
+ private CameraSoundPlayer[] mCameraSoundPlayers;
+
+ private static class CameraSoundPlayer implements Runnable {
+ private int mSoundId;
+ private int mAudioStreamType;
+ private MediaPlayer mPlayer;
+ private Thread mThread;
+ private boolean mExit;
+ private int mPlayCount;
+
+ private static final String mShutterSound =
+ "/system/media/audio/ui/camera_click.ogg";
+ private static final String mFocusSound =
+ "/system/media/audio/ui/camera_focus.ogg";
+ private static final String mVideoStartSound =
+ "/system/media/audio/ui/VideoRecord.ogg";
+ private static final String mVideoStopSound =
+ "/system/media/audio/ui/VideoRecord.ogg";
+
+ @Override
+ public void run() {
+ String soundFilePath;
+ switch (mSoundId) {
+ case Sound.SHUTTER_CLICK:
+ soundFilePath = mShutterSound;
+ break;
+ case Sound.FOCUS_COMPLETE:
+ soundFilePath = mFocusSound;
+ break;
+ case Sound.START_VIDEO_RECORDING:
+ soundFilePath = mVideoStartSound;
+ break;
+ case Sound.STOP_VIDEO_RECORDING:
+ soundFilePath = mVideoStopSound;
+ break;
+ default:
+ Log.e(TAG, "Unknown sound " + mSoundId + " requested.");
+ return;
+ }
+ mPlayer = new MediaPlayer();
+ try {
+ mPlayer.setAudioStreamType(mAudioStreamType);
+ mPlayer.setDataSource(soundFilePath);
+ mPlayer.setLooping(false);
+ mPlayer.prepare();
+ } catch(IOException e) {
+ Log.e(TAG, "Error setting up sound " + mSoundId, e);
+ return;
+ }
+
+ while(true) {
+ try {
+ synchronized (this) {
+ while(true) {
+ if (mExit) {
+ return;
+ } else if (mPlayCount <= 0) {
+ wait();
+ } else {
+ mPlayCount--;
+ break;
+ }
+ }
+ }
+ mPlayer.start();
+ } catch (Exception e) {
+ Log.e(TAG, "Error playing sound " + mSoundId, e);
+ }
+ }
+ }
+
+ public CameraSoundPlayer(int soundId) {
+ mSoundId = soundId;
+ if (SystemProperties.get("ro.camera.sound.forced", "0").equals("0")) {
+ mAudioStreamType = AudioManager.STREAM_MUSIC;
+ } else {
+ mAudioStreamType = AudioManager.STREAM_SYSTEM_ENFORCED;
+ }
+ }
+
+ public void play() {
+ if (mThread == null) {
+ mThread = new Thread(this);
+ mThread.start();
+ }
+ synchronized (this) {
+ mPlayCount++;
+ notifyAll();
+ }
+ }
+
+ public void release() {
+ if (mThread != null) {
+ synchronized (this) {
+ mExit = true;
+ notifyAll();
+ }
+ try {
+ mThread.join();
+ } catch (InterruptedException e) {
+ }
+ mThread = null;
+ }
+ if (mPlayer != null) {
+ mPlayer.release();
+ mPlayer = null;
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ release();
+ }
+ }
+
}
diff --git a/core/java/android/os/RemoteException.java b/core/java/android/os/RemoteException.java
index 9d76156..e30d24f 100644
--- a/core/java/android/os/RemoteException.java
+++ b/core/java/android/os/RemoteException.java
@@ -24,4 +24,8 @@ public class RemoteException extends AndroidException {
public RemoteException() {
super();
}
+
+ public RemoteException(String message) {
+ super(message);
+ }
}
diff --git a/core/java/android/os/TransactionTooLargeException.java b/core/java/android/os/TransactionTooLargeException.java
new file mode 100644
index 0000000..25f09e8
--- /dev/null
+++ b/core/java/android/os/TransactionTooLargeException.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+import android.os.RemoteException;
+
+/**
+ * The Binder transaction failed because it was too large.
+ * <p>
+ * During a remote procedure call, the arguments and the return value of the call
+ * are transferred as {@link Parcel} objects stored in the Binder transaction buffer.
+ * If the arguments or the return value are too large to fit in the transaction buffer,
+ * then the call will fail and {@link TransactionTooLargeException} will be thrown.
+ * </p><p>
+ * The Binder transaction buffer has a limited fixed size, currently 1Mb, which
+ * is shared by all transactions in progress for the process. Consequently this
+ * exception can be thrown when there are many transactions in progress even when
+ * most of the individual transactions are of moderate size.
+ * </p><p>
+ * There are two possible outcomes when a remote procedure call throws
+ * {@link TransactionTooLargeException}. Either the client was unable to send
+ * its request to the service (most likely if the arguments were too large to fit in
+ * the transaction buffer), or the service was unable to send its response back
+ * to the client (most likely if the return value was too large to fit
+ * in the transaction buffer). It is not possible to tell which of these outcomes
+ * actually occurred. The client should assume that a partial failure occurred.
+ * </p><p>
+ * The key to avoiding {@link TransactionTooLargeException} is to keep all
+ * transactions relatively small. Try to minimize the amount of memory needed to create
+ * a {@link Parcel} for the arguments and the return value of the remote procedure call.
+ * Avoid transferring huge arrays of strings or large bitmaps.
+ * If possible, try to break up big requests into smaller pieces.
+ * </p><p>
+ * If you are implementing a service, it may help to impose size or complexity
+ * contraints on the queries that clients can perform. For example, if the result set
+ * could become large, then don't allow the client to request more than a few records
+ * at a time. Alternately, instead of returning all of the available data all at once,
+ * return the essential information first and make the client ask for additional information
+ * later as needed.
+ * </p>
+ */
+public class TransactionTooLargeException extends RemoteException {
+ public TransactionTooLargeException() {
+ super();
+ }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1b7c327..4349063 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4097,13 +4097,6 @@ public final class Settings {
"contacts_preauth_uri_expiration";
/**
- * Whether the Messaging app posts notifications.
- * 0=disabled. 1=enabled.
- */
- public static final String MESSAGING_APP_NOTIFICATIONS = "messaging_app_notifications";
-
-
- /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -4140,8 +4133,7 @@ public final class Settings {
MOUNT_UMS_NOTIFY_ENABLED,
UI_NIGHT_MODE,
LOCK_SCREEN_OWNER_INFO,
- LOCK_SCREEN_OWNER_INFO_ENABLED,
- MESSAGING_APP_NOTIFICATIONS
+ LOCK_SCREEN_OWNER_INFO_ENABLED
};
/**
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 8eb9da1..0e6d07d 100755
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -1838,15 +1838,5 @@ public final class Telephony {
public static final String EXTRA_PLMN = "plmn";
public static final String EXTRA_SHOW_SPN = "showSpn";
public static final String EXTRA_SPN = "spn";
-
- /**
- * Activity Action: Shows a dialog to turn off Messaging app notification.
- * <p>Input: Nothing.
- * <p>Output: Nothing.
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_MESSAGING_APP_NOTIFICATIONS =
- "android.provider.Telephony.MESSAGING_APP_NOTIFICATIONS";
-
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index feea4ee..2095e91 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11458,8 +11458,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* {@link SystemClock#uptimeMillis} timebase.
*/
public void scheduleDrawable(Drawable who, Runnable what, long when) {
- if (verifyDrawable(who) && what != null && mAttachInfo != null) {
- mAttachInfo.mHandler.postAtTime(what, who, when);
+ if (verifyDrawable(who) && what != null) {
+ if (mAttachInfo != null) {
+ mAttachInfo.mHandler.postAtTime(what, who, when);
+ } else {
+ ViewRootImpl.getRunQueue().postDelayed(what, when - SystemClock.uptimeMillis());
+ }
}
}
@@ -11470,8 +11474,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* @param what the action to cancel
*/
public void unscheduleDrawable(Drawable who, Runnable what) {
- if (verifyDrawable(who) && what != null && mAttachInfo != null) {
- mAttachInfo.mHandler.removeCallbacks(what, who);
+ if (verifyDrawable(who) && what != null) {
+ if (mAttachInfo != null) {
+ mAttachInfo.mHandler.removeCallbacks(what, who);
+ } else {
+ ViewRootImpl.getRunQueue().removeCallbacks(what);
+ }
}
}
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 01b114c..0eb6e27 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -146,6 +146,13 @@ public class SpellCheckerSession {
}
/**
+ * Cancel pending and running spell check tasks
+ */
+ public void cancel() {
+ mSpellCheckerSessionListenerImpl.cancel();
+ }
+
+ /**
* Finish this session and allow TextServicesManagerService to disconnect the bound spell
* checker.
*/
@@ -242,6 +249,13 @@ public class SpellCheckerSession {
}
}
+ public void cancel() {
+ if (DBG) {
+ Log.w(TAG, "cancel");
+ }
+ processOrEnqueueTask(new SpellCheckerParams(TASK_CANCEL, null, 0, false));
+ }
+
public void getSuggestionsMultiple(
TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
if (DBG) {
@@ -275,8 +289,22 @@ public class SpellCheckerSession {
if (DBG) {
Log.d(TAG, "process or enqueue task: " + mISpellCheckerSession);
}
+ SpellCheckerParams closeTask = null;
if (mISpellCheckerSession == null) {
+ if (scp.mWhat == TASK_CANCEL) {
+ while (!mPendingTasks.isEmpty()) {
+ final SpellCheckerParams tmp = mPendingTasks.poll();
+ if (tmp.mWhat == TASK_CLOSE) {
+ // Only one close task should be processed, while we need to remove all
+ // close tasks from the queue
+ closeTask = tmp;
+ }
+ }
+ }
mPendingTasks.offer(scp);
+ if (closeTask != null) {
+ mPendingTasks.offer(closeTask);
+ }
} else {
processTask(scp);
}
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 388920c..c194559 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -1404,4 +1404,17 @@ class BrowserFrame extends Handler {
native void nativeSslClientCert(int handle,
byte[] pkcs8EncodedPrivateKey,
byte[][] asn1DerEncodedCertificateChain);
+
+ /**
+ * Returns true when the contents of the frame is an RTL or vertical-rl
+ * page. This is used for determining whether a frame should be initially
+ * scrolled right-most as opposed to left-most.
+ * @return true when the frame should be initially scrolled right-most
+ * based on the text direction and writing mode.
+ */
+ /* package */ boolean getShouldStartScrolledRight() {
+ return nativeGetShouldStartScrolledRight(mNativeFrame);
+ }
+
+ private native boolean nativeGetShouldStartScrolledRight(int nativeBrowserFrame);
}
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
index b85fd17..fffa90b 100644
--- a/core/java/android/webkit/FindActionModeCallback.java
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -18,6 +18,8 @@ package android.webkit;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
@@ -254,13 +256,18 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher,
// Does nothing. Needed to implement TextWatcher.
}
- public int getActionModeHeight() {
+ private Rect mGlobalVisibleRect = new Rect();
+ private Point mGlobalVisibleOffset = new Point();
+ public int getActionModeGlobalBottom() {
if (mActionMode == null) {
return 0;
}
- View parent = (View) mCustomView.getParent();
- return parent != null ? parent.getMeasuredHeight()
- : mCustomView.getMeasuredHeight();
+ View view = (View) mCustomView.getParent();
+ if (view == null) {
+ view = mCustomView;
+ }
+ view.getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
+ return mGlobalVisibleRect.bottom;
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index df2d126..b020cbc 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1484,7 +1484,21 @@ public class WebView extends AbsoluteLayout
private int getVisibleTitleHeightImpl() {
// need to restrict mScrollY due to over scroll
return Math.max(getTitleHeight() - Math.max(0, mScrollY),
- mFindCallback != null ? mFindCallback.getActionModeHeight() : 0);
+ getOverlappingActionModeHeight());
+ }
+
+ private int mCachedOverlappingActionModeHeight = -1;
+
+ private int getOverlappingActionModeHeight() {
+ if (mFindCallback == null) {
+ return 0;
+ }
+ if (mCachedOverlappingActionModeHeight < 0) {
+ getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
+ mCachedOverlappingActionModeHeight = Math.max(0,
+ mFindCallback.getActionModeGlobalBottom() - mGlobalVisibleRect.top);
+ }
+ return mCachedOverlappingActionModeHeight;
}
/*
@@ -3240,6 +3254,26 @@ public class WebView extends AbsoluteLayout
if (mHTML5VideoViewProxy != null) {
mHTML5VideoViewProxy.pauseAndDispatch();
}
+ if (mNativeClass != 0) {
+ nativeSetPauseDrawing(mNativeClass, true);
+ }
+ }
+ }
+
+ @Override
+ protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+ updateDrawingState();
+ }
+
+ void updateDrawingState() {
+ if (mNativeClass == 0 || mIsPaused) return;
+ if (getWindowVisibility() != VISIBLE) {
+ nativeSetPauseDrawing(mNativeClass, true);
+ } else if (getVisibility() != VISIBLE) {
+ nativeSetPauseDrawing(mNativeClass, true);
+ } else {
+ nativeSetPauseDrawing(mNativeClass, false);
}
}
@@ -3251,6 +3285,9 @@ public class WebView extends AbsoluteLayout
if (mIsPaused) {
mIsPaused = false;
mWebViewCore.sendMessage(EventHub.ON_RESUME);
+ if (mNativeClass != 0) {
+ nativeSetPauseDrawing(mNativeClass, false);
+ }
}
}
@@ -3375,6 +3412,7 @@ public class WebView extends AbsoluteLayout
// Could not start the action mode, so end Find on page
return false;
}
+ mCachedOverlappingActionModeHeight = -1;
mFindCallback = callback;
setFindIsUp(true);
mFindCallback.setWebView(this);
@@ -3492,6 +3530,7 @@ public class WebView extends AbsoluteLayout
*/
void notifyFindDialogDismissed() {
mFindCallback = null;
+ mCachedOverlappingActionModeHeight = -1;
if (mWebViewCore == null) {
return;
}
@@ -4346,6 +4385,7 @@ public class WebView extends AbsoluteLayout
@Override
protected void onConfigurationChanged(Configuration newConfig) {
+ mCachedOverlappingActionModeHeight = -1;
if (mSelectingText && mOrientation != newConfig.orientation) {
selectionDone();
}
@@ -5587,6 +5627,7 @@ public class WebView extends AbsoluteLayout
if (visibility != View.VISIBLE && mZoomManager != null) {
mZoomManager.dismissZoomPicker();
}
+ updateDrawingState();
}
/**
@@ -8391,6 +8432,9 @@ public class WebView extends AbsoluteLayout
setNewPicture(mDelaySetPicture, true);
mDelaySetPicture = null;
}
+ if (mIsPaused) {
+ nativeSetPauseDrawing(mNativeClass, true);
+ }
break;
case UPDATE_TEXTFIELD_TEXT_MSG_ID:
// Make sure that the textfield is currently focused
@@ -8721,27 +8765,6 @@ public class WebView extends AbsoluteLayout
isPictureAfterFirstLayout, registerPageSwapCallback);
}
final Point viewSize = draw.mViewSize;
- if (isPictureAfterFirstLayout) {
- // Reset the last sent data here since dealing with new page.
- mLastWidthSent = 0;
- mZoomManager.onFirstLayout(draw);
- if (!mDrawHistory) {
- // Do not send the scroll event for this particular
- // scroll message. Note that a scroll event may
- // still be fired if the user scrolls before the
- // message can be handled.
- mSendScrollEvent = false;
- setContentScrollTo(viewState.mScrollX, viewState.mScrollY);
- mSendScrollEvent = true;
-
- // As we are on a new page, remove the WebTextView. This
- // is necessary for page loads driven by webkit, and in
- // particular when the user was on a password field, so
- // the WebTextView was visible.
- clearTextEntry();
- }
- }
-
// We update the layout (i.e. request a layout from the
// view system) if the last view size that we sent to
// WebCore matches the view size of the picture we just
@@ -8754,7 +8777,25 @@ public class WebView extends AbsoluteLayout
mSendScrollEvent = false;
recordNewContentSize(draw.mContentSize.x,
draw.mContentSize.y, updateLayout);
+
+ if (isPictureAfterFirstLayout) {
+ // Reset the last sent data here since dealing with new page.
+ mLastWidthSent = 0;
+ mZoomManager.onFirstLayout(draw);
+ int scrollX = viewState.mShouldStartScrolledRight
+ ? getContentWidth() : viewState.mScrollX;
+ int scrollY = viewState.mScrollY;
+ setContentScrollTo(scrollX, scrollY);
+ if (!mDrawHistory) {
+ // As we are on a new page, remove the WebTextView. This
+ // is necessary for page loads driven by webkit, and in
+ // particular when the user was on a password field, so
+ // the WebTextView was visible.
+ clearTextEntry();
+ }
+ }
mSendScrollEvent = true;
+
if (DebugFlags.WEB_VIEW) {
Rect b = draw.mInvalRegion.getBounds();
Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
@@ -9572,4 +9613,5 @@ public class WebView extends AbsoluteLayout
* See {@link ComponentCallbacks2} for the trim levels and descriptions
*/
private static native void nativeOnTrimMemory(int level);
+ private static native void nativeSetPauseDrawing(int instance, boolean pause);
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 754d6e9..cd61481 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1982,6 +1982,7 @@ public final class WebViewCore {
int mScrollY;
boolean mMobileSite;
boolean mIsRestored;
+ boolean mShouldStartScrolledRight;
}
static class DrawData {
@@ -2382,6 +2383,7 @@ public final class WebViewCore {
viewState.mMobileSite = false;
// for non-mobile site, we don't need minPrefWidth, set it as 0
viewState.mScrollX = 0;
+ viewState.mShouldStartScrolledRight = false;
Message.obtain(mWebView.mPrivateHandler,
WebView.UPDATE_ZOOM_RANGE, viewState).sendToTarget();
return;
@@ -2412,6 +2414,11 @@ public final class WebViewCore {
mInitialViewState.mDefaultScale = adjust;
mInitialViewState.mScrollX = mRestoredX;
mInitialViewState.mScrollY = mRestoredY;
+ mInitialViewState.mShouldStartScrolledRight = (mRestoredX == 0)
+ && (mRestoredY == 0)
+ && (mBrowserFrame != null)
+ && mBrowserFrame.getShouldStartScrolledRight();
+
mInitialViewState.mMobileSite = (0 == mViewportWidth);
if (mIsRestored) {
mInitialViewState.mIsRestored = true;
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index b24dd69..1e2d0d1 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -1035,4 +1035,28 @@ public class ImageView extends View {
mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8);
}
}
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+ if (mDrawable != null) {
+ mDrawable.setVisible(visibility == VISIBLE, false);
+ }
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mDrawable != null) {
+ mDrawable.setVisible(getVisibility() == VISIBLE, false);
+ }
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mDrawable != null) {
+ mDrawable.setVisible(false, false);
+ }
+ }
}
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 97678da..2184297 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -82,12 +82,13 @@ public class SpellChecker implements SpellCheckerSessionListener {
mIds = new int[size];
mSpellCheckSpans = new SpellCheckSpan[size];
- setLocale(mTextView.getLocale());
+ setLocale(mTextView.getTextServicesLocale());
mCookie = hashCode();
}
private void setLocale(Locale locale) {
+ closeSession();
final TextServicesManager textServicesManager = (TextServicesManager)
mTextView.getContext().getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
if (!textServicesManager.isSpellCheckerEnabled()) {
@@ -107,8 +108,6 @@ public class SpellChecker implements SpellCheckerSessionListener {
}
mLength = 0;
- // Reset the SpellParser pool: they will get re-created on demand
- stopAllSpellParsers();
mSpellParsers = new SpellParser[0];
// This class is the global listener for locale change: warn other locale-aware objects
@@ -179,7 +178,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
}
public void spellCheck(int start, int end) {
- final Locale locale = mTextView.getLocale();
+ final Locale locale = mTextView.getTextServicesLocale();
if (mCurrentLocale == null || (!(mCurrentLocale.equals(locale)))) {
setLocale(locale);
// Re-check the entire text
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0cc5bcc..6638ea6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -36,7 +36,6 @@ import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.ExtractEditText;
-import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -101,7 +100,6 @@ import android.util.Log;
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.ActionMode.Callback;
-import android.view.ContextMenu;
import android.view.DragEvent;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -133,6 +131,8 @@ import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
+import android.view.textservice.SpellCheckerSubtype;
+import android.view.textservice.TextServicesManager;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.RemoteViews.RemoteView;
@@ -6077,6 +6077,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mSavedMarqueeModeLayout = mLayout = mHintLayout = null;
+ mBoring = mHintBoring = null;
+
// Since it depends on the value of mLayout
prepareCursorControllers();
}
@@ -8374,10 +8376,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// When the cursor moves, the word that was typed may need spell check
mSpellChecker.onSelectionChanged();
}
- if (isCursorInsideEasyCorrectionSpan()) {
- showSuggestions();
- } else if (hasInsertionController()) {
- getInsertionController().show();
+ if (!extractedTextModeWillBeStarted()) {
+ if (isCursorInsideEasyCorrectionSpan()) {
+ showSuggestions();
+ } else if (hasInsertionController()) {
+ getInsertionController().show();
+ }
}
}
@@ -8923,21 +8927,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* This is a temporary method. Future versions may support multi-locale text.
*
- * @return The current locale used in this TextView, based on the current IME's locale,
- * or the system default locale if this is not defined.
+ * @return The locale that should be used for a word iterator and a spell checker
+ * in this TextView, based on the current spell checker settings,
+ * the current IME's locale, or the system default locale.
* @hide
*/
- public Locale getLocale() {
+ public Locale getTextServicesLocale() {
Locale locale = Locale.getDefault();
- final InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- final InputMethodSubtype currentInputMethodSubtype = imm.getCurrentInputMethodSubtype();
- if (currentInputMethodSubtype != null) {
- String localeString = currentInputMethodSubtype.getLocale();
- if (!TextUtils.isEmpty(localeString)) {
- locale = new Locale(localeString);
- }
- }
+ final TextServicesManager textServicesManager = (TextServicesManager)
+ mContext.getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
+ final SpellCheckerSubtype subtype = textServicesManager.getCurrentSpellCheckerSubtype(true);
+ if (subtype != null) {
+ locale = new Locale(subtype.getLocale());
}
return locale;
}
@@ -8953,7 +8954,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
public WordIterator getWordIterator() {
if (mWordIterator == null) {
- mWordIterator = new WordIterator(getLocale());
+ mWordIterator = new WordIterator(getTextServicesLocale());
}
return mWordIterator;
}
@@ -10133,27 +10134,35 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- final InputMethodManager imm = InputMethodManager.peekInstance();
- boolean extractedTextModeWillBeStartedFullScreen = !(this instanceof ExtractEditText) &&
- imm != null && imm.isFullscreenMode();
+ boolean willExtract = extractedTextModeWillBeStarted();
// Do not start the action mode when extracted text will show up full screen, thus
// immediately hiding the newly created action bar, which would be visually distracting.
- if (!extractedTextModeWillBeStartedFullScreen) {
+ if (!willExtract) {
ActionMode.Callback actionModeCallback = new SelectionActionModeCallback();
mSelectionActionMode = startActionMode(actionModeCallback);
}
- final boolean selectionStarted = mSelectionActionMode != null ||
- extractedTextModeWillBeStartedFullScreen;
- if (selectionStarted && !mTextIsSelectable && imm != null && mSoftInputShownOnFocus) {
+ final boolean selectionStarted = mSelectionActionMode != null || willExtract;
+ if (selectionStarted && !mTextIsSelectable && mSoftInputShownOnFocus) {
// Show the IME to be able to replace text, except when selecting non editable text.
- imm.showSoftInput(this, 0, null);
+ final InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null) {
+ imm.showSoftInput(this, 0, null);
+ }
}
return selectionStarted;
}
+ private boolean extractedTextModeWillBeStarted() {
+ if (!(this instanceof ExtractEditText)) {
+ final InputMethodManager imm = InputMethodManager.peekInstance();
+ return imm != null && imm.isFullscreenMode();
+ }
+ return false;
+ }
+
private void stopSelectionActionMode() {
if (mSelectionActionMode != null) {
// This will hide the mSelectionModifierCursorController
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 17b8acf..89f9d4e 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -25,14 +25,11 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.hardware.Camera;
-import android.hardware.Camera.CameraInfo;
import android.os.FileObserver;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.storage.IMountService;
import android.provider.Settings;
import android.security.KeyStore;
@@ -41,7 +38,6 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
-import android.widget.TextView;
import java.io.File;
import java.io.FileNotFoundException;
@@ -968,6 +964,11 @@ public class LockPatternUtils {
com.android.internal.R.bool.config_enable_puk_unlock_screen);
}
+ public boolean isEmergencyCallEnabledWhileSimLocked() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked);
+ }
+
/**
* @return A formatted string of the next alarm (for showing on the lock screen),
* or null if there is no next alarm.
@@ -1031,12 +1032,10 @@ public class LockPatternUtils {
* {@link TelephonyManager#CALL_STATE_IDLE}
* {@link TelephonyManager#CALL_STATE_RINGING}
* {@link TelephonyManager#CALL_STATE_OFFHOOK}
- * @param showIfCapable indicates whether the button should be shown if emergency calls are
- * possible on the device
+ * @param shown indicates whether the given screen wants the emergency button to show at all
*/
- public void updateEmergencyCallButtonState(Button button, int phoneState,
- boolean showIfCapable) {
- if (isEmergencyCallCapable() && showIfCapable) {
+ public void updateEmergencyCallButtonState(Button button, int phoneState, boolean shown) {
+ if (isEmergencyCallCapable() && shown) {
button.setVisibility(View.VISIBLE);
} else {
button.setVisibility(View.GONE);
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index f76b64d..bd62268 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -38,6 +38,7 @@
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <utils/threads.h>
+#include <utils/String8.h>
#include <ScopedUtfChars.h>
#include <ScopedLocalRef.h>
@@ -651,7 +652,8 @@ jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc)
gParcelFileDescriptorOffsets.mClass, gParcelFileDescriptorOffsets.mConstructor, fileDesc);
}
-void signalExceptionForError(JNIEnv* env, jobject obj, status_t err)
+static void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
+ bool canThrowRemoteException = false)
{
switch (err) {
case UNKNOWN_ERROR:
@@ -688,14 +690,25 @@ void signalExceptionForError(JNIEnv* env, jobject obj, status_t err)
jniThrowException(env, "java/lang/RuntimeException", "Item already exists");
break;
case DEAD_OBJECT:
- jniThrowException(env, "android/os/DeadObjectException", NULL);
+ // DeadObjectException is a checked exception, only throw from certain methods.
+ jniThrowException(env, canThrowRemoteException
+ ? "android/os/DeadObjectException"
+ : "java/lang/RuntimeException", NULL);
break;
case UNKNOWN_TRANSACTION:
jniThrowException(env, "java/lang/RuntimeException", "Unknown transaction code");
break;
case FAILED_TRANSACTION:
LOGE("!!! FAILED BINDER TRANSACTION !!!");
- //jniThrowException(env, "java/lang/OutOfMemoryError", "Binder transaction too large");
+ // TransactionTooLargeException is a checked exception, only throw from certain methods.
+ // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
+ // but it is not the only one. The Binder driver can return BR_FAILED_REPLY
+ // for other reasons also, such as if the transaction is malformed or
+ // refers to an FD that has been closed. We should change the driver
+ // to enable us to distinguish these cases in the future.
+ jniThrowException(env, canThrowRemoteException
+ ? "android/os/TransactionTooLargeException"
+ : "java/lang/RuntimeException", NULL);
break;
case FDS_NOT_ALLOWED:
jniThrowException(env, "java/lang/RuntimeException",
@@ -703,6 +716,12 @@ void signalExceptionForError(JNIEnv* env, jobject obj, status_t err)
break;
default:
LOGE("Unknown binder error code. 0x%x", err);
+ String8 msg;
+ msg.appendFormat("Unknown binder error code. 0x%x", err);
+ // RemoteException is a checked exception, only throw from certain methods.
+ jniThrowException(env, canThrowRemoteException
+ ? "android/os/RemoteException" : "java/lang/RuntimeException", msg.string());
+ break;
}
}
@@ -1036,8 +1055,7 @@ static bool should_time_binder_calls() {
}
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
- jint code, jobject dataObj,
- jobject replyObj, jint flags)
+ jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
if (dataObj == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -1084,12 +1102,12 @@ static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
return JNI_FALSE;
}
- signalExceptionForError(env, obj, err);
+ signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
return JNI_FALSE;
}
static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
- jobject recipient, jint flags)
+ jobject recipient, jint flags) // throws RemoteException
{
if (recipient == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -1114,7 +1132,7 @@ static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
// Failure adding the death recipient, so clear its reference
// now.
jdr->clearReference();
- signalExceptionForError(env, obj, err);
+ signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
}
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0ed0523..230df39 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1027,6 +1027,13 @@
android:label="@string/permlab_readLogs"
android:description="@string/permdesc_readLogs" />
+ <!-- Allows an application to use any media decoder when decoding for playback
+ @hide -->
+ <permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
+ android:protectionLevel="signatureOrSystem"
+ android:label="@string/permlab_anyCodecForPlayback"
+ android:description="@string/permdesc_anyCodecForPlayback" />
+
<!-- ========================================= -->
<!-- Permissions for special development tools -->
<!-- ========================================= -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8b07e34..767cafe 100644..100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -453,6 +453,11 @@
If unlock screen is disabled, the puk should be unlocked through Emergency Dialer -->
<bool name="config_enable_puk_unlock_screen">true</bool>
+ <!-- Enable emergency call when sim is locked or puk locked. Some countries/carriers do not
+ allow emergency calls to be placed without the IMSI, which is locked in the SIM.
+ If so, this should be set to 'false' in an overlay. -->
+ <bool name="config_enable_emergency_call_while_sim_locked">true</bool>
+
<!-- Control the behavior when the user long presses the home button.
0 - Nothing
1 - Recent apps dialog
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 333736b..25ca27c 100644..100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -822,6 +822,12 @@
including personal or private information.</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_anyCodecForPlayback">use any media decoder for playback</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_anyCodecForPlayback">Allows an application to use any installed
+ media decoder to decode for playback.</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_diagnostic">read/write to resources owned by diag</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_diagnostic">Allows the app to read and write to
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
index 0cc883f..01a5fd0 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -90,7 +90,26 @@ public class BandwidthTest extends InstrumentationTestCase {
*/
@LargeTest
public void testWifiDownload() throws Exception {
- assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
+ assertTrue("Could not connect to wifi!", setDeviceWifiAndAirplaneMode(mSsid));
+ downloadFile();
+ }
+
+ /**
+ * Ensure that downloading on mobile reports reasonable stats.
+ */
+ @LargeTest
+ public void testMobileDownload() throws Exception {
+ // As part of the setup we disconnected from wifi; make sure we are connected to mobile and
+ // that we have data.
+ assertTrue("Do not have mobile data!", hasMobileData());
+ downloadFile();
+ }
+
+ /**
+ * Helper method that downloads a file using http connection from a test server and reports the
+ * data usage stats to instrumentation out.
+ */
+ protected void downloadFile() throws Exception {
NetworkStats pre_test_stats = fetchDataFromProc(mUid);
String ts = Long.toString(System.currentTimeMillis());
@@ -120,11 +139,28 @@ public class BandwidthTest extends InstrumentationTestCase {
}
/**
- * Ensure that downloading on wifi reports reasonable stats.
+ * Ensure that uploading on wifi reports reasonable stats.
*/
@LargeTest
public void testWifiUpload() throws Exception {
assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
+ uploadFile();
+ }
+
+ /**
+ * Ensure that uploading on wifi reports reasonable stats.
+ */
+ @LargeTest
+ public void testMobileUpload() throws Exception {
+ assertTrue(hasMobileData());
+ uploadFile();
+ }
+
+ /**
+ * Helper method that downloads a test file to upload. The stats reported to instrumentation out
+ * only include upload stats.
+ */
+ protected void uploadFile() throws Exception {
// Download a file from the server.
String ts = Long.toString(System.currentTimeMillis());
String targetUrl = BandwidthTestUtil.buildDownloadUrl(
@@ -156,12 +192,30 @@ public class BandwidthTest extends InstrumentationTestCase {
}
/**
- * We want to make sure that if we use the Download Manager to download stuff,
+ * We want to make sure that if we use wifi and the Download Manager to download stuff,
* accounting still goes to the app making the call and that the numbers still make sense.
*/
@LargeTest
public void testWifiDownloadWithDownloadManager() throws Exception {
assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
+ downloadFileUsingDownloadManager();
+ }
+
+ /**
+ * We want to make sure that if we use mobile data and the Download Manager to download stuff,
+ * accounting still goes to the app making the call and that the numbers still make sense.
+ */
+ @LargeTest
+ public void testMobileDownloadWithDownloadManager() throws Exception {
+ assertTrue(hasMobileData());
+ downloadFileUsingDownloadManager();
+ }
+
+ /**
+ * Helper method that downloads a file from a test server using the download manager and reports
+ * the stats to instrumentation out.
+ */
+ protected void downloadFileUsingDownloadManager() throws Exception {
// If we are using the download manager, then the data that is written to /proc/uid_stat/
// is accounted against download manager's uid, since it uses pre-ICS API.
int downloadManagerUid = mConnectionUtil.downloadManagerUid();
@@ -195,6 +249,7 @@ public class BandwidthTest extends InstrumentationTestCase {
/**
* Fetch network data from /proc/uid_stat/uid
+ *
* @return populated {@link NetworkStats}
*/
public NetworkStats fetchDataFromProc(int uid) {
@@ -210,7 +265,8 @@ public class BandwidthTest extends InstrumentationTestCase {
}
/**
- * Turn on Airplane mode and connect to the wifi
+ * Turn on Airplane mode and connect to the wifi.
+ *
* @param ssid of the wifi to connect to
* @return true if we successfully connected to a given network.
*/
@@ -219,12 +275,25 @@ public class BandwidthTest extends InstrumentationTestCase {
assertTrue(mConnectionUtil.connectToWifi(ssid));
assertTrue(mConnectionUtil.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
ConnectionUtil.LONG_TIMEOUT));
- return mConnectionUtil.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
- ConnectionUtil.LONG_TIMEOUT);
+ assertTrue(mConnectionUtil.waitForNetworkState(ConnectivityManager.TYPE_WIFI,
+ State.CONNECTED, ConnectionUtil.LONG_TIMEOUT));
+ return mConnectionUtil.hasData();
+ }
+
+ /**
+ * Helper method to make sure we are connected to mobile data.
+ *
+ * @return true if we successfully connect to mobile data.
+ */
+ public boolean hasMobileData() {
+ assertTrue("Not connected to mobile", mConnectionUtil.isConnectedToMobile());
+ assertFalse("Still connected to wifi.", mConnectionUtil.isConnectedToWifi());
+ return mConnectionUtil.hasData();
}
/**
* Output the {@link NetworkStats} to Instrumentation out.
+ *
* @param label to attach to this given stats.
* @param stats {@link NetworkStats} to add.
* @param results {@link Bundle} to be added to.
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
index d663aad..a5e5ab0 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
@@ -44,6 +44,8 @@ import com.android.bandwidthtest.NetworkState;
import com.android.bandwidthtest.NetworkState.StateTransitionDirection;
import com.android.internal.util.AsyncChannel;
+import java.io.IOException;
+import java.net.UnknownHostException;
import java.util.List;
/*
@@ -257,14 +259,14 @@ public class ConnectionUtil {
mConnectivityState[networkType].recordState(networkState);
}
- /**
- * Set the state transition criteria
- *
- * @param networkType
- * @param initState
- * @param transitionDir
- * @param targetState
- */
+ /**
+ * Set the state transition criteria
+ *
+ * @param networkType
+ * @param initState
+ * @param transitionDir
+ * @param targetState
+ */
public void setStateTransitionCriteria(int networkType, State initState,
StateTransitionDirection transitionDir, State targetState) {
mConnectivityState[networkType].setStateTransitionCriteria(
@@ -495,7 +497,8 @@ public class ConnectionUtil {
* @return true if connected to a mobile network, false otherwise.
*/
public boolean isConnectedToMobile() {
- return (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
+ NetworkInfo networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+ return networkInfo.isConnected();
}
/**
@@ -503,10 +506,10 @@ public class ConnectionUtil {
* @return true if connected to wifi, false otherwise.
*/
public boolean isConnectedToWifi() {
- return (mNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI);
+ NetworkInfo networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ return networkInfo.isConnected();
}
-
/**
* Associate the device to given SSID
* If the device is already associated with a WiFi, disconnect and forget it,
@@ -681,4 +684,30 @@ public class ConnectionUtil {
}
Log.v(LOG_TAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
}
+
+ /**
+ * Helper method used to test data connectivity by pinging a series of popular sites.
+ * @return true if device has data connectivity, false otherwise.
+ */
+ public boolean hasData() {
+ String[] hostList = {"www.google.com", "www.yahoo.com",
+ "www.bing.com", "www.facebook.com", "www.ask.com"};
+ try {
+ for (int i = 0; i < hostList.length; ++i) {
+ String host = hostList[i];
+ Process p = Runtime.getRuntime().exec("ping -c 10 -w 100 " + host);
+ int status = p.waitFor();
+ if (status == 0) {
+ return true;
+ }
+ }
+ } catch (UnknownHostException e) {
+ Log.e(LOG_TAG, "Ping test Failed: Unknown Host");
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Ping test Failed: IOException");
+ } catch (InterruptedException e) {
+ Log.e(LOG_TAG, "Ping test Failed: InterruptedException");
+ }
+ return false;
+ }
} \ No newline at end of file
diff --git a/data/sounds/AudioPackage5.mk b/data/sounds/AudioPackage5.mk
index 550f990..5961f06 100755
--- a/data/sounds/AudioPackage5.mk
+++ b/data/sounds/AudioPackage5.mk
@@ -20,6 +20,7 @@ PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
+ $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
diff --git a/data/sounds/AudioPackage6.mk b/data/sounds/AudioPackage6.mk
index 610e821..d113a29 100755
--- a/data/sounds/AudioPackage6.mk
+++ b/data/sounds/AudioPackage6.mk
@@ -19,6 +19,7 @@ PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
$(LOCAL_PATH)/effects/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
+ $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
diff --git a/data/sounds/AudioPackage7.mk b/data/sounds/AudioPackage7.mk
index 93e0fa0..6ae624e 100755
--- a/data/sounds/AudioPackage7.mk
+++ b/data/sounds/AudioPackage7.mk
@@ -21,6 +21,7 @@ PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
$(LOCAL_PATH)/effects/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
+ $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
diff --git a/data/sounds/effects/ogg/camera_focus.ogg b/data/sounds/effects/ogg/camera_focus.ogg
new file mode 100644
index 0000000..0db2683
--- /dev/null
+++ b/data/sounds/effects/ogg/camera_focus.ogg
Binary files differ
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
index 237e18c..41a5a51 100644
--- a/docs/html/resources/resources-data.js
+++ b/docs/html/resources/resources-data.js
@@ -578,7 +578,7 @@ var ANDROID_RESOURCES = [
}
},
{
- tags: ['sample', 'newfeature', 'performance', 'gamedev', 'gl'],
+ tags: ['sample', 'newfeature', 'performance', 'gamedev', 'gl', 'updated'],
path: 'samples/RenderScript/index.html',
title: {
en: 'RenderScript'
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 2f32bd8..fe15605 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -521,6 +521,9 @@ public class AudioService extends IAudioService.Stub {
ensureValidDirection(direction);
ensureValidStreamType(streamType);
+ // use stream type alias here so that streams with same alias have the same behavior,
+ // including with regard to silent mode control (e.g the use of STREAM_RING below and in
+ // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
int streamTypeAlias = STREAM_VOLUME_ALIAS[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
@@ -529,9 +532,8 @@ public class AudioService extends IAudioService.Stub {
// If either the client forces allowing ringer modes for this adjustment,
// or the stream type is one that is affected by ringer modes
if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
- (!mVoiceCapable && streamType != AudioSystem.STREAM_VOICE_CALL &&
- streamType != AudioSystem.STREAM_BLUETOOTH_SCO) ||
- (mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_RING)) {
+ streamTypeAlias == AudioSystem.STREAM_RING ||
+ (!mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_MUSIC)) {
// do not vibrate if already in vibrate mode
if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
flags &= ~AudioManager.FLAG_VIBRATE;
@@ -545,10 +547,19 @@ public class AudioService extends IAudioService.Stub {
int index;
if (streamState.muteCount() != 0) {
if (adjustVolume) {
- streamState.adjustLastAudibleIndex(direction);
- // Post a persist volume msg
- sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
- SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
+ // adjust volume on all stream types sharing the same alias otherwise a query
+ // on last audible index for an alias would not give the correct value
+ int numStreamTypes = AudioSystem.getNumStreamTypes();
+ for (int i = numStreamTypes - 1; i >= 0; i--) {
+ if (STREAM_VOLUME_ALIAS[i] == streamTypeAlias) {
+ VolumeStreamState s = mStreamStates[i];
+
+ s.adjustLastAudibleIndex(direction);
+ // Post a persist volume msg
+ sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, i,
+ SENDMSG_REPLACE, 0, 1, s, PERSIST_DELAY);
+ }
+ }
}
index = streamState.mLastAudibleIndex;
} else {
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index 7bbd07e..bedfc58 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -421,8 +421,13 @@ void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) {
int32_t bufferSize = inHeader->nFilledLen;
+ // The PV decoder is lying to us, sometimes it'll claim to only have
+ // consumed a subset of the buffer when it clearly consumed all of it.
+ // ignore whatever it says...
+ int32_t tmp = bufferSize;
+
if (PVDecodeVideoFrame(
- mHandle, &bitstream, &timestamp, &bufferSize,
+ mHandle, &bitstream, &timestamp, &tmp,
&useExtTimestamp,
outHeader->pBuffer) != PV_TRUE) {
LOGE("failed to decode video frame.");
diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
index 740c957..dede3ac 100644
--- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
+++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
@@ -76,7 +76,8 @@ SoftAVC::SoftAVC(
mPicId(0),
mHeadersDecoded(false),
mEOSStatus(INPUT_DATA_AVAILABLE),
- mOutputPortSettingsChange(NONE) {
+ mOutputPortSettingsChange(NONE),
+ mSignalledError(false) {
initPorts();
CHECK_EQ(initDecoder(), (status_t)OK);
}
@@ -287,7 +288,7 @@ OMX_ERRORTYPE SoftAVC::getConfig(
}
void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
- if (mOutputPortSettingsChange != NONE) {
+ if (mSignalledError || mOutputPortSettingsChange != NONE) {
return;
}
@@ -298,7 +299,6 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex);
List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
H264SwDecRet ret = H264SWDEC_PIC_RDY;
- status_t err = OK;
bool portSettingsChanged = false;
while ((mEOSStatus != INPUT_DATA_AVAILABLE || !inQueue.empty())
&& outQueue.size() == kNumOutputBuffers) {
@@ -372,7 +372,12 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
inPicture.dataLen = 0;
if (ret < 0) {
LOGE("Decoder failed: %d", ret);
- err = ERROR_MALFORMED;
+
+ notify(OMX_EventError, OMX_ErrorUndefined,
+ ERROR_MALFORMED, NULL);
+
+ mSignalledError = true;
+ return;
}
}
}
@@ -400,10 +405,6 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
uint8_t *data = (uint8_t *) decodedPicture.pOutputPicture;
drainOneOutputBuffer(picId, data);
}
-
- if (err != OK) {
- notify(OMX_EventError, OMX_ErrorUndefined, err, NULL);
- }
}
}
diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h
index 1cc85e8..879b014 100644
--- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h
+++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h
@@ -88,6 +88,8 @@ private:
};
OutputPortSettingChange mOutputPortSettingsChange;
+ bool mSignalledError;
+
void initPorts();
status_t initDecoder();
void updatePortDefinitions();
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 2abdefc..c5d8740 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -220,7 +220,7 @@ void ARTPConnection::onRemoveStream(const sp<AMessage> &msg) {
}
if (it == mStreams.end()) {
- TRESPASS();
+ return;
}
mStreams.erase(it);
@@ -274,41 +274,52 @@ void ARTPConnection::onPollStreams() {
}
int res = select(maxSocket + 1, &rs, NULL, NULL, &tv);
- CHECK_GE(res, 0);
if (res > 0) {
- for (List<StreamInfo>::iterator it = mStreams.begin();
- it != mStreams.end(); ++it) {
+ List<StreamInfo>::iterator it = mStreams.begin();
+ while (it != mStreams.end()) {
if ((*it).mIsInjected) {
+ ++it;
continue;
}
+ status_t err = OK;
if (FD_ISSET(it->mRTPSocket, &rs)) {
- receive(&*it, true);
+ err = receive(&*it, true);
}
- if (FD_ISSET(it->mRTCPSocket, &rs)) {
- receive(&*it, false);
+ if (err == OK && FD_ISSET(it->mRTCPSocket, &rs)) {
+ err = receive(&*it, false);
}
+
+ if (err == -ECONNRESET) {
+ // socket failure, this stream is dead, Jim.
+
+ LOGW("failed to receive RTP/RTCP datagram.");
+ it = mStreams.erase(it);
+ continue;
+ }
+
+ ++it;
}
}
- postPollEvent();
-
int64_t nowUs = ALooper::GetNowUs();
if (mLastReceiverReportTimeUs <= 0
|| mLastReceiverReportTimeUs + 5000000ll <= nowUs) {
sp<ABuffer> buffer = new ABuffer(kMaxUDPSize);
- for (List<StreamInfo>::iterator it = mStreams.begin();
- it != mStreams.end(); ++it) {
+ List<StreamInfo>::iterator it = mStreams.begin();
+ while (it != mStreams.end()) {
StreamInfo *s = &*it;
if (s->mIsInjected) {
+ ++it;
continue;
}
if (s->mNumRTCPPacketsReceived == 0) {
// We have never received any RTCP packets on this stream,
// we don't even know where to send a report.
+ ++it;
continue;
}
@@ -327,16 +338,34 @@ void ARTPConnection::onPollStreams() {
if (buffer->size() > 0) {
ALOGV("Sending RR...");
- ssize_t n = sendto(
+ ssize_t n;
+ do {
+ n = sendto(
s->mRTCPSocket, buffer->data(), buffer->size(), 0,
(const struct sockaddr *)&s->mRemoteRTCPAddr,
sizeof(s->mRemoteRTCPAddr));
+ } while (n < 0 && errno == EINTR);
+
+ if (n <= 0) {
+ LOGW("failed to send RTCP receiver report (%s).",
+ n == 0 ? "connection gone" : strerror(errno));
+
+ it = mStreams.erase(it);
+ continue;
+ }
+
CHECK_EQ(n, (ssize_t)buffer->size());
mLastReceiverReportTimeUs = nowUs;
}
+
+ ++it;
}
}
+
+ if (!mStreams.empty()) {
+ postPollEvent();
+ }
}
status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) {
@@ -350,16 +379,19 @@ status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) {
(!receiveRTP && s->mNumRTCPPacketsReceived == 0)
? sizeof(s->mRemoteRTCPAddr) : 0;
- ssize_t nbytes = recvfrom(
+ ssize_t nbytes;
+ do {
+ nbytes = recvfrom(
receiveRTP ? s->mRTPSocket : s->mRTCPSocket,
buffer->data(),
buffer->capacity(),
0,
remoteAddrLen > 0 ? (struct sockaddr *)&s->mRemoteRTCPAddr : NULL,
remoteAddrLen > 0 ? &remoteAddrLen : NULL);
+ } while (nbytes < 0 && errno == EINTR);
- if (nbytes < 0) {
- return -1;
+ if (nbytes <= 0) {
+ return -ECONNRESET;
}
buffer->setRange(0, nbytes);
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 8847878..3789b8d 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -44,12 +44,14 @@
// If no access units are received within 5 secs, assume that the rtp
// stream has ended and signal end of stream.
-static int64_t kAccessUnitTimeoutUs = 5000000ll;
+static int64_t kAccessUnitTimeoutUs = 10000000ll;
// If no access units arrive for the first 10 secs after starting the
// stream, assume none ever will and signal EOS or switch transports.
static int64_t kStartupTimeoutUs = 10000000ll;
+static int64_t kDefaultKeepAliveTimeoutUs = 60000000ll;
+
namespace android {
static void MakeUserAgentString(AString *s) {
@@ -130,7 +132,9 @@ struct MyHandler : public AHandler {
mTryFakeRTCP(false),
mReceivedFirstRTCPPacket(false),
mReceivedFirstRTPPacket(false),
- mSeekable(false) {
+ mSeekable(false),
+ mKeepAliveTimeoutUs(kDefaultKeepAliveTimeoutUs),
+ mKeepAliveGeneration(0) {
mNetLooper->setName("rtsp net");
mNetLooper->start(false /* runOnCallingThread */,
false /* canCallJava */,
@@ -371,6 +375,8 @@ struct MyHandler : public AHandler {
case 'disc':
{
+ ++mKeepAliveGeneration;
+
int32_t reconnect;
if (msg->findInt32("reconnect", &reconnect) && reconnect) {
sp<AMessage> reply = new AMessage('conn', id());
@@ -502,6 +508,34 @@ struct MyHandler : public AHandler {
CHECK_GE(i, 0);
mSessionID = response->mHeaders.valueAt(i);
+
+ mKeepAliveTimeoutUs = kDefaultKeepAliveTimeoutUs;
+ AString timeoutStr;
+ if (GetAttribute(
+ mSessionID.c_str(), "timeout", &timeoutStr)) {
+ char *end;
+ unsigned long timeoutSecs =
+ strtoul(timeoutStr.c_str(), &end, 10);
+
+ if (end == timeoutStr.c_str() || *end != '\0') {
+ LOGW("server specified malformed timeout '%s'",
+ timeoutStr.c_str());
+
+ mKeepAliveTimeoutUs = kDefaultKeepAliveTimeoutUs;
+ } else if (timeoutSecs < 15) {
+ LOGW("server specified too short a timeout "
+ "(%lu secs), using default.",
+ timeoutSecs);
+
+ mKeepAliveTimeoutUs = kDefaultKeepAliveTimeoutUs;
+ } else {
+ mKeepAliveTimeoutUs = timeoutSecs * 1000000ll;
+
+ LOGI("server specified timeout of %lu secs.",
+ timeoutSecs);
+ }
+ }
+
i = mSessionID.find(";");
if (i >= 0) {
// Remove options, i.e. ";timeout=90"
@@ -555,6 +589,9 @@ struct MyHandler : public AHandler {
if (index < mSessionDesc->countTracks()) {
setupTrack(index);
} else if (mSetupTracksSuccessful) {
+ ++mKeepAliveGeneration;
+ postKeepAlive();
+
AString request = "PLAY ";
request.append(mSessionURL);
request.append(" RTSP/1.0\r\n");
@@ -606,6 +643,51 @@ struct MyHandler : public AHandler {
break;
}
+ case 'aliv':
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mKeepAliveGeneration) {
+ // obsolete event.
+ break;
+ }
+
+ AString request;
+ request.append("OPTIONS ");
+ request.append(mSessionURL);
+ request.append(" RTSP/1.0\r\n");
+ request.append("Session: ");
+ request.append(mSessionID);
+ request.append("\r\n");
+ request.append("\r\n");
+
+ sp<AMessage> reply = new AMessage('opts', id());
+ reply->setInt32("generation", mKeepAliveGeneration);
+ mConn->sendRequest(request.c_str(), reply);
+ break;
+ }
+
+ case 'opts':
+ {
+ int32_t result;
+ CHECK(msg->findInt32("result", &result));
+
+ LOGI("OPTIONS completed with result %d (%s)",
+ result, strerror(-result));
+
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mKeepAliveGeneration) {
+ // obsolete event.
+ break;
+ }
+
+ postKeepAlive();
+ break;
+ }
+
case 'abor':
{
for (size_t i = 0; i < mTracks.size(); ++i) {
@@ -952,6 +1034,12 @@ struct MyHandler : public AHandler {
}
}
+ void postKeepAlive() {
+ sp<AMessage> msg = new AMessage('aliv', id());
+ msg->setInt32("generation", mKeepAliveGeneration);
+ msg->post((mKeepAliveTimeoutUs * 9) / 10);
+ }
+
void postAccessUnitTimeoutCheck() {
if (mCheckPending) {
return;
@@ -1120,6 +1208,8 @@ private:
bool mReceivedFirstRTCPPacket;
bool mReceivedFirstRTPPacket;
bool mSeekable;
+ int64_t mKeepAliveTimeoutUs;
+ int32_t mKeepAliveGeneration;
Vector<TrackInfo> mTracks;
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index aa40d58..522421b 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -34,6 +34,9 @@ static const size_t maxTotalSize = 64 * 1024;
static const char* cacheFileMagic = "EGL$";
static const size_t cacheFileHeaderSize = 8;
+// The time in seconds to wait before saving newly inserted cache entries.
+static const unsigned int deferredSaveDelay = 4;
+
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
@@ -128,6 +131,30 @@ void egl_cache_t::setBlob(const void* key, EGLsizei keySize, const void* value,
if (mInitialized) {
sp<BlobCache> bc = getBlobCacheLocked();
bc->set(key, keySize, value, valueSize);
+
+ if (!mSavePending) {
+ class DeferredSaveThread : public Thread {
+ public:
+ DeferredSaveThread() : Thread(false) {}
+
+ virtual bool threadLoop() {
+ sleep(deferredSaveDelay);
+ egl_cache_t* c = egl_cache_t::get();
+ Mutex::Autolock lock(c->mMutex);
+ if (c->mInitialized) {
+ c->saveBlobCacheLocked();
+ }
+ c->mSavePending = false;
+ return false;
+ }
+ };
+
+ // The thread will hold a strong ref to itself until it has finished
+ // running, so there's no need to keep a ref around.
+ sp<Thread> deferredSaveThread(new DeferredSaveThread());
+ mSavePending = true;
+ deferredSaveThread->run();
+ }
}
}
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 05d5873..4389623 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -108,6 +108,13 @@ private:
// from disk.
String8 mFilename;
+ // mSavePending indicates whether or not a deferred save operation is
+ // pending. Each time a key/value pair is inserted into the cache via
+ // setBlob, a deferred save is initiated if one is not already pending.
+ // This will wait some amount of time and then trigger a save of the cache
+ // contents to disk.
+ bool mSavePending;
+
// mMutex is the mutex used to prevent concurrent access to the member
// variables. It must be locked whenever the member variables are accessed.
mutable Mutex mMutex;
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 0891525..7a98615 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -133,8 +133,4 @@
<bool name="def_dtmf_tones_enabled">true</bool>
<!-- Default for UI touch sounds enabled -->
<bool name="def_sound_effects_enabled">true</bool>
-
- <!-- Default for Messaging app notifications enabled -->
- <bool name="def_messaging_app_notifications_on">true</bool>
-
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 44194f0..5495d08 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -1472,10 +1472,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
loadBooleanSetting(stmt, Settings.Secure.TOUCH_EXPLORATION_ENABLED,
R.bool.def_touch_exploration_enabled);
-
- loadBooleanSetting(stmt, Settings.Secure.MESSAGING_APP_NOTIFICATIONS,
- R.bool.def_messaging_app_notifications_on);
-
} finally {
if (stmt != null) stmt.close();
}
diff --git a/packages/SystemUI/res/drawable-hdpi/scrubber_control_disabled_holo.png b/packages/SystemUI/res/drawable-hdpi/scrubber_control_disabled_holo.png
deleted file mode 100644
index 55f6aa1..0000000
--- a/packages/SystemUI/res/drawable-hdpi/scrubber_control_disabled_holo.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/scrubber_control_holo.png b/packages/SystemUI/res/drawable-hdpi/scrubber_control_holo.png
deleted file mode 100644
index 19dae07..0000000
--- a/packages/SystemUI/res/drawable-hdpi/scrubber_control_holo.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/scrubber_track_holo_dark.9.png b/packages/SystemUI/res/drawable-hdpi/scrubber_track_holo_dark.9.png
deleted file mode 100644
index 8811df5..0000000
--- a/packages/SystemUI/res/drawable-hdpi/scrubber_track_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png b/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png
deleted file mode 100644
index 2b8768b..0000000
--- a/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png b/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png
deleted file mode 100644
index 0672564..0000000
--- a/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/scrubber_track_holo_dark.9.png b/packages/SystemUI/res/drawable-mdpi/scrubber_track_holo_dark.9.png
deleted file mode 100644
index 511d5c3..0000000
--- a/packages/SystemUI/res/drawable-mdpi/scrubber_track_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_normal.9.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_normal.9.png
index 60bd807..88137e8 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_normal.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_pressed.9.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_pressed.9.png
index 7fb5b20..6507a51 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_pressed.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/notify_panel_clock_bg_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_normal.9.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_normal.9.png
index 8354fef..798f589 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_normal.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_pressed.9.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_pressed.9.png
index f32980d..73247e5 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_pressed.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/notify_panel_clock_bg_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_normal.9.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_normal.9.png
index dbfce78..2b46c89 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_normal.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_pressed.9.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_pressed.9.png
index 37313e9..dd476b7 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_pressed.9.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/notify_panel_clock_bg_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/scrubber_control_disabled_holo.png b/packages/SystemUI/res/drawable-xhdpi/scrubber_control_disabled_holo.png
deleted file mode 100644
index 551c6dc..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/scrubber_control_disabled_holo.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/scrubber_control_holo.png b/packages/SystemUI/res/drawable-xhdpi/scrubber_control_holo.png
deleted file mode 100644
index 11f9c51..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/scrubber_control_holo.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/scrubber_track_holo_dark.9.png b/packages/SystemUI/res/drawable-xhdpi/scrubber_track_holo_dark.9.png
deleted file mode 100644
index b28dddf..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/scrubber_track_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java
index 0eb2be6..fe2ec69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java
@@ -80,10 +80,13 @@ public class ToggleSlider extends RelativeLayout
Drawable slider;
final Resources res = getContext().getResources();
if (checked) {
- thumb = res.getDrawable(R.drawable.scrubber_control_disabled_holo);
- slider = res.getDrawable(R.drawable.status_bar_settings_slider_disabled);
+ thumb = res.getDrawable(
+ com.android.internal.R.drawable.scrubber_control_disabled_holo);
+ slider = res.getDrawable(
+ R.drawable.status_bar_settings_slider_disabled);
} else {
- thumb = res.getDrawable(R.drawable.scrubber_control_holo);
+ thumb = res.getDrawable(
+ com.android.internal.R.drawable.scrubber_control_selector_holo);
slider = res.getDrawable(
com.android.internal.R.drawable.scrubber_progress_horizontal_holo_dark);
}
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
index dafbdcf..a7da96e 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
@@ -91,7 +91,7 @@ class KeyguardStatusViewManager implements OnClickListener {
private LockPatternUtils mLockPatternUtils;
private KeyguardUpdateMonitor mUpdateMonitor;
private Button mEmergencyCallButton;
- private boolean mUnlockDisabledDueToSimState;
+ private boolean mEmergencyButtonEnabledBecauseSimLocked;
// Shadowed text values
private CharSequence mCarrierText;
@@ -101,9 +101,10 @@ class KeyguardStatusViewManager implements OnClickListener {
private CharSequence mOwnerInfoText;
private boolean mShowingStatus;
private KeyguardScreenCallback mCallback;
- private final boolean mShowEmergencyButtonByDefault;
+ private final boolean mEmergencyCallButtonEnabledInScreen;
private CharSequence mPlmn;
private CharSequence mSpn;
+ protected int mPhoneState;
private class TransientTextManager {
private TextView mTextView;
@@ -154,9 +155,17 @@ class KeyguardStatusViewManager implements OnClickListener {
}
};
+ /**
+ *
+ * @param view the containing view of all widgets
+ * @param updateMonitor the update monitor to use
+ * @param lockPatternUtils lock pattern util object
+ * @param callback used to invoke emergency dialer
+ * @param emergencyButtonEnabledInScreen whether emergency button is enabled by default
+ */
public KeyguardStatusViewManager(View view, KeyguardUpdateMonitor updateMonitor,
LockPatternUtils lockPatternUtils, KeyguardScreenCallback callback,
- boolean showEmergencyButtonByDefault) {
+ boolean emergencyButtonEnabledInScreen) {
if (DEBUG) Log.v(TAG, "KeyguardStatusViewManager()");
mContainer = view;
mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);
@@ -171,7 +180,7 @@ class KeyguardStatusViewManager implements OnClickListener {
mOwnerInfoView = (TextView) findViewById(R.id.propertyOf);
mTransportView = (TransportControlView) findViewById(R.id.transport);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
- mShowEmergencyButtonByDefault = showEmergencyButtonByDefault;
+ mEmergencyCallButtonEnabledInScreen = emergencyButtonEnabledInScreen;
// Hide transport control view until we know we need to show it.
if (mTransportView != null) {
@@ -452,12 +461,12 @@ class KeyguardStatusViewManager implements OnClickListener {
*
* @param simState
*/
- private void updateCarrierTextWithSimStatus(State simState) {
+ private void updateCarrierStateWithSimStatus(State simState) {
if (DEBUG) Log.d(TAG, "updateCarrierTextWithSimStatus(), simState = " + simState);
CharSequence carrierText = null;
int carrierHelpTextId = 0;
- mUnlockDisabledDueToSimState = false;
+ mEmergencyButtonEnabledBecauseSimLocked = false;
mStatus = getStatusForIccState(simState);
mSimState = simState;
switch (mStatus) {
@@ -479,32 +488,35 @@ class KeyguardStatusViewManager implements OnClickListener {
case SimPermDisabled:
carrierText = getContext().getText(R.string.lockscreen_missing_sim_message_short);
carrierHelpTextId = R.string.lockscreen_permanent_disabled_sim_instructions;
- mUnlockDisabledDueToSimState = true;
+ mEmergencyButtonEnabledBecauseSimLocked = true;
break;
case SimMissingLocked:
carrierText = makeCarierString(mPlmn,
getContext().getText(R.string.lockscreen_missing_sim_message_short));
carrierHelpTextId = R.string.lockscreen_missing_sim_instructions;
- mUnlockDisabledDueToSimState = true;
+ mEmergencyButtonEnabledBecauseSimLocked = true;
break;
case SimLocked:
carrierText = makeCarierString(mPlmn,
getContext().getText(R.string.lockscreen_sim_locked_message));
+ mEmergencyButtonEnabledBecauseSimLocked = true;
break;
case SimPukLocked:
carrierText = makeCarierString(mPlmn,
getContext().getText(R.string.lockscreen_sim_puk_locked_message));
if (!mLockPatternUtils.isPukUnlockScreenEnable()) {
- mUnlockDisabledDueToSimState = true;
+ // This means we're showing the PUK unlock screen
+ mEmergencyButtonEnabledBecauseSimLocked = true;
}
break;
}
setCarrierText(carrierText);
setCarrierHelpText(carrierHelpTextId);
+ updateEmergencyCallButtonState(mPhoneState);
}
private View findViewById(int id) {
@@ -569,9 +581,12 @@ class KeyguardStatusViewManager implements OnClickListener {
private void updateEmergencyCallButtonState(int phoneState) {
if (mEmergencyCallButton != null) {
- boolean showIfCapable = mShowEmergencyButtonByDefault || mUnlockDisabledDueToSimState;
+ boolean enabledBecauseSimLocked =
+ mLockPatternUtils.isEmergencyCallEnabledWhileSimLocked()
+ && mEmergencyButtonEnabledBecauseSimLocked;
+ boolean shown = mEmergencyCallButtonEnabledInScreen || enabledBecauseSimLocked;
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton,
- phoneState, showIfCapable);
+ phoneState, shown);
}
}
@@ -594,7 +609,7 @@ class KeyguardStatusViewManager implements OnClickListener {
public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
mPlmn = plmn;
mSpn = spn;
- updateCarrierTextWithSimStatus(mSimState);
+ updateCarrierStateWithSimStatus(mSimState);
}
public void onRingerModeChanged(int state) {
@@ -602,6 +617,7 @@ class KeyguardStatusViewManager implements OnClickListener {
}
public void onPhoneStateChanged(int phoneState) {
+ mPhoneState = phoneState;
updateEmergencyCallButtonState(phoneState);
}
@@ -618,7 +634,7 @@ class KeyguardStatusViewManager implements OnClickListener {
private SimStateCallback mSimStateCallback = new SimStateCallback() {
public void onSimStateChanged(State simState) {
- updateCarrierTextWithSimStatus(simState);
+ updateCarrierStateWithSimStatus(simState);
}
};
diff --git a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
index 6acd1c5..47a7157 100644
--- a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
@@ -106,7 +106,7 @@ public class SimPukUnlockScreen extends LinearLayout implements KeyguardScreen,
mHeaderText.setSelected(true);
mKeyguardStatusViewManager = new KeyguardStatusViewManager(this, updateMonitor,
- lockpatternutils, callback, true);
+ lockpatternutils, callback, false);
mPinText.setFocusableInTouchMode(true);
mPinText.setOnFocusChangeListener(this);
diff --git a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
index 184748a..99e1ce1 100644
--- a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
@@ -100,7 +100,7 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie
mOkButton.setOnClickListener(this);
mKeyguardStatusViewManager = new KeyguardStatusViewManager(this, updateMonitor,
- lockpatternutils, callback, true);
+ lockpatternutils, callback, false);
setFocusableInTouchMode(true);
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 433af4a..cb6fcc6 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2066,9 +2066,14 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
// The first time a track is added we wait
// for all its buffers to be filled before processing it
mAudioMixer->setActiveTrack(track->name());
- // make sure that we have enough frames to mix one full buffer
+ // make sure that we have enough frames to mix one full buffer.
+ // enforce this condition only once to enable draining the buffer in case the client
+ // app does not call stop() and relies on underrun to stop:
+ // hence the test on (track->mRetryCount >= kMaxTrackRetries) meaning the track was mixed
+ // during last round
uint32_t minFrames = 1;
- if (!track->isStopped() && !track->isPausing()) {
+ if (!track->isStopped() && !track->isPausing() &&
+ (track->mRetryCount >= kMaxTrackRetries)) {
if (t->sampleRate() == (int)mSampleRate) {
minFrames = mFrameCount;
} else {
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index b042da6..af9152d 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -40,6 +40,8 @@ import android.provider.Settings;
import android.service.textservice.SpellCheckerService;
import android.text.TextUtils;
import android.util.Slog;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
import android.view.textservice.SpellCheckerInfo;
import android.view.textservice.SpellCheckerSubtype;
@@ -222,20 +224,40 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
if (hashCode == 0 && !allowImplicitlySelectedSubtype) {
return null;
}
- final String systemLocale =
- mContext.getResources().getConfiguration().locale.toString();
+ String candidateLocale = null;
+ if (hashCode == 0) {
+ // Spell checker language settings == "auto"
+ final InputMethodManager imm =
+ (InputMethodManager)mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ final InputMethodSubtype currentInputMethodSubtype =
+ imm.getCurrentInputMethodSubtype();
+ if (currentInputMethodSubtype != null) {
+ final String localeString = currentInputMethodSubtype.getLocale();
+ if (!TextUtils.isEmpty(localeString)) {
+ // 1. Use keyboard locale if available in the spell checker
+ candidateLocale = localeString;
+ }
+ }
+ }
+ if (candidateLocale == null) {
+ // 2. Use System locale if available in the spell checker
+ candidateLocale = mContext.getResources().getConfiguration().locale.toString();
+ }
+ }
SpellCheckerSubtype candidate = null;
for (int i = 0; i < sci.getSubtypeCount(); ++i) {
final SpellCheckerSubtype scs = sci.getSubtypeAt(i);
if (hashCode == 0) {
- if (systemLocale.equals(locale)) {
+ if (candidateLocale.equals(locale)) {
return scs;
} else if (candidate == null) {
final String scsLocale = scs.getLocale();
- if (systemLocale.length() >= 2
+ if (candidateLocale.length() >= 2
&& scsLocale.length() >= 2
- && systemLocale.substring(0, 2).equals(
+ && candidateLocale.substring(0, 2).equals(
scsLocale.substring(0, 2))) {
+ // Fall back to the applicable language
candidate = scs;
}
}
@@ -244,9 +266,13 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
Slog.w(TAG, "Return subtype " + scs.hashCode() + ", input= " + locale
+ ", " + scs.getLocale());
}
+ // 3. Use the user specified spell check language
return scs;
}
}
+ // 4. Fall back to the applicable language and return it if not null
+ // 5. Simply just return it even if it's null which means we could find no suitable
+ // spell check languages
return candidate;
}
}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index bfe6613..36442a0 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -63,6 +63,8 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import libcore.io.IoUtils;
+
/**
* Holds information about dynamic settings.
*/
@@ -998,8 +1000,8 @@ final class Settings {
FileUtils.sync(fstr);
str.close();
journal.commit();
- }
- catch (Exception e) {
+ } catch (Exception e) {
+ IoUtils.closeQuietly(str);
journal.rollback();
}
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index df7e0e1..a4f0a0c 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -675,7 +675,13 @@ public class InputManager implements Watchdog.Monitor {
} catch (NumberFormatException e) {
}
if (result < 1) {
- result = 55;
+ // This number equates to the refresh rate * 1.5. The rate should be at least
+ // equal to the screen refresh rate. We increase the rate by 50% to compensate for
+ // the discontinuity between the actual rate that events come in at (they do
+ // not necessarily come in constantly and are not handled synchronously).
+ // Ideally, we would use Display.getRefreshRate(), but as this does not necessarily
+ // return a sensible result, we use '60' as our default assumed refresh rate.
+ result = 90;
}
return result;
}
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index d82a7e2..7575ebd 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -166,7 +166,11 @@ status_t SensorDevice::initCheck() const {
ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
if (!mSensorDevice) return NO_INIT;
- return mSensorDevice->poll(mSensorDevice, buffer, count);
+ ssize_t c;
+ do {
+ c = mSensorDevice->poll(mSensorDevice, buffer, count);
+ } while (c == -EINTR);
+ return c;
}
status_t SensorDevice::activate(void* ident, int handle, int enabled)
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 7d5a761..f61a11a 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -286,7 +286,8 @@ bool SensorService::threadLoop()
}
} while (count >= 0 || Thread::exitPending());
- LOGW("Exiting SensorService::threadLoop!");
+ LOGW("Exiting SensorService::threadLoop => aborting...");
+ abort();
return false;
}
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index b916bd7..53502db 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -31,7 +31,7 @@ ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
endif
ifneq (,$(findstring $(TARGET_DEVICE),tuna toro maguro))
- LOCAL_CFLAGS += -DREFRESH_RATE=48
+ LOCAL_CFLAGS += -DREFRESH_RATE=59
endif
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 34f8848..f2ccb5b 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1513,14 +1513,65 @@ public class PhoneNumberUtils
static final int MIN_MATCH = 7;
/**
- * isEmergencyNumber: checks a given number against the list of
- * emergency numbers provided by the RIL and SIM card.
+ * Checks a given number against the list of
+ * emergency numbers provided by the RIL and SIM card.
*
* @param number the number to look up.
- * @return if the number is in the list of emergency numbers
- * listed in the ril / sim, then return true, otherwise false.
+ * @return true if the number is in the list of emergency numbers
+ * listed in the RIL / SIM, otherwise return false.
*/
public static boolean isEmergencyNumber(String number) {
+ // Return true only if the specified number *exactly* matches
+ // one of the emergency numbers listed by the RIL / SIM.
+ return isEmergencyNumberInternal(number, true /* useExactMatch */);
+ }
+
+ /**
+ * Checks if given number might *potentially* result in
+ * a call to an emergency service on the current network.
+ *
+ * Specifically, this method will return true if the specified number
+ * is an emergency number according to the list managed by the RIL or
+ * SIM, *or* if the specified number simply starts with the same
+ * digits as any of the emergency numbers listed in the RIL / SIM.
+ *
+ * This method is intended for internal use by the phone app when
+ * deciding whether to allow ACTION_CALL intents from 3rd party apps
+ * (where we're required to *not* allow emergency calls to be placed.)
+ *
+ * @param number the number to look up.
+ * @return true if the number is in the list of emergency numbers
+ * listed in the RIL / SIM, *or* if the number starts with the
+ * same digits as any of those emergency numbers.
+ *
+ * @hide
+ */
+ public static boolean isPotentialEmergencyNumber(String number) {
+ // Check against the emergency numbers listed by the RIL / SIM,
+ // and *don't* require an exact match.
+ return isEmergencyNumberInternal(number, false /* useExactMatch */);
+ }
+
+ /**
+ * Helper function for isEmergencyNumber(String) and
+ * isPotentialEmergencyNumber(String).
+ *
+ * @param number the number to look up.
+ *
+ * @param useExactMatch if true, consider a number to be an emergency
+ * number only if it *exactly* matches a number listed in
+ * the RIL / SIM. If false, a number is considered to be an
+ * emergency number if it simply starts with the same digits
+ * as any of the emergency numbers listed in the RIL / SIM.
+ * (Setting useExactMatch to false allows you to identify
+ * number that could *potentially* result in emergency calls
+ * since many networks will actually ignore trailing digits
+ * after a valid emergency number.)
+ *
+ * @return true if the number is in the list of emergency numbers
+ * listed in the RIL / sim, otherwise return false.
+ */
+ private static boolean isEmergencyNumberInternal(String number, boolean useExactMatch) {
// If the number passed in is null, just return false:
if (number == null) return false;
@@ -1540,16 +1591,26 @@ public class PhoneNumberUtils
// searches through the comma-separated list for a match,
// return true if one is found.
for (String emergencyNum : numbers.split(",")) {
- if (number.startsWith(emergencyNum)) {
- return true;
+ if (useExactMatch) {
+ if (number.equals(emergencyNum)) {
+ return true;
+ }
+ } else {
+ if (number.startsWith(emergencyNum)) {
+ return true;
+ }
}
}
// no matches found against the list!
return false;
}
- //no ecclist system property, so use our own list.
- return (number.startsWith("112") || number.startsWith("911"));
+ // No ecclist system property, so use our own list.
+ if (useExactMatch) {
+ return (number.equals("112") || number.equals("911"));
+ } else {
+ return (number.startsWith("112") || number.startsWith("911"));
+ }
}
/**
@@ -1559,31 +1620,81 @@ public class PhoneNumberUtils
* @param defaultCountryIso the specific country which the number should be checked against
* @return if the number is an emergency number for the specific country, then return true,
* otherwise false
+ *
* @hide
*/
public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
- PhoneNumberUtil util = PhoneNumberUtil.getInstance();
- try {
- PhoneNumber pn = util.parse(number, defaultCountryIso);
- // libphonenumber guarantees short numbers such as emergency numbers are classified as
- // invalid. Therefore, if the number passes the validation test, we believe it is not an
- // emergency number.
- // TODO: Compare against a list of country-specific known emergency numbers instead, once
- // that has been collected.
- if (util.isValidNumber(pn)) {
- return false;
- } else if ("BR".equalsIgnoreCase(defaultCountryIso) && number.length() >= 8) {
- // This is to prevent Brazilian local numbers which start with 911 being incorrectly
- // classified as emergency numbers. 911 is not an emergency number in Brazil; it is also
- // not possible to append additional digits to an emergency number to dial the number in
- // Brazil - it won't connect.
- // TODO: Clean this up once a list of country-specific known emergency numbers is
- // collected.
- return false;
- }
- } catch (NumberParseException e) {
- }
- return isEmergencyNumber(number);
+ return isEmergencyNumberInternal(number,
+ defaultCountryIso,
+ true /* useExactMatch */);
+ }
+
+ /**
+ * Checks if a given number might *potentially* result in a call to an
+ * emergency service, for a specific country.
+ *
+ * Specifically, this method will return true if the specified number
+ * is an emergency number in the specified country, *or* if the number
+ * simply starts with the same digits as any emergency number for that
+ * country.
+ *
+ * This method is intended for internal use by the phone app when
+ * deciding whether to allow ACTION_CALL intents from 3rd party apps
+ * (where we're required to *not* allow emergency calls to be placed.)
+ *
+ * @param number the number to look up.
+ * @param defaultCountryIso the specific country which the number should be checked against
+ * @return true if the number is an emergency number for the specific
+ * country, *or* if the number starts with the same digits as
+ * any of those emergency numbers.
+ *
+ * @hide
+ */
+ public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
+ return isEmergencyNumberInternal(number,
+ defaultCountryIso,
+ false /* useExactMatch */);
+ }
+
+ /**
+ * Helper function for isEmergencyNumber(String, String) and
+ * isPotentialEmergencyNumber(String, String).
+ *
+ * @param number the number to look up.
+ * @param defaultCountryIso the specific country which the number should be checked against
+ * @param useExactMatch if true, consider a number to be an emergency
+ * number only if it *exactly* matches a number listed in
+ * the RIL / SIM. If false, a number is considered to be an
+ * emergency number if it simply starts with the same digits
+ * as any of the emergency numbers listed in the RIL / SIM.
+ *
+ * @return true if the number is an emergency number for the specified country.
+ */
+ private static boolean isEmergencyNumberInternal(String number,
+ String defaultCountryIso,
+ boolean useExactMatch) {
+ PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+ try {
+ PhoneNumber pn = util.parse(number, defaultCountryIso);
+ // libphonenumber guarantees short numbers such as emergency numbers are classified as
+ // invalid. Therefore, if the number passes the validation test, we believe it is not an
+ // emergency number.
+ // TODO: Compare against a list of country-specific known emergency numbers instead, once
+ // that has been collected.
+ if (util.isValidNumber(pn)) {
+ return false;
+ } else if ("BR".equalsIgnoreCase(defaultCountryIso) && number.length() >= 8) {
+ // This is to prevent Brazilian local numbers which start with 911 being incorrectly
+ // classified as emergency numbers. 911 is not an emergency number in Brazil; it is also
+ // not possible to append additional digits to an emergency number to dial the number in
+ // Brazil - it won't connect.
+ // TODO: Clean this up once a list of country-specific known emergency numbers is
+ // collected.
+ return false;
+ }
+ } catch (NumberParseException e) {
+ }
+ return isEmergencyNumberInternal(number, useExactMatch);
}
/**
@@ -1592,12 +1703,66 @@ public class PhoneNumberUtils
*
* @param number the number to look up.
* @param context the specific context which the number should be checked against
- * @return if a phone number is an emergency number for a local country, based on the
- * CountryDetector.
+ * @return true if the specified number is an emergency number for a local country, based on the
+ * CountryDetector.
+ *
* @see android.location.CountryDetector
* @hide
*/
public static boolean isLocalEmergencyNumber(String number, Context context) {
+ return isLocalEmergencyNumberInternal(number,
+ context,
+ true /* useExactMatch */);
+ }
+
+ /**
+ * Checks if a given number might *potentially* result in a call to an
+ * emergency service, for the country that the user is in. The current
+ * country is determined using the CountryDetector.
+ *
+ * Specifically, this method will return true if the specified number
+ * is an emergency number in the current country, *or* if the number
+ * simply starts with the same digits as any emergency number for the
+ * current country.
+ *
+ * This method is intended for internal use by the phone app when
+ * deciding whether to allow ACTION_CALL intents from 3rd party apps
+ * (where we're required to *not* allow emergency calls to be placed.)
+ *
+ * @param number the number to look up.
+ * @param context the specific context which the number should be checked against
+ * @return true if the specified number is an emergency number for a local country, based on the
+ * CountryDetector.
+ *
+ * @see android.location.CountryDetector
+ * @hide
+ */
+ public static boolean isPotentialLocalEmergencyNumber(String number, Context context) {
+ return isLocalEmergencyNumberInternal(number,
+ context,
+ false /* useExactMatch */);
+ }
+
+ /**
+ * Helper function for isLocalEmergencyNumber() and
+ * isPotentialLocalEmergencyNumber().
+ *
+ * @param number the number to look up.
+ * @param context the specific context which the number should be checked against
+ * @param useExactMatch if true, consider a number to be an emergency
+ * number only if it *exactly* matches a number listed in
+ * the RIL / SIM. If false, a number is considered to be an
+ * emergency number if it simply starts with the same digits
+ * as any of the emergency numbers listed in the RIL / SIM.
+ *
+ * @return true if the specified number is an emergency number for a
+ * local country, based on the CountryDetector.
+ *
+ * @see android.location.CountryDetector
+ */
+ private static boolean isLocalEmergencyNumberInternal(String number,
+ Context context,
+ boolean useExactMatch) {
String countryIso;
CountryDetector detector = (CountryDetector) context.getSystemService(
Context.COUNTRY_DETECTOR);
@@ -1609,7 +1774,7 @@ public class PhoneNumberUtils
Log.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: "
+ countryIso);
}
- return isEmergencyNumber(number, countryIso);
+ return isEmergencyNumberInternal(number, countryIso, useExactMatch);
}
/**
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 410e961..6d9a2c2 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -213,7 +213,7 @@ public abstract class DataConnectionTracker extends Handler {
protected static final String NULL_IP = "0.0.0.0";
// Default for the data stall alarm
- protected static final int DATA_STALL_ALARM_DELAY_IN_MS_DEFAULT = 1000 * 60 * 3;
+ protected static final int DATA_STALL_ALARM_DELAY_IN_MS_DEFAULT = 1000 * 60 * 6;
// If attempt is less than this value we're doing first level recovery
protected static final int DATA_STALL_NO_RECV_POLL_LIMIT = 1;
// Tag for tracking stale alarms
diff --git a/telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java b/telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java
index 99f662d..e5a2d31 100644
--- a/telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java
+++ b/telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ import java.util.List;
* {@hide}
*/
class ComprehensionTlv {
+ private static final String LOG_TAG = "ComprehensionTlv";
private int mTag;
private boolean mCr;
private int mLength;
@@ -88,8 +89,13 @@ class ComprehensionTlv {
int endIndex = data.length;
while (startIndex < endIndex) {
ComprehensionTlv ctlv = ComprehensionTlv.decode(data, startIndex);
- items.add(ctlv);
- startIndex = ctlv.mValueIndex + ctlv.mLength;
+ if (ctlv != null) {
+ items.add(ctlv);
+ startIndex = ctlv.mValueIndex + ctlv.mLength;
+ } else {
+ CatLog.d(LOG_TAG, "decodeMany: ctlv is null, stop decoding");
+ break;
+ }
}
return items;
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index 1a3e487..a385f55 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -555,21 +555,51 @@ public class PhoneNumberUtilsTest extends AndroidTestCase {
}
@SmallTest
public void testIsEmergencyNumber() {
- assertTrue(PhoneNumberUtils.isEmergencyNumber("911", "US"));
- assertTrue(PhoneNumberUtils.isEmergencyNumber("112", "US"));
- // The next two numbers are not valid phone numbers in the US, but can be used to trick the
- // system to dial 911 and 112, which are emergency numbers in the US. For the purpose of
- // addressing that, they are also classified as emergency numbers in the US.
- assertTrue(PhoneNumberUtils.isEmergencyNumber("91112345", "US"));
- assertTrue(PhoneNumberUtils.isEmergencyNumber("11212345", "US"));
- // A valid mobile phone number from Singapore shouldn't be classified as an emergency number
- // in Singapore, as 911 is not an emergency number there.
- assertFalse(PhoneNumberUtils.isEmergencyNumber("91121234", "SG"));
- // A valid fixed-line phone number from Brazil shouldn't be classified as an emergency number
- // in Brazil, as 112 is not an emergency number there.
- assertFalse(PhoneNumberUtils.isEmergencyNumber("1121234567", "BR"));
- // A valid local phone number from Brazil shouldn't be classified as an emergency number in
- // Brazil.
- assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345", "BR"));
+ // There are two parallel sets of tests here: one for the
+ // regular isEmergencyNumber() method, and the other for
+ // isPotentialEmergencyNumber().
+ //
+ // (The difference is that isEmergencyNumber() will return true
+ // only if the specified number exactly matches an actual
+ // emergency number, but isPotentialEmergencyNumber() will
+ // return true if the specified number simply starts with the
+ // same digits as any actual emergency number.)
+
+ // Tests for isEmergencyNumber():
+ assertTrue(PhoneNumberUtils.isEmergencyNumber("911", "US"));
+ assertTrue(PhoneNumberUtils.isEmergencyNumber("112", "US"));
+ // The next two numbers are not valid phone numbers in the US,
+ // so do not count as emergency numbers (but they *are* "potential"
+ // emergency numbers; see below.)
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345", "US"));
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("11212345", "US"));
+ // A valid mobile phone number from Singapore shouldn't be classified as an emergency number
+ // in Singapore, as 911 is not an emergency number there.
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("91121234", "SG"));
+ // A valid fixed-line phone number from Brazil shouldn't be classified as an emergency number
+ // in Brazil, as 112 is not an emergency number there.
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("1121234567", "BR"));
+ // A valid local phone number from Brazil shouldn't be classified as an emergency number in
+ // Brazil.
+ assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345", "BR"));
+
+ // Tests for isPotentialEmergencyNumber():
+ // These first two are obviously emergency numbers:
+ assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("911", "US"));
+ assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("112", "US"));
+ // The next two numbers are not valid phone numbers in the US, but can be used to trick the
+ // system to dial 911 and 112, which are emergency numbers in the US. For the purpose of
+ // addressing that, they are also classified as "potential" emergency numbers in the US.
+ assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("91112345", "US"));
+ assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("11212345", "US"));
+ // A valid mobile phone number from Singapore shouldn't be classified as an emergency number
+ // in Singapore, as 911 is not an emergency number there.
+ assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91121234", "SG"));
+ // A valid fixed-line phone number from Brazil shouldn't be classified as an emergency number
+ // in Brazil, as 112 is not an emergency number there.
+ assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("1121234567", "BR"));
+ // A valid local phone number from Brazil shouldn't be classified as an emergency number in
+ // Brazil.
+ assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91112345", "BR"));
}
}
diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayCoercionTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayCoercionTest.java
new file mode 100644
index 0000000..5aba764
--- /dev/null
+++ b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayCoercionTest.java
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Part of the test suite for the WebView's Java Bridge. This class tests that
+ * we correctly convert JavaScript arrays to Java arrays when passing them to
+ * the methods of injected Java objects.
+ *
+ * The conversions should follow
+ * http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. Places in
+ * which the implementation differs from the spec are marked with
+ * LIVECONNECT_COMPLIANCE.
+ * FIXME: Consider making our implementation more compliant, if it will not
+ * break backwards-compatibility. See b/4408210.
+ *
+ * To run this test ...
+ * adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeArrayCoercionTest \
+ * com.android.webviewtests/android.test.InstrumentationTestRunner
+ */
+
+package com.android.webviewtests;
+
+public class JavaBridgeArrayCoercionTest extends JavaBridgeTestBase {
+ private class TestObject extends Controller {
+ private Object mObjectInstance;
+ private CustomType mCustomTypeInstance;
+
+ private boolean[] mBooleanArray;
+ private byte[] mByteArray;
+ private char[] mCharArray;
+ private short[] mShortArray;
+ private int[] mIntArray;
+ private long[] mLongArray;
+ private float[] mFloatArray;
+ private double[] mDoubleArray;
+ private String[] mStringArray;
+ private Object[] mObjectArray;
+ private CustomType[] mCustomTypeArray;
+
+ public TestObject() {
+ mObjectInstance = new Object();
+ mCustomTypeInstance = new CustomType();
+ }
+
+ public Object getObjectInstance() {
+ return mObjectInstance;
+ }
+ public CustomType getCustomTypeInstance() {
+ return mCustomTypeInstance;
+ }
+
+ public synchronized void setBooleanArray(boolean[] x) {
+ mBooleanArray = x;
+ notifyResultIsReady();
+ }
+ public synchronized void setByteArray(byte[] x) {
+ mByteArray = x;
+ notifyResultIsReady();
+ }
+ public synchronized void setCharArray(char[] x) {
+ mCharArray = x;
+ notifyResultIsReady();
+ }
+ public synchronized void setShortArray(short[] x) {
+ mShortArray = x;
+ notifyResultIsReady();
+ }
+ public synchronized void setIntArray(int[] x) {
+ mIntArray = x;
+ notifyResultIsReady();
+ }
+ public synchronized void setLongArray(long[] x) {
+ mLongArray = x;
+ notifyResultIsReady();
+ }
+ public synchronized void setFloatArray(float[] x) {
+ mFloatArray = x;
+ notifyResultIsReady();
+ }
+ public synchronized void setDoubleArray(double[] x) {
+ mDoubleArray = x;
+ notifyResultIsReady();
+ }
+ public synchronized void setStringArray(String[] x) {
+ mStringArray = x;
+ notifyResultIsReady();
+ }
+ public synchronized void setObjectArray(Object[] x) {
+ mObjectArray = x;
+ notifyResultIsReady();
+ }
+ public synchronized void setCustomTypeArray(CustomType[] x) {
+ mCustomTypeArray = x;
+ notifyResultIsReady();
+ }
+
+ public synchronized boolean[] waitForBooleanArray() {
+ waitForResult();
+ return mBooleanArray;
+ }
+ public synchronized byte[] waitForByteArray() {
+ waitForResult();
+ return mByteArray;
+ }
+ public synchronized char[] waitForCharArray() {
+ waitForResult();
+ return mCharArray;
+ }
+ public synchronized short[] waitForShortArray() {
+ waitForResult();
+ return mShortArray;
+ }
+ public synchronized int[] waitForIntArray() {
+ waitForResult();
+ return mIntArray;
+ }
+ public synchronized long[] waitForLongArray() {
+ waitForResult();
+ return mLongArray;
+ }
+ public synchronized float[] waitForFloatArray() {
+ waitForResult();
+ return mFloatArray;
+ }
+ public synchronized double[] waitForDoubleArray() {
+ waitForResult();
+ return mDoubleArray;
+ }
+ public synchronized String[] waitForStringArray() {
+ waitForResult();
+ return mStringArray;
+ }
+ public synchronized Object[] waitForObjectArray() {
+ waitForResult();
+ return mObjectArray;
+ }
+ public synchronized CustomType[] waitForCustomTypeArray() {
+ waitForResult();
+ return mCustomTypeArray;
+ }
+ }
+
+ // Two custom types used when testing passing objects.
+ private class CustomType {
+ }
+
+ private TestObject mTestObject;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTestObject = new TestObject();
+ setUpWebView(mTestObject, "testObject");
+ }
+
+ // Note that all tests use a single element array for simplicity. We test
+ // multiple elements elsewhere.
+
+ // Test passing an array of JavaScript numbers in the int32 range to a
+ // method which takes a Java array.
+ public void testPassNumberInt32() throws Throwable {
+ executeJavaScript("testObject.setBooleanArray([0]);");
+ assertFalse(mTestObject.waitForBooleanArray()[0]);
+ // LIVECONNECT_COMPLIANCE: Should convert to boolean.
+ executeJavaScript("testObject.setBooleanArray([42]);");
+ assertFalse(mTestObject.waitForBooleanArray()[0]);
+
+ executeJavaScript("testObject.setByteArray([42]);");
+ assertEquals(42, mTestObject.waitForByteArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should convert to numeric char value.
+ executeJavaScript("testObject.setCharArray([42]);");
+ assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+
+ executeJavaScript("testObject.setShortArray([42]);");
+ assertEquals(42, mTestObject.waitForShortArray()[0]);
+
+ executeJavaScript("testObject.setIntArray([42]);");
+ assertEquals(42, mTestObject.waitForIntArray()[0]);
+
+ executeJavaScript("testObject.setLongArray([42]);");
+ assertEquals(42L, mTestObject.waitForLongArray()[0]);
+
+ executeJavaScript("testObject.setFloatArray([42]);");
+ assertEquals(42.0f, mTestObject.waitForFloatArray()[0]);
+
+ executeJavaScript("testObject.setDoubleArray([42]);");
+ assertEquals(42.0, mTestObject.waitForDoubleArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should create array and create instances of java.lang.Number.
+ executeJavaScript("testObject.setObjectArray([42]);");
+ assertNull(mTestObject.waitForObjectArray());
+
+ // LIVECONNECT_COMPLIANCE: Should create instances of java.lang.String.
+ executeJavaScript("testObject.setStringArray([42]);");
+ assertNull(mTestObject.waitForStringArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setCustomTypeArray([42]);");
+ assertNull(mTestObject.waitForCustomTypeArray());
+ }
+
+ // Test passing an array of JavaScript numbers in the double range to a
+ // method which takes a Java array.
+ public void testPassNumberDouble() throws Throwable {
+ // LIVECONNECT_COMPLIANCE: Should convert to boolean.
+ executeJavaScript("testObject.setBooleanArray([42.1]);");
+ assertFalse(mTestObject.waitForBooleanArray()[0]);
+
+ executeJavaScript("testObject.setByteArray([42.1]);");
+ assertEquals(42, mTestObject.waitForByteArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should convert to numeric char value.
+ executeJavaScript("testObject.setCharArray([42.1]);");
+ assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+
+ executeJavaScript("testObject.setShortArray([42.1]);");
+ assertEquals(42, mTestObject.waitForShortArray()[0]);
+
+ executeJavaScript("testObject.setIntArray([42.1]);");
+ assertEquals(42, mTestObject.waitForIntArray()[0]);
+
+ executeJavaScript("testObject.setLongArray([42.1]);");
+ assertEquals(42L, mTestObject.waitForLongArray()[0]);
+
+ executeJavaScript("testObject.setFloatArray([42.1]);");
+ assertEquals(42.1f, mTestObject.waitForFloatArray()[0]);
+
+ executeJavaScript("testObject.setDoubleArray([42.1]);");
+ assertEquals(42.1, mTestObject.waitForDoubleArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should create array and create instances of java.lang.Number.
+ executeJavaScript("testObject.setObjectArray([42.1]);");
+ assertNull(mTestObject.waitForObjectArray());
+
+ // LIVECONNECT_COMPLIANCE: Should create instances of java.lang.String.
+ executeJavaScript("testObject.setStringArray([42.1]);");
+ assertNull(mTestObject.waitForStringArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setCustomTypeArray([42.1]);");
+ assertNull(mTestObject.waitForCustomTypeArray());
+ }
+
+ // Test passing an array of JavaScript NaN values to a method which takes a
+ // Java array.
+ public void testPassNumberNaN() throws Throwable {
+ executeJavaScript("testObject.setBooleanArray([Number.NaN]);");
+ assertFalse(mTestObject.waitForBooleanArray()[0]);
+
+ executeJavaScript("testObject.setByteArray([Number.NaN]);");
+ assertEquals(0, mTestObject.waitForByteArray()[0]);
+
+ executeJavaScript("testObject.setCharArray([Number.NaN]);");
+ assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+
+ executeJavaScript("testObject.setShortArray([Number.NaN]);");
+ assertEquals(0, mTestObject.waitForShortArray()[0]);
+
+ executeJavaScript("testObject.setIntArray([Number.NaN]);");
+ assertEquals(0, mTestObject.waitForIntArray()[0]);
+
+ executeJavaScript("testObject.setLongArray([Number.NaN]);");
+ assertEquals(0L, mTestObject.waitForLongArray()[0]);
+
+ executeJavaScript("testObject.setFloatArray([Number.NaN]);");
+ assertEquals(Float.NaN, mTestObject.waitForFloatArray()[0]);
+
+ executeJavaScript("testObject.setDoubleArray([Number.NaN]);");
+ assertEquals(Double.NaN, mTestObject.waitForDoubleArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should create array and create instances of java.lang.Number.
+ executeJavaScript("testObject.setObjectArray([Number.NaN]);");
+ assertNull(mTestObject.waitForObjectArray());
+
+ // LIVECONNECT_COMPLIANCE: Should create instances of java.lang.String.
+ executeJavaScript("testObject.setStringArray([Number.NaN]);");
+ assertNull(mTestObject.waitForStringArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setCustomTypeArray([Number.NaN]);");
+ assertNull(mTestObject.waitForCustomTypeArray());
+ }
+
+ // Test passing an array of JavaScript infinity values to a method which
+ // takes a Java array.
+ public void testPassNumberInfinity() throws Throwable {
+ executeJavaScript("testObject.setBooleanArray([Infinity]);");
+ assertFalse(mTestObject.waitForBooleanArray()[0]);
+
+ executeJavaScript("testObject.setByteArray([Infinity]);");
+ assertEquals(-1, mTestObject.waitForByteArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should convert to maximum numeric char value.
+ executeJavaScript("testObject.setCharArray([Infinity]);");
+ assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+
+ executeJavaScript("testObject.setShortArray([Infinity]);");
+ assertEquals(-1, mTestObject.waitForShortArray()[0]);
+
+ executeJavaScript("testObject.setIntArray([Infinity]);");
+ assertEquals(Integer.MAX_VALUE, mTestObject.waitForIntArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should be Long.MAX_VALUE.
+ executeJavaScript("testObject.setLongArray([Infinity]);");
+ assertEquals(-1L, mTestObject.waitForLongArray()[0]);
+
+ executeJavaScript("testObject.setFloatArray([Infinity]);");
+ assertEquals(Float.POSITIVE_INFINITY, mTestObject.waitForFloatArray()[0]);
+
+ executeJavaScript("testObject.setDoubleArray([Infinity]);");
+ assertEquals(Double.POSITIVE_INFINITY, mTestObject.waitForDoubleArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should create array and create instances of java.lang.Number.
+ executeJavaScript("testObject.setObjectArray([Infinity]);");
+ assertNull(mTestObject.waitForObjectArray());
+
+ // LIVECONNECT_COMPLIANCE: Should create instances of java.lang.String.
+ executeJavaScript("testObject.setStringArray([Infinity]);");
+ assertNull(mTestObject.waitForStringArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setCustomTypeArray([Infinity]);");
+ assertNull(mTestObject.waitForCustomTypeArray());
+ }
+
+ // Test passing an array of JavaScript boolean values to a method which
+ // takes a Java array.
+ public void testPassBoolean() throws Throwable {
+ executeJavaScript("testObject.setBooleanArray([true]);");
+ assertTrue(mTestObject.waitForBooleanArray()[0]);
+ executeJavaScript("testObject.setBooleanArray([false]);");
+ assertFalse(mTestObject.waitForBooleanArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should be 1.
+ executeJavaScript("testObject.setByteArray([true]);");
+ assertEquals(0, mTestObject.waitForByteArray()[0]);
+ executeJavaScript("testObject.setByteArray([false]);");
+ assertEquals(0, mTestObject.waitForByteArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should convert to numeric char value 1.
+ executeJavaScript("testObject.setCharArray([true]);");
+ assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+ executeJavaScript("testObject.setCharArray([false]);");
+ assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should be 1.
+ executeJavaScript("testObject.setShortArray([true]);");
+ assertEquals(0, mTestObject.waitForShortArray()[0]);
+ executeJavaScript("testObject.setShortArray([false]);");
+ assertEquals(0, mTestObject.waitForShortArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should be 1.
+ executeJavaScript("testObject.setIntArray([true]);");
+ assertEquals(0, mTestObject.waitForIntArray()[0]);
+ executeJavaScript("testObject.setIntArray([false]);");
+ assertEquals(0, mTestObject.waitForIntArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should be 1.
+ executeJavaScript("testObject.setLongArray([true]);");
+ assertEquals(0L, mTestObject.waitForLongArray()[0]);
+ executeJavaScript("testObject.setLongArray([false]);");
+ assertEquals(0L, mTestObject.waitForLongArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should be 1.0.
+ executeJavaScript("testObject.setFloatArray([true]);");
+ assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
+ executeJavaScript("testObject.setFloatArray([false]);");
+ assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should be 1.0.
+ executeJavaScript("testObject.setDoubleArray([true]);");
+ assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
+ executeJavaScript("testObject.setDoubleArray([false]);");
+ assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should create array and create instances of java.lang.Number.
+ executeJavaScript("testObject.setObjectArray([true]);");
+ assertNull(mTestObject.waitForObjectArray());
+
+ // LIVECONNECT_COMPLIANCE: Should create instances of java.lang.String.
+ executeJavaScript("testObject.setStringArray([true]);");
+ assertNull(mTestObject.waitForStringArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setCustomTypeArray([true]);");
+ assertNull(mTestObject.waitForCustomTypeArray());
+ }
+
+ // Test passing an array of JavaScript strings to a method which takes a
+ // Java array.
+ public void testPassString() throws Throwable {
+ // LIVECONNECT_COMPLIANCE: Non-empty string should convert to true.
+ executeJavaScript("testObject.setBooleanArray([\"+042.10\"]);");
+ assertFalse(mTestObject.waitForBooleanArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
+ executeJavaScript("testObject.setByteArray([\"+042.10\"]);");
+ assertEquals(0, mTestObject.waitForByteArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
+ executeJavaScript("testObject.setCharArray([\"+042.10\"]);");
+ assertEquals('+', mTestObject.waitForCharArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
+ executeJavaScript("testObject.setShortArray([\"+042.10\"]);");
+ assertEquals(0, mTestObject.waitForShortArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
+ executeJavaScript("testObject.setIntArray([\"+042.10\"]);");
+ assertEquals(0, mTestObject.waitForIntArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
+ executeJavaScript("testObject.setLongArray([\"+042.10\"]);");
+ assertEquals(0L, mTestObject.waitForLongArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
+ executeJavaScript("testObject.setFloatArray([\"+042.10\"]);");
+ assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
+ executeJavaScript("testObject.setDoubleArray([\"+042.10\"]);");
+ assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should create array and create instances of java.lang.Number.
+ executeJavaScript("testObject.setObjectArray([\"+042.10\"]);");
+ assertNull(mTestObject.waitForObjectArray());
+
+ executeJavaScript("testObject.setStringArray([\"+042.10\"]);");
+ assertEquals("+042.10", mTestObject.waitForStringArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setCustomTypeArray([\"+042.10\"]);");
+ assertNull(mTestObject.waitForCustomTypeArray());
+ }
+
+ // Test passing an array of JavaScript objects to a method which takes a
+ // Java array.
+ public void testPassJavaScriptObject() throws Throwable {
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setBooleanArray([{foo: 42}]);");
+ assertFalse(mTestObject.waitForBooleanArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setByteArray([{foo: 42}]);");
+ assertEquals(0, mTestObject.waitForByteArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setCharArray([{foo: 42}]);");
+ assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setShortArray([{foo: 42}]);");
+ assertEquals(0, mTestObject.waitForShortArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setIntArray([{foo: 42}]);");
+ assertEquals(0, mTestObject.waitForIntArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setLongArray([{foo: 42}]);");
+ assertEquals(0L, mTestObject.waitForLongArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setFloatArray([{foo: 42}]);");
+ assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setDoubleArray([{foo: 42}]);");
+ assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setObjectArray([{foo: 42}]);");
+ assertNull(mTestObject.waitForObjectArray());
+
+ // LIVECONNECT_COMPLIANCE: Should call toString() on object.
+ executeJavaScript("testObject.setStringArray([{foo: 42}]);");
+ assertNull(mTestObject.waitForStringArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setCustomTypeArray([{foo: 42}]);");
+ assertNull(mTestObject.waitForCustomTypeArray());
+ }
+
+ // Test passing an array of Java objects to a method which takes a Java
+ // array.
+ public void testPassJavaObject() throws Throwable {
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setBooleanArray([testObject.getObjectInstance()]);");
+ assertFalse(mTestObject.waitForBooleanArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setByteArray([testObject.getObjectInstance()]);");
+ assertEquals(0, mTestObject.waitForByteArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setCharArray([testObject.getObjectInstance()]);");
+ assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setShortArray([testObject.getObjectInstance()]);");
+ assertEquals(0, mTestObject.waitForShortArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setIntArray([testObject.getObjectInstance()]);");
+ assertEquals(0, mTestObject.waitForIntArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setLongArray([testObject.getObjectInstance()]);");
+ assertEquals(0L, mTestObject.waitForLongArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setFloatArray([testObject.getObjectInstance()]);");
+ assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
+ executeJavaScript("testObject.setDoubleArray([testObject.getObjectInstance()]);");
+ assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should create an array and pass Java object.
+ executeJavaScript("testObject.setObjectArray([testObject.getObjectInstance()]);");
+ assertNull(mTestObject.waitForObjectArray());
+
+ // LIVECONNECT_COMPLIANCE: Should call toString() on object.
+ executeJavaScript("testObject.setStringArray([testObject.getObjectInstance()]);");
+ assertNull(mTestObject.waitForStringArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should create array and pass Java object.
+ executeJavaScript("testObject.setCustomTypeArray([testObject.getObjectInstance()]);");
+ assertNull(mTestObject.waitForCustomTypeArray());
+ executeJavaScript("testObject.setCustomTypeArray([testObject.getCustomTypeInstance()]);");
+ assertNull(mTestObject.waitForCustomTypeArray());
+ }
+
+ // Test passing an array of JavaScript null values to a method which takes
+ // a Java array.
+ public void testPassNull() throws Throwable {
+ executeJavaScript("testObject.setByteArray([null]);");
+ assertEquals(0, mTestObject.waitForByteArray()[0]);
+
+ executeJavaScript("testObject.setCharArray([null]);");
+ assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
+
+ executeJavaScript("testObject.setShortArray([null]);");
+ assertEquals(0, mTestObject.waitForShortArray()[0]);
+
+ executeJavaScript("testObject.setIntArray([null]);");
+ assertEquals(0, mTestObject.waitForIntArray()[0]);
+
+ executeJavaScript("testObject.setLongArray([null]);");
+ assertEquals(0L, mTestObject.waitForLongArray()[0]);
+
+ executeJavaScript("testObject.setFloatArray([null]);");
+ assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
+
+ executeJavaScript("testObject.setDoubleArray([null]);");
+ assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
+
+ executeJavaScript("testObject.setBooleanArray([null]);");
+ assertFalse(mTestObject.waitForBooleanArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should create array and pass null.
+ executeJavaScript("testObject.setObjectArray([null]);");
+ assertNull(mTestObject.waitForObjectArray());
+
+ executeJavaScript("testObject.setStringArray([null]);");
+ assertNull(mTestObject.waitForStringArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should create array and pass null.
+ executeJavaScript("testObject.setCustomTypeArray([null]);");
+ assertNull(mTestObject.waitForCustomTypeArray());
+ }
+
+ // Test passing an array of JavaScript undefined values to a method which
+ // takes a Java array.
+ public void testPassUndefined() throws Throwable {
+ executeJavaScript("testObject.setByteArray([undefined]);");
+ assertEquals(0, mTestObject.waitForByteArray()[0]);
+
+ executeJavaScript("testObject.setCharArray([undefined]);");
+ assertEquals(0, mTestObject.waitForCharArray()[0]);
+
+ executeJavaScript("testObject.setShortArray([undefined]);");
+ assertEquals(0, mTestObject.waitForShortArray()[0]);
+
+ executeJavaScript("testObject.setIntArray([undefined]);");
+ assertEquals(0, mTestObject.waitForIntArray()[0]);
+
+ executeJavaScript("testObject.setLongArray([undefined]);");
+ assertEquals(0L, mTestObject.waitForLongArray()[0]);
+
+ executeJavaScript("testObject.setFloatArray([undefined]);");
+ assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
+
+ executeJavaScript("testObject.setDoubleArray([undefined]);");
+ assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
+
+ executeJavaScript("testObject.setBooleanArray([undefined]);");
+ assertEquals(false, mTestObject.waitForBooleanArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should create array and pass null.
+ executeJavaScript("testObject.setObjectArray([undefined]);");
+ assertNull(mTestObject.waitForObjectArray());
+
+ executeJavaScript("testObject.setStringArray([undefined]);");
+ assertNull(mTestObject.waitForStringArray()[0]);
+
+ // LIVECONNECT_COMPLIANCE: Should create array and pass null.
+ executeJavaScript("testObject.setCustomTypeArray([undefined]);");
+ assertNull(mTestObject.waitForCustomTypeArray());
+ }
+}
diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayTest.java
new file mode 100644
index 0000000..51dd80d
--- /dev/null
+++ b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Part of the test suite for the WebView's Java Bridge. This class tests the
+ * general use of arrays.
+ *
+ * The conversions should follow
+ * http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. Places in
+ * which the implementation differs from the spec are marked with
+ * LIVECONNECT_COMPLIANCE.
+ * FIXME: Consider making our implementation more compliant, if it will not
+ * break backwards-compatibility. See b/4408210.
+ *
+ * To run this test ...
+ * adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeArrayTest \
+ * com.android.webviewtests/android.test.InstrumentationTestRunner
+ */
+
+package com.android.webviewtests;
+
+public class JavaBridgeArrayTest extends JavaBridgeTestBase {
+ private class TestObject extends Controller {
+ private boolean mBooleanValue;
+ private int mIntValue;
+ private String mStringValue;
+
+ private int[] mIntArray;
+
+ public synchronized void setBooleanValue(boolean x) {
+ mBooleanValue = x;
+ notifyResultIsReady();
+ }
+ public synchronized void setIntValue(int x) {
+ mIntValue = x;
+ notifyResultIsReady();
+ }
+ public synchronized void setStringValue(String x) {
+ mStringValue = x;
+ notifyResultIsReady();
+ }
+
+ public synchronized boolean waitForBooleanValue() {
+ waitForResult();
+ return mBooleanValue;
+ }
+ public synchronized int waitForIntValue() {
+ waitForResult();
+ return mIntValue;
+ }
+ public synchronized String waitForStringValue() {
+ waitForResult();
+ return mStringValue;
+ }
+
+ public synchronized void setIntArray(int[] x) {
+ mIntArray = x;
+ notifyResultIsReady();
+ }
+
+ public synchronized int[] waitForIntArray() {
+ waitForResult();
+ return mIntArray;
+ }
+
+ public int[] getIntArray() {
+ return new int[] {42, 43, 44};
+ }
+ public int[] getEmptyIntArray() {
+ return new int[] {};
+ }
+ }
+
+ private TestObject mTestObject;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTestObject = new TestObject();
+ setUpWebView(mTestObject, "testObject");
+ }
+
+ public void testArrayLength() throws Throwable {
+ executeJavaScript("testObject.setIntArray([42, 43, 44]);");
+ int[] result = mTestObject.waitForIntArray();
+ assertEquals(3, result.length);
+ assertEquals(42, result[0]);
+ assertEquals(43, result[1]);
+ assertEquals(44, result[2]);
+ }
+
+ public void testPassNull() throws Throwable {
+ executeJavaScript("testObject.setIntArray(null);");
+ assertNull(mTestObject.waitForIntArray());
+ }
+
+ public void testPassUndefined() throws Throwable {
+ executeJavaScript("testObject.setIntArray(undefined);");
+ assertNull(mTestObject.waitForIntArray());
+ }
+
+ public void testPassEmptyArray() throws Throwable {
+ executeJavaScript("testObject.setIntArray([]);");
+ assertEquals(0, mTestObject.waitForIntArray().length);
+ }
+
+ // Note that this requires being able to pass a string from JavaScript to
+ // Java.
+ public void testPassArrayToStringMethod() throws Throwable {
+ // LIVECONNECT_COMPLIANCE: Should call toString() on array.
+ executeJavaScript("testObject.setStringValue([42, 42, 42]);");
+ assertEquals("undefined", mTestObject.waitForStringValue());
+ }
+
+ // Note that this requires being able to pass an integer from JavaScript to
+ // Java.
+ public void testPassArrayToNonStringNonArrayMethod() throws Throwable {
+ // LIVECONNECT_COMPLIANCE: Should raise JavaScript exception.
+ executeJavaScript("testObject.setIntValue([42, 42, 42]);");
+ assertEquals(0, mTestObject.waitForIntValue());
+ }
+
+ public void testPassNonArrayToArrayMethod() throws Throwable {
+ // LIVECONNECT_COMPLIANCE: Should raise JavaScript exception.
+ executeJavaScript("testObject.setIntArray(42);");
+ assertNull(mTestObject.waitForIntArray());
+ }
+
+ public void testObjectWithLengthProperty() throws Throwable {
+ executeJavaScript("testObject.setIntArray({length: 3, 1: 42});");
+ int[] result = mTestObject.waitForIntArray();
+ assertEquals(3, result.length);
+ assertEquals(0, result[0]);
+ assertEquals(42, result[1]);
+ assertEquals(0, result[2]);
+ }
+
+ public void testSparseArray() throws Throwable {
+ executeJavaScript("var x = [42, 43]; x[3] = 45; testObject.setIntArray(x);");
+ int[] result = mTestObject.waitForIntArray();
+ assertEquals(4, result.length);
+ assertEquals(42, result[0]);
+ assertEquals(43, result[1]);
+ assertEquals(0, result[2]);
+ assertEquals(45, result[3]);
+ }
+
+ // Note that this requires being able to pass a boolean from JavaScript to
+ // Java.
+ public void testReturnArray() throws Throwable {
+ // LIVECONNECT_COMPLIANCE: Convert to JavaScript array.
+ executeJavaScript("testObject.setBooleanValue(undefined === testObject.getIntArray())");
+ assertTrue(mTestObject.waitForBooleanValue());
+ }
+
+ // Note that this requires being able to pass a boolean from JavaScript to
+ // Java.
+ public void testReturnEmptyArray() throws Throwable {
+ // LIVECONNECT_COMPLIANCE: Convert to JavaScript array.
+ executeJavaScript(
+ "testObject.setBooleanValue(undefined === testObject.getEmptyIntArray())");
+ assertTrue(mTestObject.waitForBooleanValue());
+ }
+}
diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeCoercionTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeCoercionTest.java
index 34b3432..9aed888 100644
--- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeCoercionTest.java
+++ b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeCoercionTest.java
@@ -197,7 +197,7 @@ public class JavaBridgeCoercionTest extends JavaBridgeTestBase {
assertEquals(42, mTestObject.waitForIntValue());
executeJavaScript("testObject.setLongValue(42);");
- assertEquals(42, mTestObject.waitForLongValue());
+ assertEquals(42L, mTestObject.waitForLongValue());
executeJavaScript("testObject.setFloatValue(42);");
assertEquals(42.0f, mTestObject.waitForFloatValue());
@@ -252,7 +252,7 @@ public class JavaBridgeCoercionTest extends JavaBridgeTestBase {
assertEquals(Integer.MAX_VALUE, mTestObject.waitForIntValue());
executeJavaScript("testObject.setLongValue(42.1);");
- assertEquals(42, mTestObject.waitForLongValue());
+ assertEquals(42L, mTestObject.waitForLongValue());
// LIVECONNECT_COMPLIANCE: Should be Long.MAX_VALUE.
executeJavaScript("testObject.setLongValue(" + Long.MAX_VALUE + " + 42.1);");
assertEquals(Long.MIN_VALUE, mTestObject.waitForLongValue());
@@ -296,7 +296,7 @@ public class JavaBridgeCoercionTest extends JavaBridgeTestBase {
assertEquals(0, mTestObject.waitForIntValue());
executeJavaScript("testObject.setLongValue(Number.NaN);");
- assertEquals(0, mTestObject.waitForLongValue());
+ assertEquals(0L, mTestObject.waitForLongValue());
executeJavaScript("testObject.setFloatValue(Number.NaN);");
assertEquals(Float.NaN, mTestObject.waitForFloatValue());
@@ -336,7 +336,7 @@ public class JavaBridgeCoercionTest extends JavaBridgeTestBase {
// LIVECONNECT_COMPLIANCE: Should be Long.MAX_VALUE.
executeJavaScript("testObject.setLongValue(Infinity);");
- assertEquals(-1, mTestObject.waitForLongValue());
+ assertEquals(-1L, mTestObject.waitForLongValue());
executeJavaScript("testObject.setFloatValue(Infinity);");
assertEquals(Float.POSITIVE_INFINITY, mTestObject.waitForFloatValue());
@@ -401,9 +401,9 @@ public class JavaBridgeCoercionTest extends JavaBridgeTestBase {
// LIVECONNECT_COMPLIANCE: Should be 1.
executeJavaScript("testObject.setLongValue(true);");
- assertEquals(0, mTestObject.waitForLongValue());
+ assertEquals(0L, mTestObject.waitForLongValue());
executeJavaScript("testObject.setLongValue(false);");
- assertEquals(0, mTestObject.waitForLongValue());
+ assertEquals(0L, mTestObject.waitForLongValue());
// LIVECONNECT_COMPLIANCE: Should be 1.0.
executeJavaScript("testObject.setFloatValue(true);");
@@ -449,7 +449,7 @@ public class JavaBridgeCoercionTest extends JavaBridgeTestBase {
// LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
executeJavaScript("testObject.setLongValue(\"+042.10\");");
- assertEquals(0, mTestObject.waitForLongValue());
+ assertEquals(0L, mTestObject.waitForLongValue());
// LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
executeJavaScript("testObject.setFloatValue(\"+042.10\");");
@@ -463,6 +463,7 @@ public class JavaBridgeCoercionTest extends JavaBridgeTestBase {
executeJavaScript("testObject.setCharValue(\"+042.10\");");
assertEquals('\u0000', mTestObject.waitForCharValue());
+ // LIVECONNECT_COMPLIANCE: Non-empty string should convert to true.
executeJavaScript("testObject.setBooleanValue(\"+042.10\");");
assertFalse(mTestObject.waitForBooleanValue());
@@ -505,7 +506,7 @@ public class JavaBridgeCoercionTest extends JavaBridgeTestBase {
// LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
executeJavaScript("testObject.setLongValue({foo: 42});");
- assertEquals(0, mTestObject.waitForLongValue());
+ assertEquals(0L, mTestObject.waitForLongValue());
// LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
executeJavaScript("testObject.setFloatValue({foo: 42});");
@@ -560,7 +561,7 @@ public class JavaBridgeCoercionTest extends JavaBridgeTestBase {
// LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
executeJavaScript("testObject.setLongValue(testObject.getObjectInstance());");
- assertEquals(0, mTestObject.waitForLongValue());
+ assertEquals(0L, mTestObject.waitForLongValue());
// LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
executeJavaScript("testObject.setFloatValue(testObject.getObjectInstance());");
@@ -599,7 +600,7 @@ public class JavaBridgeCoercionTest extends JavaBridgeTestBase {
assertEquals(0, mTestObject.waitForIntValue());
executeJavaScript("testObject.setLongValue(null);");
- assertEquals(0, mTestObject.waitForLongValue());
+ assertEquals(0L, mTestObject.waitForLongValue());
executeJavaScript("testObject.setFloatValue(null);");
assertEquals(0.0f, mTestObject.waitForFloatValue());
@@ -636,7 +637,7 @@ public class JavaBridgeCoercionTest extends JavaBridgeTestBase {
assertEquals(0, mTestObject.waitForIntValue());
executeJavaScript("testObject.setLongValue(undefined);");
- assertEquals(0, mTestObject.waitForLongValue());
+ assertEquals(0L, mTestObject.waitForLongValue());
executeJavaScript("testObject.setFloatValue(undefined);");
assertEquals(0.0f, mTestObject.waitForFloatValue());
diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeTestBase.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeTestBase.java
index a9ab3b7..1af3f63 100644
--- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeTestBase.java
+++ b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeTestBase.java
@@ -93,7 +93,7 @@ public class JavaBridgeTestBase extends ActivityInstrumentationTestCase2<WebView
webView.addJavascriptInterface(object, name);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(mWebViewClient);
- webView.loadData("<html><head></head><body></body></html>", "text/html", null);
+ webView.loadData("<!DOCTYPE html><title></title>", "text/html", null);
}
});
mWebViewClient.waitForOnPageFinished();