diff options
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 Binary files differnew file mode 100644 index 0000000..0db2683 --- /dev/null +++ b/data/sounds/effects/ogg/camera_focus.ogg 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, ×tamp, &bufferSize, + mHandle, &bitstream, ×tamp, &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 Binary files differdeleted file mode 100644 index 55f6aa1..0000000 --- a/packages/SystemUI/res/drawable-hdpi/scrubber_control_disabled_holo.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/scrubber_control_holo.png b/packages/SystemUI/res/drawable-hdpi/scrubber_control_holo.png Binary files differdeleted file mode 100644 index 19dae07..0000000 --- a/packages/SystemUI/res/drawable-hdpi/scrubber_control_holo.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 8811df5..0000000 --- a/packages/SystemUI/res/drawable-hdpi/scrubber_track_holo_dark.9.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png b/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png Binary files differdeleted file mode 100644 index 2b8768b..0000000 --- a/packages/SystemUI/res/drawable-mdpi/scrubber_control_disabled_holo.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png b/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png Binary files differdeleted file mode 100644 index 0672564..0000000 --- a/packages/SystemUI/res/drawable-mdpi/scrubber_control_holo.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 511d5c3..0000000 --- a/packages/SystemUI/res/drawable-mdpi/scrubber_track_holo_dark.9.png +++ /dev/null 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 diff --git a/packages/SystemUI/res/drawable-xhdpi/scrubber_control_disabled_holo.png b/packages/SystemUI/res/drawable-xhdpi/scrubber_control_disabled_holo.png Binary files differdeleted file mode 100644 index 551c6dc..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/scrubber_control_disabled_holo.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/scrubber_control_holo.png b/packages/SystemUI/res/drawable-xhdpi/scrubber_control_holo.png Binary files differdeleted file mode 100644 index 11f9c51..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/scrubber_control_holo.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index b28dddf..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/scrubber_track_holo_dark.9.png +++ /dev/null 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(); |