diff options
80 files changed, 1767 insertions, 524 deletions
diff --git a/api/current.txt b/api/current.txt index 5c830d4..f07e66b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8797,6 +8797,7 @@ package android.graphics { method public long getTimestamp(); method public void getTransformMatrix(float[]); method public void release(); + method public void setDefaultBufferSize(int, int); method public void setOnFrameAvailableListener(android.graphics.SurfaceTexture.OnFrameAvailableListener); method public void updateTexImage(); } @@ -15131,6 +15132,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 { @@ -15225,6 +15227,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(); @@ -24824,6 +24830,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/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 4f72289..7c03a2f 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -109,6 +109,10 @@ public class Am { runStartService(); } else if (op.equals("force-stop")) { runForceStop(); + } else if (op.equals("kill")) { + runKill(); + } else if (op.equals("kill-all")) { + runKillAll(); } else if (op.equals("instrument")) { runInstrument(); } else if (op.equals("broadcast")) { @@ -484,6 +488,14 @@ public class Am { mAm.forceStopPackage(nextArgRequired()); } + private void runKill() throws Exception { + mAm.killBackgroundProcesses(nextArgRequired()); + } + + private void runKillAll() throws Exception { + mAm.killAllBackgroundProcesses(); + } + private void sendBroadcast() throws Exception { Intent intent = makeIntent(); IntentReceiver receiver = new IntentReceiver(); @@ -1179,6 +1191,8 @@ public class Am { " [--R COUNT] [-S] <INTENT>\n" + " am startservice <INTENT>\n" + " am force-stop <PACKAGE>\n" + + " am kill <PACKAGE>\n" + + " am kill-all\n" + " am broadcast <INTENT>\n" + " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" + " [--no-window-animation] <COMPONENT>\n" + @@ -1202,6 +1216,12 @@ public class Am { "\n" + "am force-stop: force stop everything associated with <PACKAGE>.\n" + "\n" + + "am kill: Kill all processes associated with <PACKAGE>. Only kills.\n" + + " processes that are safe to kill -- that is, will not impact the user\n" + + " experience.\n" + + "\n" + + "am kill-all: Kill all background processes.\n" + + "\n" + "am broadcast: send a broadcast Intent.\n" + "\n" + "am instrument: start an Instrumentation. Typically this target <COMPONENT>\n" + diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index b4471f0..7994d7c 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1092,6 +1092,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeNoException(); return true; } + + case KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + killAllBackgroundProcesses(); + reply.writeNoException(); + return true; + } case FORCE_STOP_PACKAGE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); @@ -2906,7 +2913,7 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } - + public void killBackgroundProcesses(String packageName) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -2917,7 +2924,17 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } - + + public void killAllBackgroundProcesses() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + public void forceStopPackage(String packageName) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 00fe953..a4714ca 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3832,11 +3832,16 @@ public final class ActivityThread { * Initialize the default http proxy in this process for the reasons we set the time zone. */ IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); - IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); - try { - ProxyProperties proxyProperties = service.getProxy(); - Proxy.setHttpProxySystemProperty(proxyProperties); - } catch (RemoteException e) {} + if (b != null) { + // In pre-boot mode (doing initial launch to collect password), not + // all system is up. This includes the connectivity service, so don't + // crash if we can't get it. + IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); + try { + ProxyProperties proxyProperties = service.getProxy(); + Proxy.setHttpProxySystemProperty(proxyProperties); + } catch (RemoteException e) {} + } if (data.instrumentationName != null) { ContextImpl appContext = new ContextImpl(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 26813bf..5222d37 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -234,6 +234,7 @@ public interface IActivityManager extends IInterface { public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) throws RemoteException; public void killBackgroundProcesses(final String packageName) throws RemoteException; + public void killAllBackgroundProcesses() throws RemoteException; public void forceStopPackage(final String packageName) throws RemoteException; // Note: probably don't want to allow applications access to these. @@ -605,4 +606,5 @@ public interface IActivityManager extends IInterface { int GET_PROCESS_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+136; int SHOW_BOOT_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+137; int DISMISS_KEYGUARD_ON_NEXT_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+138; + int KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+139; } diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 5c6ef1a..3605652 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -165,6 +165,17 @@ public class NetworkStats implements Parcelable { dest.writeLongArray(operations); } + @Override + public NetworkStats clone() { + final NetworkStats clone = new NetworkStats(elapsedRealtime, size); + NetworkStats.Entry entry = null; + for (int i = 0; i < size; i++) { + entry = getValues(i, entry); + clone.addValues(entry); + } + return clone; + } + // @VisibleForTesting public NetworkStats addIfaceValues( String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) { @@ -455,7 +466,7 @@ public class NetworkStats implements Parcelable { public NetworkStats subtract(NetworkStats value) throws NonMonotonicException { final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime; if (deltaRealtime < 0) { - throw new IllegalArgumentException("found non-monotonic realtime"); + throw new NonMonotonicException(this, value); } // result will have our rows, and elapsed time between snapshots @@ -575,7 +586,8 @@ public class NetworkStats implements Parcelable { pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); for (int i = 0; i < size; i++) { pw.print(prefix); - pw.print(" iface="); pw.print(iface[i]); + pw.print(" ["); pw.print(i); pw.print("]"); + pw.print(" iface="); pw.print(iface[i]); pw.print(" uid="); pw.print(uid[i]); pw.print(" set="); pw.print(setToString(set[i])); pw.print(" tag="); pw.print(tagToString(tag[i])); @@ -638,6 +650,10 @@ public class NetworkStats implements Parcelable { public final int leftIndex; public final int rightIndex; + public NonMonotonicException(NetworkStats left, NetworkStats right) { + this(left, -1, right, -1); + } + public NonMonotonicException( NetworkStats left, int leftIndex, NetworkStats right, int rightIndex) { this.left = checkNotNull(left, "missing left"); 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/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/WebTextView.java b/core/java/android/webkit/WebTextView.java index 5ee1b8a..8aafc3d 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -16,14 +16,9 @@ package android.webkit; -import com.android.internal.widget.EditableInputConnection; - import android.content.Context; -import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.ColorFilter; import android.graphics.Paint; -import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; @@ -60,12 +55,12 @@ import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.TextView; +import junit.framework.Assert; + import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; -import junit.framework.Assert; - /** * WebTextView is a specialized version of EditText used by WebView * to overlay html textfields (and textareas) to use our standard @@ -926,18 +921,23 @@ import junit.framework.Assert; */ /* package */ void setRect(int x, int y, int width, int height) { LayoutParams lp = (LayoutParams) getLayoutParams(); + boolean needsUpdate = false; if (null == lp) { lp = new LayoutParams(width, height, x, y); } else { - lp.x = x; - lp.y = y; - lp.width = width; - lp.height = height; + if ((lp.x != x) || (lp.y != y) || (lp.width != width) + || (lp.height != height)) { + needsUpdate = true; + lp.x = x; + lp.y = y; + lp.width = width; + lp.height = height; + } } if (getParent() == null) { // Insert the view so that it's drawn first (at index 0) mWebView.addView(this, 0, lp); - } else { + } else if (needsUpdate) { setLayoutParams(lp); } // Set up a measure spec so a layout can always be recreated. diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 55f345f..c80994a 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; } /* @@ -3375,6 +3389,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 +3507,7 @@ public class WebView extends AbsoluteLayout */ void notifyFindDialogDismissed() { mFindCallback = null; + mCachedOverlappingActionModeHeight = -1; if (mWebViewCore == null) { return; } @@ -4341,6 +4357,7 @@ public class WebView extends AbsoluteLayout @Override protected void onConfigurationChanged(Configuration newConfig) { + mCachedOverlappingActionModeHeight = -1; if (mSelectingText && mOrientation != newConfig.orientation) { selectionDone(); } @@ -8716,27 +8733,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 @@ -8749,7 +8745,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 {" + 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/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 5ab99dc..1a1b8d0 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -741,9 +741,16 @@ public class NumberPicker extends LinearLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, mMinWidth, mMaxWidth); - final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, mMinHeight, mMaxHeight); + // Try greedily to fit the max width and height. + final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, mMaxWidth); + final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, mMaxHeight); super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec); + // Flag if we are measured with width or height less than the respective min. + final int desiredWidth = Math.max(mMinWidth, getMeasuredWidth()); + final int desiredHeight = Math.max(mMinHeight, getMeasuredHeight()); + final int widthSize = resolveSizeAndState(desiredWidth, newWidthMeasureSpec, 0); + final int heightSize = resolveSizeAndState(desiredHeight, newHeightMeasureSpec, 0); + setMeasuredDimension(widthSize, heightSize); } @Override @@ -1357,23 +1364,19 @@ public class NumberPicker extends LinearLayout { * Makes a measure spec that tries greedily to use the max value. * * @param measureSpec The measure spec. - * @param maxValue The max value for the size. + * @param maxSize The max value for the size. * @return A measure spec greedily imposing the max size. */ - private int makeMeasureSpec(int measureSpec, int minValue, int maxValue) { + private int makeMeasureSpec(int measureSpec, int maxSize) { final int size = MeasureSpec.getSize(measureSpec); - if (size < minValue) { - throw new IllegalArgumentException("Available space is less than min size: " - + size + " < " + minValue); - } final int mode = MeasureSpec.getMode(measureSpec); switch (mode) { case MeasureSpec.EXACTLY: return measureSpec; case MeasureSpec.AT_MOST: - return MeasureSpec.makeMeasureSpec(Math.min(size, maxValue), MeasureSpec.EXACTLY); + return MeasureSpec.makeMeasureSpec(Math.min(size, maxSize), MeasureSpec.EXACTLY); case MeasureSpec.UNSPECIFIED: - return MeasureSpec.makeMeasureSpec(maxValue, MeasureSpec.EXACTLY); + return MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.EXACTLY); default: throw new IllegalArgumentException("Unknown measure mode: " + mode); } diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index e929e7d..ebb2604 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -76,12 +76,14 @@ 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()) { @@ -104,12 +106,6 @@ public class SpellChecker implements SpellCheckerSessionListener { // Change SpellParsers' wordIterator locale mWordIterator = new WordIterator(locale); - // Stop all SpellParsers - final int length = mSpellParsers.length; - for (int i = 0; i < length; i++) { - mSpellParsers[i].finish(); - } - // Remove existing misspelled SuggestionSpans mTextView.removeMisspelledSpans((Editable) mTextView.getText()); @@ -177,7 +173,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 5833afd..bc8721a 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; @@ -8355,10 +8355,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(); + } } } @@ -8904,21 +8906,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; } @@ -8933,7 +8932,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; } @@ -10113,27 +10112,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 1718e74..2bd4fa0 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 2f40a5a..9b8be85 100644..100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -831,6 +831,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 an application to read and write to diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java index b37eb46..098464f 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java @@ -294,6 +294,22 @@ public class NetworkStatsTest extends TestCase { assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L); } + public void testClone() throws Exception { + final NetworkStats original = new NetworkStats(TEST_START, 5) + .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L) + .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L); + + // make clone and mutate original + final NetworkStats clone = original.clone(); + original.addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L); + + assertEquals(3, original.size()); + assertEquals(2, clone.size()); + + assertEquals(128L + 512L + 128L, original.getTotalBytes()); + assertEquals(128L + 512L, clone.getTotalBytes()); + } + private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { final NetworkStats.Entry entry = stats.getValues(index, null); 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/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java index f3b62ec..29fab11 100644 --- a/graphics/java/android/graphics/SurfaceTexture.java +++ b/graphics/java/android/graphics/SurfaceTexture.java @@ -130,10 +130,17 @@ public class SurfaceTexture { } /** - * Set the size of buffers returned by requestBuffers when a width and height - * of zero is requested. + * Set the default size of the image buffers. The image producer may override the buffer size, + * in which case the producer-set buffer size will be used, not the default size set by this + * method. Both video and camera based image producers do override the size. This method may + * be used to set the image size when producing images with {@link android.graphics.Canvas} (via + * {@link android.view.Surface#lockCanvas}), or OpenGL ES (via an EGLSurface). * - * @hide Pending approval by API council. + * The new default buffer size will take effect the next time the image producer requests a + * buffer to fill. For {@link android.graphics.Canvas} this will be the next time {@link + * android.view.Surface#lockCanvas} is called. For OpenGL ES, the EGLSurface should be + * destroyed (via eglDestroySurface), made not-current (via eglMakeCurrent), and then recreated + * (via eglCreateWindowSurface) to ensure that the new default size has taken effect. */ public void setDefaultBufferSize(int width, int height) { nativeSetDefaultBufferSize(width, height); 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/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index bf19040..640e9fa 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -381,7 +381,7 @@ void NuPlayer::Renderer::onDrainVideoQueue() { LOGV("rendering video at media time %.2f secs", mediaTimeUs / 1E6); } - entry->mNotifyConsumed->setInt32("render", true); + entry->mNotifyConsumed->setInt32("render", !tooLate); entry->mNotifyConsumed->post(); mVideoQueue.erase(mVideoQueue.begin()); entry = NULL; 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/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index a08932a..8e8e26c 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -256,6 +256,21 @@ typedef EGLuint64NV (EGLAPIENTRYP PFNEGLGETSYSTEMTIMEFREQUENCYNVPROC)(void); typedef EGLuint64NV (EGLAPIENTRYP PFNEGLGETSYSTEMTIMENVPROC)(void); #endif + +/* EGL_ANDROID_blob_cache + */ +#ifndef EGL_ANDROID_blob_cache +#define EGL_ANDROID_blob_cache 1 +typedef khronos_ssize_t EGLsizei; +typedef void (*EGLSetBlobFunc) (const void* key, EGLsizei keySize, const void* value, EGLsizei valueSize); +typedef EGLsizei (*EGLGetBlobFunc) (const void* key, EGLsizei keySize, void* value, EGLsizei valueSize); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI void EGLAPIENTRY eglSetBlobCacheFuncs(EGLDisplay dpy, EGLSetBlobFunc set, EGLGetBlobFunc get); +#endif /* EGL_EGLEXT_PROTOTYPES */ +typedef void (EGLAPIENTRYP PFNEGLSETBLOBCACHEFUNCSPROC) (EGLDisplay dpy, + EGLSetBlobFunc set, EGLGetBlobFunc get); +#endif + #ifdef __cplusplus } #endif diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index 1e64302..522421b 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -19,6 +19,24 @@ #include "egl_impl.h" #include "egldefs.h" +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +// Cache size limits. +static const size_t maxKeySize = 1024; +static const size_t maxValueSize = 4096; +static const size_t maxTotalSize = 64 * 1024; + +// Cache file header +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 { // ---------------------------------------------------------------------------- @@ -26,37 +44,37 @@ namespace android { #define BC_EXT_STR "EGL_ANDROID_blob_cache" // -// EGL_ANDROID_blob_cache types and functions +// Callback functions passed to EGL. // -typedef khronos_ssize_t EGLsizei; - -typedef void (*EGLSetBlobFunc) (const void* key, EGLsizei keySize, - const void* value, EGLsizei valueSize); - -typedef EGLsizei (*EGLGetBlobFunc) (const void* key, EGLsizei keySize, - void* value, EGLsizei valueSize); +static void setBlob(const void* key, EGLsizei keySize, const void* value, + EGLsizei valueSize) { + egl_cache_t::get()->setBlob(key, keySize, value, valueSize); +} -typedef void (EGLAPIENTRYP PFNEGLSETBLOBCACHEFUNCSPROC) (EGLDisplay dpy, - EGLSetBlobFunc set, EGLGetBlobFunc get); +static EGLsizei getBlob(const void* key, EGLsizei keySize, void* value, + EGLsizei valueSize) { + return egl_cache_t::get()->getBlob(key, keySize, value, valueSize); +} // // egl_cache_t definition // -static void setBlob(const void* key, EGLsizei keySize, const void* value, - EGLsizei valueSize) { +egl_cache_t::egl_cache_t() : + mInitialized(false), + mBlobCache(NULL) { } -static EGLsizei getBlob(const void* key, EGLsizei keySize, void* value, - EGLsizei valueSize) { - return 0; +egl_cache_t::~egl_cache_t() { } +egl_cache_t egl_cache_t::sCache; + egl_cache_t* egl_cache_t::get() { - static egl_cache_t theCache; - return &theCache; + return &sCache; } void egl_cache_t::initialize(egl_display_t *display) { + Mutex::Autolock lock(mMutex); for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) { egl_connection_t* const cnx = &gEGLImpl[i]; if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) { @@ -79,7 +97,8 @@ void egl_cache_t::initialize(egl_display_t *display) { continue; } - eglSetBlobCacheFuncs(display->disp[i].dpy, setBlob, getBlob); + eglSetBlobCacheFuncs(display->disp[i].dpy, android::setBlob, + android::getBlob); EGLint err = cnx->egl.eglGetError(); if (err != EGL_SUCCESS) { LOGE("eglSetBlobCacheFuncs resulted in an error: %#x", @@ -88,6 +107,234 @@ void egl_cache_t::initialize(egl_display_t *display) { } } } + mInitialized = true; +} + +void egl_cache_t::terminate() { + Mutex::Autolock lock(mMutex); + if (mBlobCache != NULL) { + saveBlobCacheLocked(); + mBlobCache = NULL; + } + mInitialized = false; +} + +void egl_cache_t::setBlob(const void* key, EGLsizei keySize, const void* value, + EGLsizei valueSize) { + Mutex::Autolock lock(mMutex); + + if (keySize < 0 || valueSize < 0) { + LOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed"); + return; + } + + 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(); + } + } +} + +EGLsizei egl_cache_t::getBlob(const void* key, EGLsizei keySize, void* value, + EGLsizei valueSize) { + Mutex::Autolock lock(mMutex); + + if (keySize < 0 || valueSize < 0) { + LOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed"); + return 0; + } + + if (mInitialized) { + sp<BlobCache> bc = getBlobCacheLocked(); + return bc->get(key, keySize, value, valueSize); + } + return 0; +} + +void egl_cache_t::setCacheFilename(const char* filename) { + Mutex::Autolock lock(mMutex); + mFilename = filename; +} + +sp<BlobCache> egl_cache_t::getBlobCacheLocked() { + if (mBlobCache == NULL) { + mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize); + loadBlobCacheLocked(); + } + return mBlobCache; +} + +static uint32_t crc32c(const uint8_t* buf, size_t len) { + const uint32_t polyBits = 0x82F63B78; + uint32_t r = 0; + for (size_t i = 0; i < len; i++) { + r ^= buf[i]; + for (int j = 0; j < 8; j++) { + if (r & 1) { + r = (r >> 1) ^ polyBits; + } else { + r >>= 1; + } + } + } + return r; +} + +void egl_cache_t::saveBlobCacheLocked() { + if (mFilename.length() > 0) { + size_t cacheSize = mBlobCache->getFlattenedSize(); + size_t headerSize = cacheFileHeaderSize; + const char* fname = mFilename.string(); + + // Try to create the file with no permissions so we can write it + // without anyone trying to read it. + int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0); + if (fd == -1) { + if (errno == EEXIST) { + // The file exists, delete it and try again. + if (unlink(fname) == -1) { + // No point in retrying if the unlink failed. + LOGE("error unlinking cache file %s: %s (%d)", fname, + strerror(errno), errno); + return; + } + // Retry now that we've unlinked the file. + fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0); + } + if (fd == -1) { + LOGE("error creating cache file %s: %s (%d)", fname, + strerror(errno), errno); + return; + } + } + + size_t fileSize = headerSize + cacheSize; + if (ftruncate(fd, fileSize) == -1) { + LOGE("error setting cache file size: %s (%d)", strerror(errno), + errno); + close(fd); + unlink(fname); + return; + } + + uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize, + PROT_WRITE, MAP_SHARED, fd, 0)); + if (buf == MAP_FAILED) { + LOGE("error mmaping cache file: %s (%d)", strerror(errno), + errno); + close(fd); + unlink(fname); + return; + } + + status_t err = mBlobCache->flatten(buf + headerSize, cacheSize, NULL, + 0); + if (err != OK) { + LOGE("error writing cache contents: %s (%d)", strerror(-err), + -err); + munmap(buf, fileSize); + close(fd); + unlink(fname); + return; + } + + // Write the file magic and CRC + memcpy(buf, cacheFileMagic, 4); + uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4); + *crc = crc32c(buf + headerSize, cacheSize); + + munmap(buf, fileSize); + fchmod(fd, S_IRUSR); + close(fd); + } +} + +void egl_cache_t::loadBlobCacheLocked() { + if (mFilename.length() > 0) { + size_t headerSize = cacheFileHeaderSize; + + int fd = open(mFilename.string(), O_RDONLY, 0); + if (fd == -1) { + if (errno != ENOENT) { + LOGE("error opening cache file %s: %s (%d)", mFilename.string(), + strerror(errno), errno); + } + return; + } + + struct stat statBuf; + if (fstat(fd, &statBuf) == -1) { + LOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno); + close(fd); + return; + } + + // Sanity check the size before trying to mmap it. + size_t fileSize = statBuf.st_size; + if (fileSize > maxTotalSize * 2) { + LOGE("cache file is too large: %#llx", statBuf.st_size); + close(fd); + return; + } + + uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize, + PROT_READ, MAP_PRIVATE, fd, 0)); + if (buf == MAP_FAILED) { + LOGE("error mmaping cache file: %s (%d)", strerror(errno), + errno); + close(fd); + return; + } + + // Check the file magic and CRC + size_t cacheSize = fileSize - headerSize; + if (memcmp(buf, cacheFileMagic, 4) != 0) { + LOGE("cache file has bad mojo"); + close(fd); + return; + } + uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4); + if (crc32c(buf + headerSize, cacheSize) != *crc) { + LOGE("cache file failed CRC check"); + close(fd); + return; + } + + status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize, NULL, 0); + if (err != OK) { + LOGE("error reading cache contents: %s (%d)", strerror(-err), + -err); + munmap(buf, fileSize); + close(fd); + return; + } + + munmap(buf, fileSize); + close(fd); + } } // ---------------------------------------------------------------------------- diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h index 1fcfacc..4389623 100644 --- a/opengl/libs/EGL/egl_cache.h +++ b/opengl/libs/EGL/egl_cache.h @@ -14,20 +14,117 @@ ** limitations under the License. */ +#ifndef ANDROID_EGL_CACHE_H +#define ANDROID_EGL_CACHE_H + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <utils/BlobCache.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- class egl_display_t; -class egl_cache_t { +class EGLAPI egl_cache_t { public: + // get returns a pointer to the singleton egl_cache_t object. This + // singleton object will never be destroyed. static egl_cache_t* get(); + // initialize puts the egl_cache_t into an initialized state, such that it + // is able to insert and retrieve entries from the cache. This should be + // called when EGL is initialized. When not in the initialized state the + // getBlob and setBlob methods will return without performing any cache + // operations. void initialize(egl_display_t* display); + + // terminate puts the egl_cache_t back into the uninitialized state. When + // in this state the getBlob and setBlob methods will return without + // performing any cache operations. + void terminate(); + + // setBlob attempts to insert a new key/value blob pair into the cache. + // This will be called by the hardware vendor's EGL implementation via the + // EGL_ANDROID_blob_cache extension. + void setBlob(const void* key, EGLsizei keySize, const void* value, + EGLsizei valueSize); + + // getBlob attempts to retrieve the value blob associated with a given key + // blob from cache. This will be called by the hardware vendor's EGL + // implementation via the EGL_ANDROID_blob_cache extension. + EGLsizei getBlob(const void* key, EGLsizei keySize, void* value, + EGLsizei valueSize); + + // setCacheFilename sets the name of the file that should be used to store + // cache contents from one program invocation to another. + void setCacheFilename(const char* filename); + +private: + // Creation and (the lack of) destruction is handled internally. + egl_cache_t(); + ~egl_cache_t(); + + // Copying is disallowed. + egl_cache_t(const egl_cache_t&); // not implemented + void operator=(const egl_cache_t&); // not implemented + + // getBlobCacheLocked returns the BlobCache object being used to store the + // key/value blob pairs. If the BlobCache object has not yet been created, + // this will do so, loading the serialized cache contents from disk if + // possible. + sp<BlobCache> getBlobCacheLocked(); + + // saveBlobCache attempts to save the current contents of mBlobCache to + // disk. + void saveBlobCacheLocked(); + + // loadBlobCache attempts to load the saved cache contents from disk into + // mBlobCache. + void loadBlobCacheLocked(); + + // mInitialized indicates whether the egl_cache_t is in the initialized + // state. It is initialized to false at construction time, and gets set to + // true when initialize is called. It is set back to false when terminate + // is called. When in this state, the cache behaves as normal. When not, + // the getBlob and setBlob methods will return without performing any cache + // operations. + bool mInitialized; + + // mBlobCache is the cache in which the key/value blob pairs are stored. It + // is initially NULL, and will be initialized by getBlobCacheLocked the + // first time it's needed. + sp<BlobCache> mBlobCache; + + // mFilename is the name of the file for storing cache contents in between + // program invocations. It is initialized to an empty string at + // construction time, and can be set with the setCacheFilename method. An + // empty string indicates that the cache should not be saved to or restored + // 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; + + // sCache is the singleton egl_cache_t object. + static egl_cache_t sCache; }; // ---------------------------------------------------------------------------- }; // namespace android // ---------------------------------------------------------------------------- + +#endif // ANDROID_EGL_CACHE_H diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 0f92864..558ca77 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -44,6 +44,7 @@ egl_display_t::egl_display_t() : egl_display_t::~egl_display_t() { magic = 0; + egl_cache_t::get()->terminate(); } egl_display_t* egl_display_t::get(EGLDisplay dpy) { diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h index 113595f..1c1092c 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -59,7 +59,7 @@ struct egl_config_t { // ---------------------------------------------------------------------------- -class egl_display_t { +class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes static egl_display_t sDisplay[NUM_DISPLAYS]; EGLDisplay getDisplay(EGLNativeDisplayType display); @@ -141,4 +141,3 @@ EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface); // ---------------------------------------------------------------------------- #endif // ANDROID_EGL_DISPLAY_H - diff --git a/opengl/tests/EGLTest/Android.mk b/opengl/tests/EGLTest/Android.mk index 92d7eb1..14104d1 100644 --- a/opengl/tests/EGLTest/Android.mk +++ b/opengl/tests/EGLTest/Android.mk @@ -7,6 +7,7 @@ LOCAL_MODULE := EGL_test LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ + egl_cache_test.cpp \ EGL_test.cpp \ LOCAL_SHARED_LIBRARIES := \ @@ -21,9 +22,12 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_C_INCLUDES := \ bionic \ + bionic/libc/private \ bionic/libstdc++/include \ external/gtest/include \ external/stlport/stlport \ + frameworks/base/opengl/libs \ + frameworks/base/opengl/libs/EGL \ include $(BUILD_EXECUTABLE) diff --git a/opengl/tests/EGLTest/egl_cache_test.cpp b/opengl/tests/EGLTest/egl_cache_test.cpp new file mode 100644 index 0000000..c7d9e3e --- /dev/null +++ b/opengl/tests/EGLTest/egl_cache_test.cpp @@ -0,0 +1,110 @@ +/* + * 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. + */ + +#define LOG_TAG "EGL_test" +//#define LOG_NDEBUG 0 + +#include <gtest/gtest.h> + +#include <utils/Log.h> + +#include "egl_cache.h" +#include "egl_display.h" + +namespace android { + +class EGLCacheTest : public ::testing::Test { +protected: + virtual void SetUp() { + mCache = egl_cache_t::get(); + } + + virtual void TearDown() { + mCache->setCacheFilename(""); + mCache->terminate(); + } + + egl_cache_t* mCache; +}; + +TEST_F(EGLCacheTest, UninitializedCacheAlwaysMisses) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mCache->setBlob("abcd", 4, "efgh", 4); + ASSERT_EQ(0, mCache->getBlob("abcd", 4, buf, 4)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ(0xee, buf[1]); + ASSERT_EQ(0xee, buf[2]); + ASSERT_EQ(0xee, buf[3]); +} + +TEST_F(EGLCacheTest, InitializedCacheAlwaysHits) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mCache->initialize(egl_display_t::get(EGL_DEFAULT_DISPLAY)); + mCache->setBlob("abcd", 4, "efgh", 4); + ASSERT_EQ(4, mCache->getBlob("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +TEST_F(EGLCacheTest, TerminatedCacheAlwaysMisses) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mCache->initialize(egl_display_t::get(EGL_DEFAULT_DISPLAY)); + mCache->setBlob("abcd", 4, "efgh", 4); + mCache->terminate(); + ASSERT_EQ(0, mCache->getBlob("abcd", 4, buf, 4)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ(0xee, buf[1]); + ASSERT_EQ(0xee, buf[2]); + ASSERT_EQ(0xee, buf[3]); +} + +class EGLCacheSerializationTest : public EGLCacheTest { + +protected: + + virtual void SetUp() { + EGLCacheTest::SetUp(); + + char* tn = tempnam("/sdcard", "EGL_test-cache-"); + mFilename = tn; + free(tn); + } + + virtual void TearDown() { + unlink(mFilename.string()); + EGLCacheTest::TearDown(); + } + + String8 mFilename; +}; + +TEST_F(EGLCacheSerializationTest, ReinitializedCacheContainsValues) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mCache->setCacheFilename(mFilename); + mCache->initialize(egl_display_t::get(EGL_DEFAULT_DISPLAY)); + mCache->setBlob("abcd", 4, "efgh", 4); + mCache->terminate(); + mCache->initialize(egl_display_t::get(EGL_DEFAULT_DISPLAY)); + ASSERT_EQ(4, mCache->getBlob("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +} 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/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml index 93ec481..089a54d 100644 --- a/packages/SystemUI/res/values/donottranslate.xml +++ b/packages/SystemUI/res/values/donottranslate.xml @@ -18,8 +18,9 @@ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- For formatting day of week and date in DateView. %1$s is DOW, %2$s is date. - In Roman locales we now show only the date, but DOW is available for other locales if - necessary. --> - <string name="status_bar_date_formatter">%2$s</string> + We show both (DOW on one line, then the date) but this can be overridden for locales as + necessary. + --> + <string name="status_bar_date_formatter">%1$s\n%2$s</string> </resources> 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/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java index 81e1901..0f21bdb 100644 --- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -360,8 +360,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler mHasOverlay = true; // Continue showing FaceLock area until dialer comes up or call is resumed - if (mLockPatternUtils.usingBiometricWeak() && - mLockPatternUtils.isBiometricWeakInstalled() && mFaceLockServiceRunning) { + if (usingFaceLock() && mFaceLockServiceRunning) { showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_EMERGENCY_DIALER_TIMEOUT); } @@ -582,8 +581,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler bindToFaceLock(); // Show FaceLock area, but only for a little bit so lockpattern will become visible if // FaceLock fails to start or crashes - if (mLockPatternUtils.usingBiometricWeak() && - mLockPatternUtils.isBiometricWeakInstalled()) { + if (usingFaceLock()) { showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_SERVICE_TIMEOUT); } } else { @@ -653,11 +651,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler ((KeyguardScreen) mUnlockScreen).onResume(); } - if (mLockPatternUtils.usingBiometricWeak() && - mLockPatternUtils.isBiometricWeakInstalled() && !mHasOverlay) { + if (usingFaceLock() && !mHasOverlay) { // Note that show() gets called before the screen turns off to set it up for next time // it is turned on. We don't want to set a timeout on the FaceLock area here because it - // may be gone by the time the screen is turned on again. We set the timout when the + // may be gone by the time the screen is turned on again. We set the timeout when the // screen turns on instead. showFaceLockArea(); } else { @@ -854,7 +851,9 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler if (mode == Mode.UnlockScreen) { final UnlockMode unlockMode = getUnlockMode(); if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) { + boolean restartFaceLock = stopFaceLockIfRunning(); recreateUnlockScreen(unlockMode); + if (restartFaceLock) activateFaceLockIfAble(); } } @@ -1147,28 +1146,33 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler // Everything below pertains to FaceLock - might want to separate this out - // Take care of FaceLock area when layout is created + // Indicates whether FaceLock is in use + private boolean usingFaceLock() { + return (mLockPatternUtils.usingBiometricWeak() && + mLockPatternUtils.isBiometricWeakInstalled()); + } + + // Takes care of FaceLock area when layout is created private void initializeFaceLockAreaView(View view) { - if (mLockPatternUtils.usingBiometricWeak() && - mLockPatternUtils.isBiometricWeakInstalled()) { + if (usingFaceLock()) { mFaceLockAreaView = view.findViewById(R.id.faceLockAreaView); if (mFaceLockAreaView == null) { Log.e(TAG, "Layout does not have faceLockAreaView and FaceLock is enabled"); - } else { - if (mBoundToFaceLockService) { - // If we are creating a layout when we are already bound to FaceLock, then we - // are undergoing an orientation change. Stop FaceLock and restart it in the - // new location. - if (DEBUG) Log.d(TAG, "Restarting FL - creating view while already bound"); - stopAndUnbindFromFaceLock(); - activateFaceLockIfAble(); - } } } else { mFaceLockAreaView = null; // Set to null if not using FaceLock } } + // Stops FaceLock if it is running and reports back whether it was running or not + private boolean stopFaceLockIfRunning() { + if (usingFaceLock() && mBoundToFaceLockService) { + stopAndUnbindFromFaceLock(); + return true; + } + return false; + } + // Handles covering or exposing FaceLock area on the client side when FaceLock starts or stops // This needs to be done in a handler because the call could be coming from a callback from the // FaceLock service that is in a thread that can't modify the UI @@ -1221,8 +1225,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler // Binds to FaceLock service. This call does not tell it to start, but it causes the service // to call the onServiceConnected callback, which then starts FaceLock. public void bindToFaceLock() { - if (mLockPatternUtils.usingBiometricWeak() && - mLockPatternUtils.isBiometricWeakInstalled()) { + if (usingFaceLock()) { if (!mBoundToFaceLockService) { if (DEBUG) Log.d(TAG, "before bind to FaceLock service"); mContext.bindService(new Intent(IFaceLockInterface.class.getName()), @@ -1238,8 +1241,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler // Tells FaceLock to stop and then unbinds from the FaceLock service public void stopAndUnbindFromFaceLock() { - if (mLockPatternUtils.usingBiometricWeak() && - mLockPatternUtils.isBiometricWeakInstalled()) { + if (usingFaceLock()) { stopFaceLock(); if (mBoundToFaceLockService) { @@ -1300,8 +1302,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler // Tells the FaceLock service to start displaying its UI and perform recognition public void startFaceLock(IBinder windowToken, int x, int y, int h, int w) { - if (mLockPatternUtils.usingBiometricWeak() && - mLockPatternUtils.isBiometricWeakInstalled()) { + if (usingFaceLock()) { synchronized (mFaceLockServiceRunningLock) { if (!mFaceLockServiceRunning) { if (DEBUG) Log.d(TAG, "Starting FaceLock"); @@ -1322,8 +1323,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler // Tells the FaceLock service to stop displaying its UI and stop recognition public void stopFaceLock() { - if (mLockPatternUtils.usingBiometricWeak() && - mLockPatternUtils.isBiometricWeakInstalled()) { + if (usingFaceLock()) { // Note that attempting to stop FaceLock when it's not running is not an issue. // FaceLock can return, which stops it and then we try to stop it when the // screen is turned off. That's why we check. diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 7d97246..aa1c81c 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -493,7 +493,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } if ((mCarDockEnablesAccelerometer && mDockMode == Intent.EXTRA_DOCK_STATE_CAR) || - (mDeskDockEnablesAccelerometer && mDockMode == Intent.EXTRA_DOCK_STATE_DESK)) { + (mDeskDockEnablesAccelerometer && (mDockMode == Intent.EXTRA_DOCK_STATE_DESK + || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK + || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) { // enable accelerometer if we are docked in a dock that enables accelerometer // orientation management, return true; @@ -3137,7 +3139,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // enable 180 degree rotation while docked. preferredRotation = mCarDockEnablesAccelerometer ? sensorRotation : mCarDockRotation; - } else if (mDockMode == Intent.EXTRA_DOCK_STATE_DESK + } else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK + || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK + || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK) && (mDeskDockEnablesAccelerometer || mDeskDockRotation >= 0)) { // Ignore sensor when in desk dock unless explicitly enabled. // This case can override the behavior of NOSENSOR, and can also 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 96e8eb9..ff262f1 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/DockObserver.java b/services/java/com/android/server/DockObserver.java index dea9007..64789d3 100644 --- a/services/java/com/android/server/DockObserver.java +++ b/services/java/com/android/server/DockObserver.java @@ -82,7 +82,9 @@ class DockObserver extends UEventObserver { // Don't force screen on when undocking from the desk dock. // The change in power state will do this anyway. // FIXME - we should be configurable. - if (mPreviousDockState != Intent.EXTRA_DOCK_STATE_DESK || + if ((mPreviousDockState != Intent.EXTRA_DOCK_STATE_DESK + && mPreviousDockState != Intent.EXTRA_DOCK_STATE_LE_DESK + && mPreviousDockState != Intent.EXTRA_DOCK_STATE_HE_DESK) || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { mPowerManager.userActivityWithForce(SystemClock.uptimeMillis(), false, true); diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 6887de3..da960ae 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -1136,12 +1136,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub final StringBuilder command = new StringBuilder(); command.append("bandwidth removeiquota ").append(iface); + mActiveQuotaIfaces.remove(iface); + mActiveAlertIfaces.remove(iface); + try { // TODO: support quota shared across interfaces mConnector.doCommand(command.toString()); - mActiveQuotaIfaces.remove(iface); - mActiveAlertIfaces.remove(iface); } catch (NativeDaemonConnectorException e) { + // TODO: include current iptables state throw new IllegalStateException("Error communicating to native daemon", e); } } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 1b0addf..6b23b33 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -100,10 +100,13 @@ public class PowerManagerService extends IPowerManager.Stub private static final int LONG_KEYLIGHT_DELAY = 6000; // t+6 sec private static final int LONG_DIM_TIME = 7000; // t+N-5 sec - // How long to wait to debounce light sensor changes. + // How long to wait to debounce light sensor changes in milliseconds private static final int LIGHT_SENSOR_DELAY = 2000; - // For debouncing the proximity sensor. + // light sensor events rate in microseconds + private static final int LIGHT_SENSOR_RATE = 1000000; + + // For debouncing the proximity sensor in milliseconds private static final int PROXIMITY_SENSOR_DELAY = 1000; // trigger proximity if distance is less than 5 cm @@ -3049,7 +3052,7 @@ public class PowerManagerService extends IPowerManager.Stub try { if (enable) { mSensorManager.registerListener(mLightListener, mLightSensor, - SensorManager.SENSOR_DELAY_NORMAL); + LIGHT_SENSOR_RATE); } else { mSensorManager.unregisterListener(mLightListener); mHandler.removeCallbacks(mAutoBrightnessTask); 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/WifiService.java b/services/java/com/android/server/WifiService.java index 01eade1..3c65255 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -619,12 +619,7 @@ public class WifiService extends IWifiManager.Stub { */ public WifiConfiguration getWifiApConfiguration() { enforceAccessPermission(); - if (mWifiStateMachineChannel != null) { - return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel); - } else { - Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); - return null; - } + return mWifiStateMachine.syncGetWifiApConfiguration(); } /** diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 4fe8119..05d42ad 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1220,6 +1220,8 @@ public final class ActivityManagerService extends ActivityManagerNative } Thread thread = new Thread() { @Override public void run() { + StringBuilder dropBuilder = new StringBuilder(1024); + StringBuilder logBuilder = new StringBuilder(1024); try { java.lang.Process proc = Runtime.getRuntime().exec(new String[] { "procrank", }); @@ -1233,16 +1235,29 @@ public final class ActivityManagerService extends ActivityManagerNative break; } if (line.length() > 0) { - Slog.i(TAG, line); + logBuilder.append(line); + logBuilder.append('\n'); } + dropBuilder.append(line); + dropBuilder.append('\n'); } converter.close(); } catch (IOException e) { } StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); - dumpApplicationMemoryUsage(null, pw, " ", new String[] { }, true); - Slog.i(TAG, sw.toString()); + StringWriter catSw = new StringWriter(); + PrintWriter catPw = new PrintWriter(catSw); + dumpApplicationMemoryUsage(null, pw, " ", new String[] { }, true, catPw); + String memUsage = sw.toString(); + dropBuilder.append('\n'); + dropBuilder.append(memUsage); + dropBuilder.append(catSw.toString()); + logBuilder.append(memUsage); + addErrorToDropBox("watchdog", null, "system_server", null, + null, "Low on memory -- no more background processes", + dropBuilder.toString(), null, null); + Slog.i(TAG, logBuilder.toString()); synchronized (ActivityManagerService.this) { long now = SystemClock.uptimeMillis(); if (mLastMemUsageReportTime < now) { @@ -1394,7 +1409,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false); + mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null); } } @@ -3192,7 +3207,49 @@ public final class ActivityManagerService extends ActivityManagerNative return; } killPackageProcessesLocked(packageName, pkgUid, - ProcessList.SERVICE_ADJ, false, true, true, false); + ProcessList.SERVICE_ADJ, false, true, true, false, "kill background"); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + public void killAllBackgroundProcesses() { + if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: killAllBackgroundProcesses() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + + long callingId = Binder.clearCallingIdentity(); + try { + synchronized(this) { + ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); + for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) { + final int NA = apps.size(); + for (int ia=0; ia<NA; ia++) { + ProcessRecord app = apps.valueAt(ia); + if (app.persistent) { + // we don't kill persistent processes + continue; + } + if (app.removed) { + procs.add(app); + } else if (app.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) { + app.removed = true; + procs.add(app); + } + } + } + + int N = procs.size(); + for (int i=0; i<N; i++) { + removeProcessLocked(procs.get(i), false, true, "kill all background"); + } } } finally { Binder.restoreCallingIdentity(callingId); @@ -3364,7 +3421,7 @@ public final class ActivityManagerService extends ActivityManagerNative private final boolean killPackageProcessesLocked(String packageName, int uid, int minOomAdj, boolean callerWillRestart, boolean allowRestart, boolean doit, - boolean evenPersistent) { + boolean evenPersistent, String reason) { ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); // Remove all processes this package may have touched: all with the @@ -3399,7 +3456,7 @@ public final class ActivityManagerService extends ActivityManagerNative int N = procs.size(); for (int i=0; i<N; i++) { - removeProcessLocked(procs.get(i), callerWillRestart, allowRestart); + removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason); } return N > 0; } @@ -3430,7 +3487,7 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean didSomething = killPackageProcessesLocked(name, uid, -100, - callerWillRestart, false, doit, evenPersistent); + callerWillRestart, false, doit, evenPersistent, "force stop"); TaskRecord lastTask = null; for (i=0; i<mMainStack.mHistory.size(); i++) { @@ -3518,11 +3575,11 @@ public final class ActivityManagerService extends ActivityManagerNative } private final boolean removeProcessLocked(ProcessRecord app, - boolean callerWillRestart, boolean allowRestart) { + boolean callerWillRestart, boolean allowRestart, String reason) { final String name = app.processName; final int uid = app.info.uid; if (DEBUG_PROCESSES) Slog.d( - TAG, "Force removing process " + app + " (" + name + TAG, "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")"); mProcessNames.remove(name, uid); @@ -3537,9 +3594,10 @@ public final class ActivityManagerService extends ActivityManagerNative mPidsSelfLocked.remove(pid); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } + Slog.i(TAG, "Killing proc " + app.toShortString() + ": " + reason); handleAppDiedLocked(app, true, allowRestart); mLruProcesses.remove(app); - Process.killProcess(pid); + Process.killProcessQuiet(pid); if (app.persistent) { if (!callerWillRestart) { @@ -6846,7 +6904,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=procsToKill.size()-1; i>=0; i--) { ProcessRecord proc = procsToKill.get(i); Slog.i(TAG, "Removing system update proc: " + proc); - removeProcessLocked(proc, true, false); + removeProcessLocked(proc, true, false, "system update done"); } } @@ -7042,7 +7100,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Don't let services in this process be restarted and potentially // annoy the user repeatedly. Unless it is persistent, since those // processes run critical code. - removeProcessLocked(app, false, false); + removeProcessLocked(app, false, false, "crash"); mMainStack.resumeTopActivityLocked(null); return false; } @@ -9298,8 +9356,10 @@ public final class ActivityManagerService extends ActivityManagerNative } final void dumpApplicationMemoryUsage(FileDescriptor fd, - PrintWriter pw, String prefix, String[] args, boolean brief) { + PrintWriter pw, String prefix, String[] args, boolean brief, + PrintWriter categoryPw) { boolean dumpAll = false; + boolean oomOnly = false; int opti = 0; while (opti < args.length) { @@ -9310,9 +9370,12 @@ public final class ActivityManagerService extends ActivityManagerNative opti++; if ("-a".equals(opt)) { dumpAll = true; + } else if ("--oom".equals(opt)) { + oomOnly = true; } else if ("-h".equals(opt)) { - pw.println("meminfo dump options: [-a] [process]"); + pw.println("meminfo dump options: [-a] [--oom] [process]"); pw.println(" -a: include all available information for each process."); + pw.println(" --oom: only show processes organized by oom adj."); pw.println("If [process] is specified it can be the name or "); pw.println("pid of a specific process to dump."); return; @@ -9438,7 +9501,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - if (!brief) { + if (!brief && !oomOnly) { pw.println(); pw.println("Total PSS by process:"); dumpMemItems(pw, " ", procMems, true); @@ -9446,10 +9509,11 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println("Total PSS by OOM adjustment:"); dumpMemItems(pw, " ", oomMems, false); - if (!brief) { - pw.println(); - pw.println("Total PSS by category:"); - dumpMemItems(pw, " ", catMems, true); + if (!oomOnly) { + PrintWriter out = categoryPw != null ? categoryPw : pw; + out.println(); + out.println("Total PSS by category:"); + dumpMemItems(out, " ", catMems, true); } pw.println(); pw.print("Total PSS: "); pw.print(totalPss); pw.println(" Kb"); diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 289ea1f..2a1b1db 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -190,6 +190,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int MSG_METERED_IFACES_CHANGED = 2; private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 3; private static final int MSG_PROCESS_DIED = 4; + private static final int MSG_LIMIT_REACHED = 5; private final Context mContext; private final IActivityManager mActivityManager; @@ -225,8 +226,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Set of currently active {@link Notification} tags. */ private HashSet<String> mActiveNotifs = Sets.newHashSet(); - /** Current values from {@link #setPolicyDataEnable(int, boolean)}. */ - private SparseBooleanArray mActiveNetworkEnabled = new SparseBooleanArray(); /** Foreground at both UID and PID granularity. */ private SparseBooleanArray mUidForeground = new SparseBooleanArray(); @@ -424,19 +423,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // only someone like NMS should be calling us mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - synchronized (mRulesLock) { - if (mMeteredIfaces.contains(iface) && !LIMIT_GLOBAL_ALERT.equals(limitName)) { - try { - // force stats update to make sure we have numbers that - // caused alert to trigger. - mNetworkStats.forceUpdate(); - } catch (RemoteException e) { - // ignored; service lives in system_server - } - - updateNetworkEnabledLocked(); - updateNotificationsLocked(); - } + if (!LIMIT_GLOBAL_ALERT.equals(limitName)) { + mHandler.obtainMessage(MSG_LIMIT_REACHED, iface).sendToTarget(); } } }; @@ -1481,6 +1469,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } return true; } + case MSG_LIMIT_REACHED: { + final String iface = (String) msg.obj; + + synchronized (mRulesLock) { + if (mMeteredIfaces.contains(iface)) { + try { + // force stats update to make sure we have + // numbers that caused alert to trigger. + mNetworkStats.forceUpdate(); + } catch (RemoteException e) { + // ignored; service lives in system_server + } + + updateNetworkEnabledLocked(); + updateNotificationsLocked(); + } + } + return true; + } default: { return false; } @@ -1519,21 +1526,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** - * Control {@link IConnectivityManager#setPolicyDataEnable(int, boolean)}, - * dispatching only when actually changed. + * Control {@link IConnectivityManager#setPolicyDataEnable(int, boolean)}. */ private void setPolicyDataEnable(int networkType, boolean enabled) { - synchronized (mActiveNetworkEnabled) { - final boolean prevEnabled = mActiveNetworkEnabled.get(networkType, true); - if (prevEnabled == enabled) return; - - try { - mConnManager.setPolicyDataEnable(networkType, enabled); - } catch (RemoteException e) { - // ignored; service lives in system_server - } - - mActiveNetworkEnabled.put(networkType, enabled); + try { + mConnManager.setPolicyDataEnable(networkType, enabled); + } catch (RemoteException e) { + // ignored; service lives in system_server } } diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 494c655..77b0d96 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -152,10 +152,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private static final String TAG_NETSTATS_ERROR = "netstats_error"; - private static final String DEV = "dev"; - private static final String XT = "xt"; - private static final String UID = "uid"; - private final Context mContext; private final INetworkManagementService mNetworkManager; private final IAlarmManager mAlarmManager; @@ -278,6 +274,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { readNetworkXtStatsLocked(); } + // bootstrap initial stats to prevent double-counting later + bootstrapStats(); + // watch for network interfaces to be claimed final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION_IMMEDIATE); mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler); @@ -311,9 +310,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { registerPollAlarmLocked(); registerGlobalAlert(); - // bootstrap initial stats to prevent double-counting later - bootstrapStats(); - mDropBox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE); } @@ -837,9 +833,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // persist when enough network data has occurred final long persistNetworkDevDelta = computeStatsDelta( - mLastPersistNetworkDevSnapshot, networkDevSnapshot, true, DEV).getTotalBytes(); + mLastPersistNetworkDevSnapshot, networkDevSnapshot, true, "devp").getTotalBytes(); final long persistNetworkXtDelta = computeStatsDelta( - mLastPersistNetworkXtSnapshot, networkXtSnapshot, true, XT).getTotalBytes(); + mLastPersistNetworkXtSnapshot, networkXtSnapshot, true, "xtp").getTotalBytes(); final boolean networkOverThreshold = persistNetworkDevDelta > threshold || persistNetworkXtDelta > threshold; if (persistForce || (persistNetwork && networkOverThreshold)) { @@ -851,7 +847,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // persist when enough uid data has occurred final long persistUidDelta = computeStatsDelta( - mLastPersistUidSnapshot, uidSnapshot, true, UID).getTotalBytes(); + mLastPersistUidSnapshot, uidSnapshot, true, "uidp").getTotalBytes(); if (persistForce || (persistUid && persistUidDelta > threshold)) { writeUidStatsLocked(); mLastPersistUidSnapshot = uidSnapshot; @@ -880,7 +876,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final HashSet<String> unknownIface = Sets.newHashSet(); final NetworkStats delta = computeStatsDelta( - mLastPollNetworkDevSnapshot, networkDevSnapshot, false, DEV); + mLastPollNetworkDevSnapshot, networkDevSnapshot, false, "dev"); final long timeStart = currentTime - delta.getElapsedRealtime(); NetworkStats.Entry entry = null; @@ -910,7 +906,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final HashSet<String> unknownIface = Sets.newHashSet(); final NetworkStats delta = computeStatsDelta( - mLastPollNetworkXtSnapshot, networkXtSnapshot, false, XT); + mLastPollNetworkXtSnapshot, networkXtSnapshot, false, "xt"); final long timeStart = currentTime - delta.getElapsedRealtime(); NetworkStats.Entry entry = null; @@ -940,9 +936,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { ensureUidStatsLoadedLocked(); final NetworkStats delta = computeStatsDelta( - mLastPollUidSnapshot, uidSnapshot, false, UID); + mLastPollUidSnapshot, uidSnapshot, false, "uid"); final NetworkStats operationsDelta = computeStatsDelta( - mLastPollOperationsSnapshot, mOperations, false, UID); + mLastPollOperationsSnapshot, mOperations, false, "uidop"); final long timeStart = currentTime - delta.getElapsedRealtime(); NetworkStats.Entry entry = null; @@ -971,8 +967,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } mLastPollUidSnapshot = uidSnapshot; - mLastPollOperationsSnapshot = mOperations; - mOperations = new NetworkStats(0L, 10); + mLastPollOperationsSnapshot = mOperations.clone(); } /** @@ -1516,7 +1511,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // record error for debugging final StringBuilder builder = new StringBuilder(); - builder.append("found non-monotonic " + type + "values at left[" + e.leftIndex + builder.append("found non-monotonic " + type + " values at left[" + e.leftIndex + "] - right[" + e.rightIndex + "]\n"); builder.append("left=").append(e.left).append('\n'); builder.append("right=").append(e.right).append('\n'); diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index d2d2d8b..c2c6b4d 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -478,8 +478,9 @@ status_t SensorService::setEventRate(const sp<SensorEventConnection>& connection if (ns < 0) return BAD_VALUE; - if (ns == 0) { - ns = sensor->getSensor().getMinDelayNs(); + nsecs_t minDelayNs = sensor->getSensor().getMinDelayNs(); + if (ns < minDelayNs) { + ns = minDelayNs; } if (ns < MINIMUM_EVENTS_PERIOD) 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/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 8f4fdb8..1b00e93 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1217,23 +1217,25 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state, sp<Client> client( static_cast<Client *>(s.client.get()) ); transactionFlags |= setClientStateLocked(client, s.state); } + if (transactionFlags) { + // this triggers the transaction setTransactionFlags(transactionFlags); - } - // if this is a synchronous transaction, wait for it to take effect before - // returning. - if (flags & eSynchronous) { - mTransationPending = true; - } - while (mTransationPending) { - status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); - if (CC_UNLIKELY(err != NO_ERROR)) { - // just in case something goes wrong in SF, return to the - // called after a few seconds. - LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!"); - mTransationPending = false; - break; + // if this is a synchronous transaction, wait for it to take effect + // before returning. + if (flags & eSynchronous) { + mTransationPending = true; + } + while (mTransationPending) { + status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); + if (CC_UNLIKELY(err != NO_ERROR)) { + // just in case something goes wrong in SF, return to the + // called after a few seconds. + LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!"); + mTransationPending = false; + break; + } } } } 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/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index fc8a145..1410747 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -291,12 +291,31 @@ public class SmsMessage { // flexibly... int limit; - if (ted.msgCount > 1) { - limit = (ted.codeUnitSize == ENCODING_7BIT) ? - MAX_USER_DATA_SEPTETS_WITH_HEADER : MAX_USER_DATA_BYTES_WITH_HEADER; + if (ted.codeUnitSize == ENCODING_7BIT) { + int udhLength; + if (ted.languageTable != 0 && ted.languageShiftTable != 0) { + udhLength = GsmAlphabet.UDH_SEPTET_COST_TWO_SHIFT_TABLES; + } else if (ted.languageTable != 0 || ted.languageShiftTable != 0) { + udhLength = GsmAlphabet.UDH_SEPTET_COST_ONE_SHIFT_TABLE; + } else { + udhLength = 0; + } + + if (ted.msgCount > 1) { + udhLength += GsmAlphabet.UDH_SEPTET_COST_CONCATENATED_MESSAGE; + } + + if (udhLength != 0) { + udhLength += GsmAlphabet.UDH_SEPTET_COST_LENGTH; + } + + limit = MAX_USER_DATA_SEPTETS - udhLength; } else { - limit = (ted.codeUnitSize == ENCODING_7BIT) ? - MAX_USER_DATA_SEPTETS : MAX_USER_DATA_BYTES; + if (ted.msgCount > 1) { + limit = MAX_USER_DATA_BYTES_WITH_HEADER; + } else { + limit = MAX_USER_DATA_BYTES; + } } int pos = 0; // Index in code units. diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java index 5b13603..6d9a2c2 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java @@ -167,7 +167,9 @@ public abstract class DataConnectionTracker extends Handler { // independent of mInternalDataEnabled and requests for APN access // persisted protected boolean mUserDataEnabled = true; - protected boolean mPolicyDataEnabled = true; + + // TODO: move away from static state once 5587429 is fixed. + protected static boolean sPolicyDataEnabled = true; private boolean[] dataEnabled = new boolean[APN_NUM_TYPES]; @@ -211,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 @@ -766,7 +768,7 @@ public abstract class DataConnectionTracker extends Handler { public boolean getAnyDataEnabled() { final boolean result; synchronized (mDataEnabledLock) { - result = (mInternalDataEnabled && mUserDataEnabled && mPolicyDataEnabled + result = (mInternalDataEnabled && mUserDataEnabled && sPolicyDataEnabled && (enabledCount != 0)); } if (!result && DBG) log("getAnyDataEnabled " + result); @@ -1132,8 +1134,8 @@ public abstract class DataConnectionTracker extends Handler { protected void onSetPolicyDataEnabled(boolean enabled) { synchronized (mDataEnabledLock) { final boolean prevEnabled = getAnyDataEnabled(); - if (mPolicyDataEnabled != enabled) { - mPolicyDataEnabled = enabled; + if (sPolicyDataEnabled != enabled) { + sPolicyDataEnabled = enabled; if (prevEnabled != getAnyDataEnabled()) { if (!prevEnabled) { resetAllRetryCounts(); diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java index 2e99849..25647ac 100644 --- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java +++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java @@ -60,25 +60,25 @@ public class GsmAlphabet { * all combinations of header elements below will have at least one free bit * when padding to the nearest septet boundary. */ - private static final int UDH_SEPTET_COST_LENGTH = 1; + public static final int UDH_SEPTET_COST_LENGTH = 1; /** * Using a non-default language locking shift table OR single shift table * requires a user data header of 3 octets, or 4 septets, plus UDH length. */ - private static final int UDH_SEPTET_COST_ONE_SHIFT_TABLE = 4; + public static final int UDH_SEPTET_COST_ONE_SHIFT_TABLE = 4; /** * Using a non-default language locking shift table AND single shift table * requires a user data header of 6 octets, or 7 septets, plus UDH length. */ - private static final int UDH_SEPTET_COST_TWO_SHIFT_TABLES = 7; + public static final int UDH_SEPTET_COST_TWO_SHIFT_TABLES = 7; /** * Multi-part messages require a user data header of 5 octets, or 6 septets, * plus UDH length. */ - private static final int UDH_SEPTET_COST_CONCATENATED_MESSAGE = 6; + public static final int UDH_SEPTET_COST_CONCATENATED_MESSAGE = 6; /** * Converts a char to a GSM 7 bit table index. 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/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 865caf6..7cd01a1 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -549,7 +549,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { @Override public boolean getAnyDataEnabled() { synchronized (mDataEnabledLock) { - if (!(mInternalDataEnabled && mUserDataEnabled && mPolicyDataEnabled)) return false; + if (!(mInternalDataEnabled && mUserDataEnabled && sPolicyDataEnabled)) return false; for (ApnContext apnContext : mApnContexts.values()) { // Make sure we dont have a context that going down // and is explicitly disabled. diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java index 41a719e..5950669 100644 --- a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java @@ -16,11 +16,14 @@ package com.android.internal.telephony; +import android.telephony.TelephonyManager; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + import com.android.internal.telephony.gsm.SmsMessage; import com.android.internal.util.HexDump; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import java.util.ArrayList; public class GsmSmsTest extends AndroidTestCase { @@ -232,6 +235,110 @@ public class GsmSmsTest extends AndroidTestCase { }; @SmallTest + public void testFragmentText() throws Exception { + boolean isGsmPhone = (TelephonyManager.getDefault().getPhoneType() == + TelephonyManager.PHONE_TYPE_GSM); + + // Valid 160 character 7-bit text. + String text = "123456789012345678901234567890123456789012345678901234567890" + + "1234567890123456789012345678901234567890123456789012345678901234567890" + + "123456789012345678901234567890"; + SmsMessageBase.TextEncodingDetails ted = SmsMessage.calculateLength(text, false); + assertEquals(1, ted.msgCount); + assertEquals(160, ted.codeUnitCount); + assertEquals(1, ted.codeUnitSize); + assertEquals(0, ted.languageTable); + assertEquals(0, ted.languageShiftTable); + if (isGsmPhone) { + ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text); + assertEquals(1, fragments.size()); + } + + // Valid 161 character 7-bit text. + text = "123456789012345678901234567890123456789012345678901234567890" + + "1234567890123456789012345678901234567890123456789012345678901234567890" + + "1234567890123456789012345678901"; + ted = SmsMessage.calculateLength(text, false); + assertEquals(2, ted.msgCount); + assertEquals(161, ted.codeUnitCount); + assertEquals(1, ted.codeUnitSize); + assertEquals(0, ted.languageTable); + assertEquals(0, ted.languageShiftTable); + if (isGsmPhone) { + ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text); + assertEquals(2, fragments.size()); + assertEquals(text, fragments.get(0) + fragments.get(1)); + assertEquals(153, fragments.get(0).length()); + assertEquals(8, fragments.get(1).length()); + } + } + + @SmallTest + public void testFragmentTurkishText() throws Exception { + boolean isGsmPhone = (TelephonyManager.getDefault().getPhoneType() == + TelephonyManager.PHONE_TYPE_GSM); + + int[] oldTables = GsmAlphabet.getEnabledSingleShiftTables(); + int[] turkishTable = { 1 }; + GsmAlphabet.setEnabledSingleShiftTables(turkishTable); + + // Valid 77 character text with Turkish characters. + String text = "ĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşı" + + "ĞŞİğşıĞŞİğşıĞŞİğş"; + SmsMessageBase.TextEncodingDetails ted = SmsMessage.calculateLength(text, false); + assertEquals(1, ted.msgCount); + assertEquals(154, ted.codeUnitCount); + assertEquals(1, ted.codeUnitSize); + assertEquals(0, ted.languageTable); + assertEquals(1, ted.languageShiftTable); + if (isGsmPhone) { + ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text); + assertEquals(1, fragments.size()); + assertEquals(text, fragments.get(0)); + assertEquals(77, fragments.get(0).length()); + } + + // Valid 78 character text with Turkish characters. + text = "ĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşı" + + "ĞŞİğşıĞŞİğşıĞŞİğşı"; + ted = SmsMessage.calculateLength(text, false); + assertEquals(2, ted.msgCount); + assertEquals(156, ted.codeUnitCount); + assertEquals(1, ted.codeUnitSize); + assertEquals(0, ted.languageTable); + assertEquals(1, ted.languageShiftTable); + if (isGsmPhone) { + ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text); + assertEquals(2, fragments.size()); + assertEquals(text, fragments.get(0) + fragments.get(1)); + assertEquals(74, fragments.get(0).length()); + assertEquals(4, fragments.get(1).length()); + } + + // Valid 160 character text with Turkish characters. + text = "ĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşı" + + "ĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğ" + + "ĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşıĞŞİğşı"; + ted = SmsMessage.calculateLength(text, false); + assertEquals(3, ted.msgCount); + assertEquals(320, ted.codeUnitCount); + assertEquals(1, ted.codeUnitSize); + assertEquals(0, ted.languageTable); + assertEquals(1, ted.languageShiftTable); + if (isGsmPhone) { + ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text); + assertEquals(3, fragments.size()); + assertEquals(text, fragments.get(0) + fragments.get(1) + fragments.get(2)); + assertEquals(74, fragments.get(0).length()); + assertEquals(74, fragments.get(1).length()); + assertEquals(12, fragments.get(2).length()); + } + + GsmAlphabet.setEnabledSingleShiftTables(oldTables); + } + + + @SmallTest public void testDecode() throws Exception { decodeSingle(0); // default table decodeSingle(1); // Turkish locking shift table 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 e2349af..d34a7c5 100644 --- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java @@ -550,21 +550,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/wifi/java/android/net/wifi/WifiApConfigStore.java b/wifi/java/android/net/wifi/WifiApConfigStore.java index bb5427d..0531ca3 100644 --- a/wifi/java/android/net/wifi/WifiApConfigStore.java +++ b/wifi/java/android/net/wifi/WifiApConfigStore.java @@ -19,11 +19,16 @@ package android.net.wifi; import android.content.Context; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.os.Environment; -import android.os.Message; import android.os.Handler; -import android.os.HandlerThread; +import android.os.Message; +import android.os.Messenger; import android.util.Log; +import com.android.internal.util.AsyncChannel; +import com.android.internal.R; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; @@ -34,16 +39,13 @@ import java.io.IOException; import java.net.InetAddress; import java.util.UUID; -import com.android.internal.R; - - /** * Provides API to the WifiStateMachine for doing read/write access * to soft access point configuration */ -class WifiApConfigStore { +class WifiApConfigStore extends StateMachine { - private static Context sContext; + private Context mContext; private static final String TAG = "WifiApConfigStore"; private static final String AP_CONFIG_FILE = Environment.getDataDirectory() + @@ -51,131 +53,160 @@ class WifiApConfigStore { private static final int AP_CONFIG_FILE_VERSION = 1; - private static WifiConfiguration sApConfig = new WifiConfiguration(); - private static final Object sApConfigLock = new Object(); + private State mDefaultState = new DefaultState(); + private State mInactiveState = new InactiveState(); + private State mActiveState = new ActiveState(); + + private WifiConfiguration mWifiApConfig = null; + private AsyncChannel mReplyChannel = new AsyncChannel(); - private static FileReadWriteHandler sFileReadWriteHandler; - private static final int READ_AP_CONFIG = 1; - private static final int WRITE_AP_CONFIG = 2; + WifiApConfigStore(Context context, Handler target) { + super(TAG, target.getLooper()); - static void initialize(Context context) { - sContext = context; + mContext = context; + addState(mDefaultState); + addState(mInactiveState, mDefaultState); + addState(mActiveState, mDefaultState); - /* File operations happen on a seperate thread */ - HandlerThread configThread = new HandlerThread("WifiApConfigStore"); - configThread.start(); - sFileReadWriteHandler = new FileReadWriteHandler(configThread.getLooper()); - Message.obtain(sFileReadWriteHandler, READ_AP_CONFIG).sendToTarget(); + setInitialState(mInactiveState); } + public static WifiApConfigStore makeWifiApConfigStore(Context context, Handler target) { + WifiApConfigStore s = new WifiApConfigStore(context, target); + s.start(); + return s; + } - static void setApConfiguration(WifiConfiguration config) { - synchronized (sApConfigLock) { - sApConfig = config; + class DefaultState extends State { + public boolean processMessage(Message message) { + switch (message.what) { + case WifiStateMachine.CMD_SET_AP_CONFIG: + case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED: + Log.e(TAG, "Unexpected message: " + message); + break; + case WifiStateMachine.CMD_REQUEST_AP_CONFIG: + mReplyChannel.replyToMessage(message, + WifiStateMachine.CMD_RESPONSE_AP_CONFIG, mWifiApConfig); + break; + default: + Log.e(TAG, "Failed to handle " + message); + break; + } + return HANDLED; } - Message.obtain(sFileReadWriteHandler, WRITE_AP_CONFIG, new WifiConfiguration(config)) - .sendToTarget(); } - static WifiConfiguration getApConfiguration() { - synchronized (sApConfigLock) { - return new WifiConfiguration(sApConfig); + class InactiveState extends State { + public boolean processMessage(Message message) { + switch (message.what) { + case WifiStateMachine.CMD_SET_AP_CONFIG: + mWifiApConfig = (WifiConfiguration) message.obj; + transitionTo(mActiveState); + break; + default: + return NOT_HANDLED; + } + return HANDLED; } } - /** - * File read/write handler - */ - private static class FileReadWriteHandler extends Handler { - - public FileReadWriteHandler(android.os.Looper looper) { - super(looper); + class ActiveState extends State { + public void enter() { + new Thread(new Runnable() { + public void run() { + writeApConfiguration(mWifiApConfig); + sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED); + } + }).start(); } - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case WRITE_AP_CONFIG: - writeApConfiguration((WifiConfiguration) msg.obj); + public boolean processMessage(Message message) { + switch (message.what) { + //TODO: have feedback to the user when we do this + //to indicate the write is currently in progress + case WifiStateMachine.CMD_SET_AP_CONFIG: + deferMessage(message); break; - case READ_AP_CONFIG: - readApConfiguration(); + case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED: + transitionTo(mInactiveState); break; default: - Log.e(TAG, "Unknown command in FileReadWriteHandler: " + msg); - break; + return NOT_HANDLED; } + return HANDLED; } + } - private static void writeApConfiguration(final WifiConfiguration config) { - DataOutputStream out = null; - try { - out = new DataOutputStream(new BufferedOutputStream( - new FileOutputStream(AP_CONFIG_FILE))); - - out.writeInt(AP_CONFIG_FILE_VERSION); - out.writeUTF(config.SSID); - int authType = config.getAuthType(); - out.writeInt(authType); - if(authType != KeyMgmt.NONE) { - out.writeUTF(config.preSharedKey); - } - } catch (IOException e) { - Log.e(TAG, "Error writing hotspot configuration" + e); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) {} - } - } - } + void loadApConfiguration() { + DataInputStream in = null; + try { + WifiConfiguration config = new WifiConfiguration(); + in = new DataInputStream(new BufferedInputStream(new FileInputStream( + AP_CONFIG_FILE))); - private static void readApConfiguration() { - DataInputStream in = null; - try { - WifiConfiguration config = new WifiConfiguration(); - in = new DataInputStream(new BufferedInputStream(new FileInputStream( - AP_CONFIG_FILE))); - - int version = in.readInt(); - if (version != 1) { - Log.e(TAG, "Bad version on hotspot configuration file, set defaults"); - setDefaultApConfiguration(); - return; - } - config.SSID = in.readUTF(); - int authType = in.readInt(); - config.allowedKeyManagement.set(authType); - if (authType != KeyMgmt.NONE) { - config.preSharedKey = in.readUTF(); - } - synchronized (sApConfigLock) { - sApConfig = config; - } - } catch (IOException ignore) { + int version = in.readInt(); + if (version != 1) { + Log.e(TAG, "Bad version on hotspot configuration file, set defaults"); setDefaultApConfiguration(); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) {} - } + return; + } + config.SSID = in.readUTF(); + int authType = in.readInt(); + config.allowedKeyManagement.set(authType); + if (authType != KeyMgmt.NONE) { + config.preSharedKey = in.readUTF(); + } + mWifiApConfig = config; + } catch (IOException ignore) { + setDefaultApConfiguration(); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) {} } } + } - /* Generate a default WPA2 based configuration with a random password. - We are changing the Wifi Ap configuration storage from secure settings to a - flat file accessible only by the system. A WPA2 based default configuration - will keep the device secure after the update */ - private static void setDefaultApConfiguration() { - WifiConfiguration config = new WifiConfiguration(); - config.SSID = sContext.getString(R.string.wifi_tether_configure_ssid_default); - config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK); - String randomUUID = UUID.randomUUID().toString(); - //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx - config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9,13); - setApConfiguration(config); + Messenger getMessenger() { + return new Messenger(getHandler()); + } + + private void writeApConfiguration(final WifiConfiguration config) { + DataOutputStream out = null; + try { + out = new DataOutputStream(new BufferedOutputStream( + new FileOutputStream(AP_CONFIG_FILE))); + + out.writeInt(AP_CONFIG_FILE_VERSION); + out.writeUTF(config.SSID); + int authType = config.getAuthType(); + out.writeInt(authType); + if(authType != KeyMgmt.NONE) { + out.writeUTF(config.preSharedKey); + } + } catch (IOException e) { + Log.e(TAG, "Error writing hotspot configuration" + e); + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) {} + } } } + + /* Generate a default WPA2 based configuration with a random password. + We are changing the Wifi Ap configuration storage from secure settings to a + flat file accessible only by the system. A WPA2 based default configuration + will keep the device secure after the update */ + private void setDefaultApConfiguration() { + WifiConfiguration config = new WifiConfiguration(); + config.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default); + config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK); + String randomUUID = UUID.randomUUID().toString(); + //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx + config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9,13); + sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG, config); + } } diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 00fae2e..aadcaad 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -184,6 +184,7 @@ public class WifiStateMachine extends StateMachine { private WifiP2pManager mWifiP2pManager; //Used to initiate a connection with WifiP2pService private AsyncChannel mWifiP2pChannel = new AsyncChannel(); + private AsyncChannel mWifiApConfigChannel = new AsyncChannel(); // Event log tags (must be in sync with event-log-tags) private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021; @@ -233,12 +234,16 @@ public class WifiStateMachine extends StateMachine { static final int CMD_STOP_AP = BASE + 24; /* Set the soft access point configuration */ static final int CMD_SET_AP_CONFIG = BASE + 25; - /* Get the soft access point configuration */ - static final int CMD_GET_AP_CONFIG = BASE + 26; + /* Soft access point configuration set completed */ + static final int CMD_SET_AP_CONFIG_COMPLETED = BASE + 26; + /* Request the soft access point configuration */ + static final int CMD_REQUEST_AP_CONFIG = BASE + 27; + /* Response to access point configuration request */ + static final int CMD_RESPONSE_AP_CONFIG = BASE + 28; /* Set configuration on tether interface */ - static final int CMD_TETHER_INTERFACE = BASE + 27; + static final int CMD_TETHER_INTERFACE = BASE + 29; - static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 28; + static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 30; /* Supplicant commands */ /* Is supplicant alive ? */ @@ -530,6 +535,11 @@ public class WifiStateMachine extends StateMachine { mWpsStateMachine = new WpsStateMachine(context, this, getHandler()); mLinkProperties = new LinkProperties(); + WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore( + context, getHandler()); + wifiApConfigStore.loadApConfiguration(); + mWifiApConfigChannel.connectSync(mContext, getHandler(), wifiApConfigStore.getMessenger()); + mNetworkInfo.setIsAvailable(false); mLinkProperties.clear(); mLastBssid = null; @@ -659,11 +669,11 @@ public class WifiStateMachine extends StateMachine { } public void setWifiApConfiguration(WifiConfiguration config) { - sendMessage(obtainMessage(CMD_SET_AP_CONFIG, config)); + mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config); } - public WifiConfiguration syncGetWifiApConfiguration(AsyncChannel channel) { - Message resultMsg = channel.sendMessageSynchronously(CMD_GET_AP_CONFIG); + public WifiConfiguration syncGetWifiApConfiguration() { + Message resultMsg = mWifiApConfigChannel.sendMessageSynchronously(CMD_REQUEST_AP_CONFIG); WifiConfiguration ret = (WifiConfiguration) resultMsg.obj; resultMsg.recycle(); return ret; @@ -1714,25 +1724,27 @@ public class WifiStateMachine extends StateMachine { * TODO: Add control channel setup through hostapd that allows changing config * on a running daemon */ - private boolean startSoftApWithConfig(WifiConfiguration config) { - if (config == null) { - config = WifiApConfigStore.getApConfiguration(); - } else { - WifiApConfigStore.setApConfiguration(config); - } - try { - mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE); - } catch (Exception e) { - loge("Exception in softap start " + e); - try { - mNwService.stopAccessPoint(mInterfaceName); - mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE); - } catch (Exception e1) { - loge("Exception in softap re-start " + e1); - return false; + private void startSoftApWithConfig(final WifiConfiguration config) { + // start hostapd on a seperate thread + new Thread(new Runnable() { + public void run() { + try { + mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE); + } catch (Exception e) { + loge("Exception in softap start " + e); + try { + mNwService.stopAccessPoint(mInterfaceName); + mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE); + } catch (Exception e1) { + loge("Exception in softap re-start " + e1); + sendMessage(CMD_START_AP_FAILURE); + return; + } + } + if (DBG) log("Soft AP start successful"); + sendMessage(CMD_START_AP_SUCCESS); } - } - return true; + }).start(); } /******************************************************** @@ -1775,13 +1787,6 @@ public class WifiStateMachine extends StateMachine { case CMD_ENABLE_BACKGROUND_SCAN: mEnableBackgroundScan = (message.arg1 == 1); break; - case CMD_SET_AP_CONFIG: - WifiApConfigStore.setApConfiguration((WifiConfiguration) message.obj); - break; - case CMD_GET_AP_CONFIG: - WifiConfiguration config = WifiApConfigStore.getApConfiguration(); - mReplyChannel.replyToMessage(message, message.what, config); - break; /* Discard */ case CMD_LOAD_DRIVER: case CMD_UNLOAD_DRIVER: @@ -1823,6 +1828,11 @@ public class WifiStateMachine extends StateMachine { case CMD_ENABLE_ALL_NETWORKS: case DhcpStateMachine.CMD_PRE_DHCP_ACTION: case DhcpStateMachine.CMD_POST_DHCP_ACTION: + /* Handled by WifiApConfigStore */ + case CMD_SET_AP_CONFIG: + case CMD_SET_AP_CONFIG_COMPLETED: + case CMD_REQUEST_AP_CONFIG: + case CMD_RESPONSE_AP_CONFIG: break; case WifiMonitor.DRIVER_HUNG_EVENT: setWifiEnabled(false); @@ -1856,8 +1866,6 @@ public class WifiStateMachine extends StateMachine { // 50021 wifi_state_changed (custom|1|5) EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); - WifiApConfigStore.initialize(mContext); - if (WifiNative.isDriverLoaded()) { transitionTo(mDriverLoadedState); } @@ -3243,21 +3251,19 @@ public class WifiStateMachine extends StateMachine { if (DBG) log(getName() + "\n"); EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); - final Message message = Message.obtain(getCurrentMessage()); - final WifiConfiguration config = (WifiConfiguration) message.obj; + final Message message = getCurrentMessage(); + if (message.what == CMD_START_AP) { + final WifiConfiguration config = (WifiConfiguration) message.obj; - // start hostapd on a seperate thread - new Thread(new Runnable() { - public void run() { - if (startSoftApWithConfig(config)) { - if (DBG) log("Soft AP start successful"); - sendMessage(CMD_START_AP_SUCCESS); - } else { - loge("Soft AP start failed"); - sendMessage(CMD_START_AP_FAILURE); - } + if (config == null) { + mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG); + } else { + mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config); + startSoftApWithConfig(config); } - }).start(); + } else { + throw new RuntimeException("Illegal transition to SoftApStartingState: " + message); + } } @Override public boolean processMessage(Message message) { @@ -3282,6 +3288,15 @@ public class WifiStateMachine extends StateMachine { case WifiP2pService.P2P_ENABLE_PENDING: deferMessage(message); break; + case WifiStateMachine.CMD_RESPONSE_AP_CONFIG: + WifiConfiguration config = (WifiConfiguration) message.obj; + if (config != null) { + startSoftApWithConfig(config); + } else { + loge("Softap config is null!"); + sendMessage(CMD_START_AP_FAILURE); + } + break; case CMD_START_AP_SUCCESS: setWifiApState(WIFI_AP_STATE_ENABLED); transitionTo(mSoftApStartedState); |
