diff options
Diffstat (limited to 'core/java')
33 files changed, 677 insertions, 363 deletions
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 473a2d1..f427e78 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -181,7 +181,8 @@ final class FragmentState implements Parcelable { * * While the Fragment API was introduced in * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API - * is also available for use on older platforms. See the blog post + * at is also available for use on older platforms through + * {@link android.support.v4.app.FragmentActivity}. See the blog post * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html"> * Fragments For All</a> for more details. * diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index a8c9cba..1abb7de 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -51,6 +51,13 @@ import java.util.Arrays; * <p>For more information about using fragments, read the * <a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p> * </div> + * + * While the FragmentManager API was introduced in + * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API + * at is also available for use on older platforms through + * {@link android.support.v4.app.FragmentActivity}. See the blog post + * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html"> + * Fragments For All</a> for more details. */ public abstract class FragmentManager { /** diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java index 1b8a4f5..d83d2e6 100644 --- a/core/java/android/app/LoaderManager.java +++ b/core/java/android/app/LoaderManager.java @@ -36,7 +36,8 @@ import java.lang.reflect.Modifier; * * While the LoaderManager API was introduced in * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API - * is also available for use on older platforms. See the blog post + * at is also available for use on older platforms through + * {@link android.support.v4.app.FragmentActivity}. See the blog post * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html"> * Fragments For All</a> for more details. * diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 5f5ba50..e420bfd 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -399,6 +399,25 @@ public final class BluetoothAdapter { } /** + * Get a {@link BluetoothDevice} object for the given Bluetooth hardware + * address. + * <p>Valid Bluetooth hardware addresses must be 6 bytes. This method + * expects the address in network byte order (MSB first). + * <p>A {@link BluetoothDevice} will always be returned for a valid + * hardware address, even if this adapter has never seen that device. + * + * @param address Bluetooth MAC address (6 bytes) + * @throws IllegalArgumentException if address is invalid + */ + public BluetoothDevice getRemoteDevice(byte[] address) { + if (address == null || address.length != 6) { + throw new IllegalArgumentException("Bluetooth address must have 6 bytes"); + } + return new BluetoothDevice(String.format("%02X:%02X:%02X:%02X:%02X:%02X", + address[0], address[1], address[2], address[3], address[4], address[5])); + } + + /** * Return true if Bluetooth is currently enabled and ready for use. * <p>Equivalent to: * <code>getBluetoothState() == STATE_ON</code> @@ -1281,7 +1300,7 @@ public final class BluetoothAdapter { } /** - * Validate a Bluetooth address, such as "00:43:A8:23:10:F0" + * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0" * <p>Alphabetic characters must be uppercase to be valid. * * @param address Bluetooth address as string diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index d71a8d6..b609c26 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -28,11 +28,21 @@ import android.util.Slog; /** * Base class for code that will receive intents sent by sendBroadcast(). - * You can either dynamically register an instance of this class with + * + * <p>If you don't need to send broadcasts across applications, consider using + * this class with {@link android.support.v4.content.LocalBroadcastManager} instead + * of the more general facilities described below. This will give you a much + * more efficient implementation (no cross-process communication needed) and allow + * you to avoid thinking about any security issues related to other applications + * being able to receive or send your broadcasts. + * + * <p>You can either dynamically register an instance of this class with * {@link Context#registerReceiver Context.registerReceiver()} * or statically publish an implementation through the * {@link android.R.styleable#AndroidManifestReceiver <receiver>} - * tag in your <code>AndroidManifest.xml</code>. <em><strong>Note:</strong></em> + * tag in your <code>AndroidManifest.xml</code>. + * + * <p><em><strong>Note:</strong></em> * If registering a receiver in your * {@link android.app.Activity#onResume() Activity.onResume()} * implementation, you should unregister it in @@ -86,8 +96,8 @@ import android.util.Slog; * * <p>Topics covered here: * <ol> + * <li><a href="#Security">Security</a> * <li><a href="#ReceiverLifecycle">Receiver Lifecycle</a> - * <li><a href="#Permissions">Permissions</a> * <li><a href="#ProcessLifecycle">Process Lifecycle</a> * </ol> * @@ -98,32 +108,39 @@ import android.util.Slog; * developer guide.</p> * </div> * - * <a name="ReceiverLifecycle"></a> - * <h3>Receiver Lifecycle</h3> - * - * <p>A BroadcastReceiver object is only valid for the duration of the call - * to {@link #onReceive}. Once your code returns from this function, - * the system considers the object to be finished and no longer active. - * - * <p>This has important repercussions to what you can do in an - * {@link #onReceive} implementation: anything that requires asynchronous - * operation is not available, because you will need to return from the - * function to handle the asynchronous operation, but at that point the - * BroadcastReceiver is no longer active and thus the system is free to kill - * its process before the asynchronous operation completes. - * - * <p>In particular, you may <i>not</i> show a dialog or bind to a service from - * within a BroadcastReceiver. For the former, you should instead use the - * {@link android.app.NotificationManager} API. For the latter, you can - * use {@link android.content.Context#startService Context.startService()} to - * send a command to the service. - * - * <a name="Permissions"></a> - * <h3>Permissions</h3> - * + * <a name="Security"></a> + * <h3>Security</h3> + * + * <p>Receivers used with the {@link Context} APIs are by their nature a + * cross-application facility, so you must consider how other applications + * may be able to abuse your use of them. Some things to consider are: + * + * <ul> + * <li><p>The Intent namespace is global. Make sure that Intent action names and + * other strings are written in a namespace you own, or else you may inadvertantly + * conflict with other applications. + * <li><p>When you use {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)}, + * <em>any</em> application may send broadcasts to that registered receiver. You can + * control who can send broadcasts to it through permissions described below. + * <li><p>When you publish a receiver in your application's manifest and specify + * intent-filters for it, any other application can send broadcasts to it regardless + * of the filters you specify. To prevent others from sending to it, make it + * unavailable to them with <code>android:exported="false"</code>. + * <li><p>When you use {@link Context#sendBroadcast(Intent)} or related methods, + * normally any other application can receive these broadcasts. You can control who + * can receive such broadcasts through permissions described below. Alternatively, + * starting with {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, you + * can also safely restrict the broadcast to a single application with + * {@link Intent#setPackage(String) Intent.setPackage} + * </ul> + * + * <p>None of these issues exist when using + * {@link android.support.v4.content.LocalBroadcastManager}, since intents + * broadcast it never go outside of the current process. + * * <p>Access permissions can be enforced by either the sender or receiver - * of an Intent. - * + * of a broadcast. + * * <p>To enforce a permission when sending, you supply a non-null * <var>permission</var> argument to * {@link Context#sendBroadcast(Intent, String)} or @@ -133,7 +150,7 @@ import android.util.Slog; * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} * tag in their <code>AndroidManifest.xml</code>) will be able to receive * the broadcast. - * + * * <p>To enforce a permission when receiving, you supply a non-null * <var>permission</var> when registering your receiver -- either when calling * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)} @@ -144,10 +161,30 @@ import android.util.Slog; * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} * tag in their <code>AndroidManifest.xml</code>) will be able to send an * Intent to the receiver. - * + * * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> * document for more information on permissions and security in general. + * + * <a name="ReceiverLifecycle"></a> + * <h3>Receiver Lifecycle</h3> + * + * <p>A BroadcastReceiver object is only valid for the duration of the call + * to {@link #onReceive}. Once your code returns from this function, + * the system considers the object to be finished and no longer active. + * + * <p>This has important repercussions to what you can do in an + * {@link #onReceive} implementation: anything that requires asynchronous + * operation is not available, because you will need to return from the + * function to handle the asynchronous operation, but at that point the + * BroadcastReceiver is no longer active and thus the system is free to kill + * its process before the asynchronous operation completes. * + * <p>In particular, you may <i>not</i> show a dialog or bind to a service from + * within a BroadcastReceiver. For the former, you should instead use the + * {@link android.app.NotificationManager} API. For the latter, you can + * use {@link android.content.Context#startService Context.startService()} to + * send a command to the service. + * * <a name="ProcessLifecycle"></a> * <h3>Process Lifecycle</h3> * diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java index 2d2a90d..ac05682 100644 --- a/core/java/android/content/Loader.java +++ b/core/java/android/content/Loader.java @@ -58,6 +58,13 @@ public class Loader<D> { boolean mReset = true; boolean mContentChanged = false; + /** + * An implementation of a ContentObserver that takes care of connecting + * it to the Loader to have the loader re-load its data when the observer + * is told it has changed. You do not normally need to use this yourself; + * it is used for you by {@link CursorLoader} to take care of executing + * an update when the cursor's backing data changes. + */ public final class ForceLoadContentObserver extends ContentObserver { public ForceLoadContentObserver() { super(new Handler()); @@ -74,6 +81,14 @@ public class Loader<D> { } } + /** + * Interface that is implemented to discover when a Loader has finished + * loading its data. You do not normally need to implement this yourself; + * it is used in the implementation of {@link android.app.LoaderManager} + * to find out when a Loader it is managing has completed so that this can + * be reported to its client. This interface should only be used if a + * Loader is not being used in conjunction with LoaderManager. + */ public interface OnLoadCompleteListener<D> { /** * Called on the thread that created the Loader when the load is complete. diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java index 72431f3..10c1195 100644 --- a/core/java/android/inputmethodservice/ExtractEditText.java +++ b/core/java/android/inputmethodservice/ExtractEditText.java @@ -158,25 +158,46 @@ public class ExtractEditText extends EditText { } /** - * Delete the range of text, supposedly valid + * {@inheritDoc} * @hide */ @Override protected void deleteText_internal(int start, int end) { - // Do not call the super method. This will change the source TextView instead, which - // will update the ExtractTextView. + // Do not call the super method. + // This will change the source TextView instead, which will update the ExtractTextView. mIME.onExtractedDeleteText(start, end); } /** - * Replaces the range of text [start, end[ by replacement text + * {@inheritDoc} * @hide */ @Override protected void replaceText_internal(int start, int end, CharSequence text) { - // Do not call the super method. This will change the source TextView instead, which - // will update the ExtractTextView. + // Do not call the super method. + // This will change the source TextView instead, which will update the ExtractTextView. mIME.onExtractedReplaceText(start, end, text); } + /** + * {@inheritDoc} + * @hide + */ + @Override + protected void setSpan_internal(Object span, int start, int end, int flags) { + // Do not call the super method. + // This will change the source TextView instead, which will update the ExtractTextView. + mIME.onExtractedSetSpan(span, start, end, flags); + } + + /** + * {@inheritDoc} + * @hide + */ + @Override + protected void setCursorPosition_internal(int start, int end) { + // Do not call the super method. + // This will change the source TextView instead, which will update the ExtractTextView. + mIME.onExtractedSelectionChanged(start, end); + } } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 02839db..53cdf21 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -2006,6 +2006,22 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * @hide + */ + public void onExtractedSetSpan(Object span, int start, int end, int flags) { + InputConnection conn = getCurrentInputConnection(); + if (conn != null) { + if (!conn.setSelection(start, end)) return; + CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES); + if (text instanceof Spannable) { + ((Spannable) text).setSpan(span, 0, text.length(), flags); + conn.setComposingRegion(start, end); + conn.commitText(text, 1); + } + } + } + + /** * This is called when the user has clicked on the extracted text view, * when running in fullscreen mode. The default implementation hides * the candidates view when this happens, but only if the extracted text diff --git a/core/java/android/server/BluetoothAdapterStateMachine.java b/core/java/android/server/BluetoothAdapterStateMachine.java index f4a390e..ed59b03 100644 --- a/core/java/android/server/BluetoothAdapterStateMachine.java +++ b/core/java/android/server/BluetoothAdapterStateMachine.java @@ -360,13 +360,13 @@ final class BluetoothAdapterStateMachine extends StateMachine { boolean retValue = HANDLED; switch(message.what) { case USER_TURN_ON: + broadcastState(BluetoothAdapter.STATE_TURNING_ON); if ((Boolean) message.obj) { persistSwitchSetting(true); } // let it fall to TURN_ON_CONTINUE: //$FALL-THROUGH$ case TURN_ON_CONTINUE: - broadcastState(BluetoothAdapter.STATE_TURNING_ON); mBluetoothService.switchConnectable(true); transitionTo(mSwitching); break; diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java index 5282e61..2b8a458 100644 --- a/core/java/android/service/textservice/SpellCheckerService.java +++ b/core/java/android/service/textservice/SpellCheckerService.java @@ -24,6 +24,7 @@ import android.app.Service; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.util.Log; import android.view.textservice.SuggestionsInfo; @@ -206,11 +207,15 @@ public abstract class SpellCheckerService extends Service { @Override public void onGetSuggestionsMultiple( TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { + int pri = Process.getThreadPriority(Process.myTid()); try { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); mListener.onGetSuggestions( mSession.onGetSuggestionsMultiple( textInfos, suggestionsLimit, sequentialWords)); } catch (RemoteException e) { + } finally { + Process.setThreadPriority(pri); } } @@ -226,13 +231,25 @@ public abstract class SpellCheckerService extends Service { @Override public void onCancel() { - mSession.onCancel(); + int pri = Process.getThreadPriority(Process.myTid()); + try { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + mSession.onCancel(); + } finally { + Process.setThreadPriority(pri); + } } @Override public void onClose() { - mSession.onClose(); - mListener = null; + int pri = Process.getThreadPriority(Process.myTid()); + try { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + mSession.onClose(); + } finally { + Process.setThreadPriority(pri); + mListener = null; + } } public String getLocale() { diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index 231f913..b708750 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -863,6 +863,17 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable return new String(buf); } + /** + * Return a String containing a copy of the chars in this buffer, limited to the + * [start, end[ range. + * @hide + */ + public String substring(int start, int end) { + char[] buf = new char[end - start]; + getChars(start, end, buf, 0); + return new String(buf); + } + private TextWatcher[] sendTextWillChange(int start, int before, int after) { TextWatcher[] recip = getSpans(start, start + before, TextWatcher.class); int n = recip.length; diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index b73d900..1e8a2f7 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -59,6 +59,12 @@ class TextLine { private boolean mCharsValid; private Spanned mSpanned; private final TextPaint mWorkPaint = new TextPaint(); + private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet = + new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class); + private final SpanSet<CharacterStyle> mCharacterStyleSpanSet = + new SpanSet<CharacterStyle>(CharacterStyle.class); + private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet = + new SpanSet<ReplacementSpan>(ReplacementSpan.class); private static final TextLine[] sCached = new TextLine[3]; @@ -96,6 +102,11 @@ class TextLine { tl.mText = null; tl.mPaint = null; tl.mDirections = null; + + tl.mMetricAffectingSpanSpanSet.recycle(); + tl.mCharacterStyleSpanSet.recycle(); + tl.mReplacementSpanSpanSet.recycle(); + synchronized(sCached) { for (int i = 0; i < sCached.length; ++i) { if (sCached[i] == null) { @@ -119,7 +130,6 @@ class TextLine { * @param hasTabs true if the line might contain tabs or emoji * @param tabStops the tabStops. Can be null. */ - @SuppressWarnings("null") void set(TextPaint paint, CharSequence text, int start, int limit, int dir, Directions directions, boolean hasTabs, TabStops tabStops) { mPaint = paint; @@ -135,12 +145,10 @@ class TextLine { mSpanned = null; boolean hasReplacement = false; - SpanSet<ReplacementSpan> replacementSpans = null; if (text instanceof Spanned) { mSpanned = (Spanned) text; - replacementSpans = new SpanSet<ReplacementSpan>(mSpanned, start, limit, - ReplacementSpan.class); - hasReplacement = replacementSpans.numberOfSpans > 0; + mReplacementSpanSpanSet.init(mSpanned, start, limit); + hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0; } mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; @@ -158,9 +166,8 @@ class TextLine { // zero-width characters. char[] chars = mChars; for (int i = start, inext; i < limit; i = inext) { - // replacementSpans cannot be null if hasReplacement is true - inext = replacementSpans.getNextTransition(i, limit); - if (replacementSpans.hasSpansIntersecting(i, inext)) { + inext = mReplacementSpanSpanSet.getNextTransition(i, limit); + if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) { // transition into a span chars[i - start] = '\ufffc'; for (int j = i - start + 1, e = inext - start; j < e; ++j) { @@ -854,23 +861,32 @@ class TextLine { } private static class SpanSet<E> { - final int numberOfSpans; - final E[] spans; - final int[] spanStarts; - final int[] spanEnds; - final int[] spanFlags; + int numberOfSpans; + E[] spans; + int[] spanStarts; + int[] spanEnds; + int[] spanFlags; + final Class<? extends E> classType; + + SpanSet(Class<? extends E> type) { + classType = type; + numberOfSpans = 0; + } @SuppressWarnings("unchecked") - SpanSet(Spanned spanned, int start, int limit, Class<? extends E> type) { - final E[] allSpans = spanned.getSpans(start, limit, type); + public void init(Spanned spanned, int start, int limit) { + final E[] allSpans = spanned.getSpans(start, limit, classType); final int length = allSpans.length; - // These arrays may end up being too large because of empty spans - spans = (E[]) Array.newInstance(type, length); - spanStarts = new int[length]; - spanEnds = new int[length]; - spanFlags = new int[length]; - int count = 0; + if (length > 0 && (spans == null || spans.length < length)) { + // These arrays may end up being too large because of empty spans + spans = (E[]) Array.newInstance(classType, length); + spanStarts = new int[length]; + spanEnds = new int[length]; + spanFlags = new int[length]; + } + + numberOfSpans = 0; for (int i = 0; i < length; i++) { final E span = allSpans[i]; @@ -879,34 +895,14 @@ class TextLine { if (spanStart == spanEnd) continue; final int spanFlag = spanned.getSpanFlags(span); - final int priority = spanFlag & Spanned.SPAN_PRIORITY; - if (priority != 0 && count != 0) { - int j; - - for (j = 0; j < count; j++) { - final int otherPriority = spanFlags[j] & Spanned.SPAN_PRIORITY; - if (priority > otherPriority) break; - } - System.arraycopy(spans, j, spans, j + 1, count - j); - System.arraycopy(spanStarts, j, spanStarts, j + 1, count - j); - System.arraycopy(spanEnds, j, spanEnds, j + 1, count - j); - System.arraycopy(spanFlags, j, spanFlags, j + 1, count - j); + spans[numberOfSpans] = span; + spanStarts[numberOfSpans] = spanStart; + spanEnds[numberOfSpans] = spanEnd; + spanFlags[numberOfSpans] = spanFlag; - spans[j] = span; - spanStarts[j] = spanStart; - spanEnds[j] = spanEnd; - spanFlags[j] = spanFlag; - } else { - spans[i] = span; - spanStarts[i] = spanStart; - spanEnds[i] = spanEnd; - spanFlags[i] = spanFlag; - } - - count++; + numberOfSpans++; } - numberOfSpans = count; } public boolean hasSpansIntersecting(int start, int end) { @@ -927,6 +923,13 @@ class TextLine { } return limit; } + + public void recycle() { + // The spans array is guaranteed to be not null when numberOfSpans is > 0 + for (int i = 0; i < numberOfSpans; i++) { + spans[i] = null; // prevent a leak: no reference kept when TextLine is recycled + } + } } /** @@ -970,10 +973,8 @@ class TextLine { y, bottom, fmi, needWidth || mlimit < measureLimit); } - final SpanSet<MetricAffectingSpan> metricAffectingSpans = new SpanSet<MetricAffectingSpan>( - mSpanned, mStart + start, mStart + limit, MetricAffectingSpan.class); - final SpanSet<CharacterStyle> characterStyleSpans = new SpanSet<CharacterStyle>( - mSpanned, mStart + start, mStart + limit, CharacterStyle.class); + mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit); + mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit); // Shaping needs to take into account context up to metric boundaries, // but rendering needs to take into account character style boundaries. @@ -985,17 +986,18 @@ class TextLine { TextPaint wp = mWorkPaint; wp.set(mPaint); - inext = metricAffectingSpans.getNextTransition(mStart + i, mStart + limit) - mStart; + inext = mMetricAffectingSpanSpanSet.getNextTransition(mStart + i, mStart + limit) - + mStart; int mlimit = Math.min(inext, measureLimit); ReplacementSpan replacement = null; - for (int j = 0; j < metricAffectingSpans.numberOfSpans; j++) { + for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) { // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT // empty by construction. This special case in getSpans() explains the >= & <= tests - if ((metricAffectingSpans.spanStarts[j] >= mStart + mlimit) || - (metricAffectingSpans.spanEnds[j] <= mStart + i)) continue; - MetricAffectingSpan span = metricAffectingSpans.spans[j]; + if ((mMetricAffectingSpanSpanSet.spanStarts[j] >= mStart + mlimit) || + (mMetricAffectingSpanSpanSet.spanEnds[j] <= mStart + i)) continue; + MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j]; if (span instanceof ReplacementSpan) { replacement = (ReplacementSpan)span; } else { @@ -1016,16 +1018,16 @@ class TextLine { y, bottom, fmi, needWidth || mlimit < measureLimit); } else { for (int j = i, jnext; j < mlimit; j = jnext) { - jnext = characterStyleSpans.getNextTransition(mStart + j, mStart + mlimit) - + jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) - mStart; wp.set(mPaint); - for (int k = 0; k < characterStyleSpans.numberOfSpans; k++) { + for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) { // Intentionally using >= and <= as explained above - if ((characterStyleSpans.spanStarts[k] >= mStart + jnext) || - (characterStyleSpans.spanEnds[k] <= mStart + j)) continue; + if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) || + (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue; - CharacterStyle span = characterStyleSpans.spans[k]; + CharacterStyle span = mCharacterStyleSpanSet.spans[k]; span.updateDrawState(wp); } diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index 5ae65df..121c6f2 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -21,7 +21,7 @@ import android.net.NetworkUtils; /** * Utility class to aid in formatting common values that are not covered - * by {@link java.util.Formatter} + * by the {@link java.util.Formatter} class in {@link java.util} */ public final class Formatter { diff --git a/core/java/android/text/format/package.html b/core/java/android/text/format/package.html new file mode 100644 index 0000000..b9e6a44 --- /dev/null +++ b/core/java/android/text/format/package.html @@ -0,0 +1,7 @@ +<HTML> +<BODY> +This package contains alternative classes for some text formatting classes +defined in {@link java.util} and {@link java.text}. It also contains additional text formatting +classes for situations not covered by {@link java.util} or {@link java.text}. +</BODY> +</HTML> diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java index 239d9e8..11226a9 100644 --- a/core/java/android/text/method/WordIterator.java +++ b/core/java/android/text/method/WordIterator.java @@ -18,6 +18,7 @@ package android.text.method; import android.text.Selection; +import android.text.SpannableStringBuilder; import java.text.BreakIterator; import java.util.Locale; @@ -58,7 +59,11 @@ public class WordIterator implements Selection.PositionIterator { mOffsetShift = Math.max(0, start - WINDOW_WIDTH); final int windowEnd = Math.min(charSequence.length(), end + WINDOW_WIDTH); - mString = charSequence.toString().substring(mOffsetShift, windowEnd); + if (charSequence instanceof SpannableStringBuilder) { + mString = ((SpannableStringBuilder) charSequence).substring(mOffsetShift, windowEnd); + } else { + mString = charSequence.subSequence(mOffsetShift, windowEnd).toString(); + } mIterator.setText(mString); } diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java index ed2af10..0f26a34 100644 --- a/core/java/android/text/style/SuggestionSpan.java +++ b/core/java/android/text/style/SuggestionSpan.java @@ -92,11 +92,6 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { private float mAutoCorrectionUnderlineThickness; private int mAutoCorrectionUnderlineColor; - /* - * TODO: If switching IME is required, needs to add parameters for ids of InputMethodInfo - * and InputMethodSubtype. - */ - /** * @param context Context for the application * @param suggestions Suggestions for the string under the span @@ -146,6 +141,16 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { } private void initStyle(Context context) { + if (context == null) { + mMisspelledUnderlineThickness = 0; + mEasyCorrectUnderlineThickness = 0; + mAutoCorrectionUnderlineThickness = 0; + mMisspelledUnderlineColor = Color.BLACK; + mEasyCorrectUnderlineColor = Color.BLACK; + mAutoCorrectionUnderlineColor = Color.BLACK; + return; + } + int defStyle = com.android.internal.R.attr.textAppearanceMisspelledSuggestion; TypedArray typedArray = context.obtainStyledAttributes( null, com.android.internal.R.styleable.SuggestionSpan, defStyle, 0); @@ -169,7 +174,6 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { com.android.internal.R.styleable.SuggestionSpan_textUnderlineThickness, 0); mAutoCorrectionUnderlineColor = typedArray.getColor( com.android.internal.R.styleable.SuggestionSpan_textUnderlineColor, Color.BLACK); - } public SuggestionSpan(Parcel src) { diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 443acf6..3f793bb 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -441,6 +441,8 @@ public abstract class HardwareRenderer { } boolean mDirtyRegionsEnabled; + boolean mUpdateDirtyRegions; + final boolean mVsyncDisabled; final int mGlVersion; @@ -675,6 +677,12 @@ public abstract class HardwareRenderer { initCaches(); + enableDirtyRegions(); + + return mEglContext.getGL(); + } + + private void enableDirtyRegions() { // If mDirtyRegions is set, this means we have an EGL configuration // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set if (sDirtyRegions) { @@ -690,8 +698,6 @@ public abstract class HardwareRenderer { // configuration (see RENDER_DIRTY_REGIONS) mDirtyRegionsEnabled = GLES20Canvas.isBackBufferPreserved(); } - - return mEglContext.getGL(); } abstract void initCaches(); @@ -745,6 +751,9 @@ public abstract class HardwareRenderer { if (!createSurface(holder)) { return; } + + mUpdateDirtyRegions = true; + if (mCanvas != null) { setEnabled(true); } @@ -943,6 +952,10 @@ public abstract class HardwareRenderer { fallback(true); return SURFACE_STATE_ERROR; } else { + if (mUpdateDirtyRegions) { + enableDirtyRegions(); + mUpdateDirtyRegions = false; + } return SURFACE_STATE_UPDATED; } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 1102a47..0d34b90 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4190,10 +4190,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@inheritDoc} */ public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { - // The View is not attached to a window, 'visible' does not make sense, return false - if (mAttachInfo == null) return false; - - final RectF rect = mAttachInfo.mTmpTransformRect; + // It doesn't make a whole lot of sense to call this on a view that isn't attached, + // but for some simple tests it can be useful. If we don't have attach info this + // will allocate memory. + final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF(); rect.set(r); if (!child.hasIdentityMatrix()) { @@ -4207,7 +4207,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (offset != null) { if (!child.hasIdentityMatrix()) { - float[] position = mAttachInfo.mTmpTransformLocation; + float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation + : new float[2]; position[0] = offset.x; position[1] = offset.y; child.getMatrix().mapPoints(position); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7a9d82c..72966ef 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3257,8 +3257,9 @@ public final class ViewRootImpl extends Handler implements ViewParent, } // If the Control modifier is held, try to interpret the key as a shortcut. - if (event.getAction() == KeyEvent.ACTION_UP + if (event.getAction() == KeyEvent.ACTION_DOWN && event.isCtrlPressed() + && event.getRepeatCount() == 0 && !KeyEvent.isModifierKey(event.getKeyCode())) { if (mView.dispatchKeyShortcutEvent(event)) { finishInputEvent(q, true); diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index 48fe0df..24a3066 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -400,7 +400,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")"); if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { - if (mActiveStreamType == -1) { + if (mActiveStreamType != streamType) { reorderSliders(streamType); } onShowVolumeChanged(streamType, flags); diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index d0841dd..a99ac03 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -743,6 +743,9 @@ public abstract class Window { public void setFlags(int flags, int mask) { final WindowManager.LayoutParams attrs = getAttributes(); attrs.flags = (attrs.flags&~mask) | (flags&mask); + if ((mask&WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY) != 0) { + attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY; + } mForcedWindowFlags |= mask; if (mCallback != null) { mCallback.onWindowAttributesChanged(attrs); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e74fec6..c0eb65b 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -824,6 +824,16 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS = 0x00000004; /** + * This is set for a window that has explicitly specified its + * FLAG_NEEDS_MENU_KEY, so we know the value on this window is the + * appropriate one to use. If this is not set, we should look at + * windows behind it to determine the appropriate value. + * + * @hide + */ + public static final int PRIVATE_FLAG_SET_NEEDS_MENU_KEY = 0x00000008; + + /** * Control flags that are private to the platform. * @hide */ diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 7d729c6..c7cd662 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -238,6 +238,14 @@ public interface WindowManagerPolicy { public WindowManager.LayoutParams getAttrs(); /** + * Return whether this window needs the menu key shown. Must be called + * with window lock held, because it may need to traverse down through + * window list to determine the result. + * @param bottom The bottom-most window to consider when determining this. + */ + public boolean getNeedsMenuLw(WindowState bottom); + + /** * Retrieve the current system UI visibility flags associated with * this window. */ diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java index 1d66cbe..f6418ce 100644 --- a/core/java/android/view/textservice/SpellCheckerSession.java +++ b/core/java/android/view/textservice/SpellCheckerSession.java @@ -21,8 +21,11 @@ import com.android.internal.textservice.ISpellCheckerSessionListener; import com.android.internal.textservice.ITextServicesManager; import com.android.internal.textservice.ITextServicesSessionListener; +import android.os.Binder; import android.os.Handler; +import android.os.HandlerThread; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.util.Log; import android.view.textservice.SpellCheckerInfo; @@ -226,6 +229,8 @@ public class SpellCheckerSession { private boolean mOpened; private ISpellCheckerSession mISpellCheckerSession; + private HandlerThread mThread; + private Handler mAsyncHandler; public SpellCheckerSessionListenerImpl(Handler handler) { mOpened = false; @@ -237,6 +242,7 @@ public class SpellCheckerSession { public final TextInfo[] mTextInfos; public final int mSuggestionsLimit; public final boolean mSequentialWords; + public ISpellCheckerSession mSession; public SpellCheckerParams(int what, TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { mWhat = what; @@ -246,30 +252,100 @@ public class SpellCheckerSession { } } - private void processTask(SpellCheckerParams scp) { - switch (scp.mWhat) { - case TASK_CANCEL: - processCancel(); - break; - case TASK_GET_SUGGESTIONS_MULTIPLE: - processGetSuggestionsMultiple(scp); - break; - case TASK_CLOSE: - processClose(); - break; - case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE: - processGetSuggestionsMultipleForSentence(scp); - break; + private void processTask(ISpellCheckerSession session, SpellCheckerParams scp, + boolean async) { + if (async || mAsyncHandler == null) { + switch (scp.mWhat) { + case TASK_CANCEL: + if (DBG) { + Log.w(TAG, "Cancel spell checker tasks."); + } + try { + session.onCancel(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to cancel " + e); + } + break; + case TASK_GET_SUGGESTIONS_MULTIPLE: + if (DBG) { + Log.w(TAG, "Get suggestions from the spell checker."); + } + try { + session.onGetSuggestionsMultiple(scp.mTextInfos, + scp.mSuggestionsLimit, scp.mSequentialWords); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get suggestions " + e); + } + break; + case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE: + if (DBG) { + Log.w(TAG, "Get suggestions from the spell checker."); + } + if (scp.mTextInfos.length != 1) { + throw new IllegalArgumentException(); + } + try { + session.onGetSuggestionsMultipleForSentence( + scp.mTextInfos, scp.mSuggestionsLimit); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get suggestions " + e); + } + break; + case TASK_CLOSE: + if (DBG) { + Log.w(TAG, "Close spell checker tasks."); + } + try { + session.onClose(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to close " + e); + } + break; + } + } else { + // The interface is to a local object, so need to execute it + // asynchronously. + scp.mSession = session; + mAsyncHandler.sendMessage(Message.obtain(mAsyncHandler, 1, scp)); + } + + if (scp.mWhat == TASK_CLOSE) { + // If we are closing, we want to clean up our state now even + // if it is pending as an async operation. + synchronized (this) { + mISpellCheckerSession = null; + mHandler = null; + if (mThread != null) { + mThread.quit(); + } + mThread = null; + mAsyncHandler = null; + } } } public synchronized void onServiceConnected(ISpellCheckerSession session) { - mISpellCheckerSession = session; - mOpened = true; + synchronized (this) { + mISpellCheckerSession = session; + if (session.asBinder() instanceof Binder && mThread == null) { + // If this is a local object, we need to do our own threading + // to make sure we handle it asynchronously. + mThread = new HandlerThread("SpellCheckerSession", + Process.THREAD_PRIORITY_BACKGROUND); + mThread.start(); + mAsyncHandler = new Handler(mThread.getLooper()) { + @Override public void handleMessage(Message msg) { + SpellCheckerParams scp = (SpellCheckerParams)msg.obj; + processTask(scp.mSession, scp, true); + } + }; + } + mOpened = true; + } if (DBG) Log.d(TAG, "onServiceConnected - Success"); while (!mPendingTasks.isEmpty()) { - processTask(mPendingTasks.poll()); + processTask(session, mPendingTasks.poll(), false); } } @@ -310,105 +386,43 @@ public class SpellCheckerSession { return mOpened && mISpellCheckerSession == null; } - public boolean checkOpenConnection() { - if (mISpellCheckerSession != null) { - return true; - } - Log.e(TAG, "not connected to the spellchecker service."); - return false; - } - private void processOrEnqueueTask(SpellCheckerParams scp) { 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; + ISpellCheckerSession session; + synchronized (this) { + session = mISpellCheckerSession; + if (session == null) { + SpellCheckerParams closeTask = 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); + } + return; } - mPendingTasks.offer(scp); - if (closeTask != null) { - mPendingTasks.offer(closeTask); - } - } else { - processTask(scp); - } - } - - private void processCancel() { - if (!checkOpenConnection()) { - return; - } - if (DBG) { - Log.w(TAG, "Cancel spell checker tasks."); - } - try { - mISpellCheckerSession.onCancel(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to cancel " + e); - } - } - - private void processClose() { - if (!checkOpenConnection()) { - return; - } - if (DBG) { - Log.w(TAG, "Close spell checker tasks."); - } - try { - mISpellCheckerSession.onClose(); - mISpellCheckerSession = null; - mHandler = null; - } catch (RemoteException e) { - Log.e(TAG, "Failed to close " + e); - } - } - - private void processGetSuggestionsMultiple(SpellCheckerParams scp) { - if (!checkOpenConnection()) { - return; - } - if (DBG) { - Log.w(TAG, "Get suggestions from the spell checker."); - } - try { - mISpellCheckerSession.onGetSuggestionsMultiple( - scp.mTextInfos, scp.mSuggestionsLimit, scp.mSequentialWords); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get suggestions " + e); - } - } - - private void processGetSuggestionsMultipleForSentence(SpellCheckerParams scp) { - if (!checkOpenConnection()) { - return; - } - if (DBG) { - Log.w(TAG, "Get suggestions from the spell checker."); - } - if (scp.mTextInfos.length != 1) { - throw new IllegalArgumentException(); - } - try { - mISpellCheckerSession.onGetSuggestionsMultipleForSentence( - scp.mTextInfos, scp.mSuggestionsLimit); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get suggestions " + e); } + processTask(session, scp, false); } @Override public void onGetSuggestions(SuggestionsInfo[] results) { - mHandler.sendMessage(Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE, results)); + synchronized (this) { + if (mHandler != null) { + mHandler.sendMessage(Message.obtain(mHandler, + MSG_ON_GET_SUGGESTION_MULTIPLE, results)); + } + } } @Override diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index c194559..0da867f 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -410,6 +410,7 @@ class BrowserFrame extends Handler { mCommitted = false; // remove pending draw to block update until mFirstLayoutDone is // set to true in didFirstLayout() + mWebViewCore.clearContent(); mWebViewCore.removeMessages(WebViewCore.EventHub.WEBKIT_DRAW); } } @@ -1180,11 +1181,19 @@ class BrowserFrame extends Handler { @Override public void proceed() { SslCertLookupTable.getInstance().setIsAllowed(sslError); - nativeSslCertErrorProceed(handle); + post(new Runnable() { + public void run() { + nativeSslCertErrorProceed(handle); + } + }); } @Override public void cancel() { - nativeSslCertErrorCancel(handle, certError); + post(new Runnable() { + public void run() { + nativeSslCertErrorCancel(handle, certError); + } + }); } }; mCallbackProxy.onReceivedSslError(handler, sslError); diff --git a/core/java/android/webkit/ClientCertRequestHandler.java b/core/java/android/webkit/ClientCertRequestHandler.java index 3a71e7e..f862613 100644 --- a/core/java/android/webkit/ClientCertRequestHandler.java +++ b/core/java/android/webkit/ClientCertRequestHandler.java @@ -16,6 +16,7 @@ package android.webkit; +import android.os.Handler; import java.security.PrivateKey; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; @@ -29,7 +30,7 @@ import org.apache.harmony.xnet.provider.jsse.NativeCrypto; * * @hide */ -public final class ClientCertRequestHandler { +public final class ClientCertRequestHandler extends Handler { private final BrowserFrame mBrowserFrame; private final int mHandle; @@ -49,23 +50,35 @@ public final class ClientCertRequestHandler { * Proceed with the specified private key and client certificate chain. */ public void proceed(PrivateKey privateKey, X509Certificate[] chain) { - byte[] privateKeyBytes = privateKey.getEncoded(); - byte[][] chainBytes; + final byte[] privateKeyBytes = privateKey.getEncoded(); + final byte[][] chainBytes; try { chainBytes = NativeCrypto.encodeCertificates(chain); + mTable.Allow(mHostAndPort, privateKeyBytes, chainBytes); + post(new Runnable() { + public void run() { + mBrowserFrame.nativeSslClientCert(mHandle, privateKeyBytes, chainBytes); + } + }); } catch (CertificateEncodingException e) { - mBrowserFrame.nativeSslClientCert(mHandle, null, null); - return; + post(new Runnable() { + public void run() { + mBrowserFrame.nativeSslClientCert(mHandle, null, null); + return; + } + }); } - mTable.Allow(mHostAndPort, privateKeyBytes, chainBytes); - mBrowserFrame.nativeSslClientCert(mHandle, privateKeyBytes, chainBytes); } /** * Igore the request for now, the user may be prompted again. */ public void ignore() { - mBrowserFrame.nativeSslClientCert(mHandle, null, null); + post(new Runnable() { + public void run() { + mBrowserFrame.nativeSslClientCert(mHandle, null, null); + } + }); } /** @@ -73,6 +86,10 @@ public final class ClientCertRequestHandler { */ public void cancel() { mTable.Deny(mHostAndPort); - mBrowserFrame.nativeSslClientCert(mHandle, null, null); + post(new Runnable() { + public void run() { + mBrowserFrame.nativeSslClientCert(mHandle, null, null); + } + }); } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index a284a17..ac8693d 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -4538,7 +4538,7 @@ public class WebView extends AbsoluteLayout boolean isPictureAfterFirstLayout, boolean registerPageSwapCallback) { if (mNativeClass == 0) return; - nativeSetBaseLayer(layer, invalRegion, showVisualIndicator, + nativeSetBaseLayer(mNativeClass, layer, invalRegion, showVisualIndicator, isPictureAfterFirstLayout, registerPageSwapCallback); if (mHTML5VideoViewProxy != null) { mHTML5VideoViewProxy.setBaseLayer(layer); @@ -9493,7 +9493,12 @@ public class WebView extends AbsoluteLayout /** @hide call pageSwapCallback upon next page swap */ protected void registerPageSwapCallback() { - nativeRegisterPageSwapCallback(); + nativeRegisterPageSwapCallback(mNativeClass); + } + + /** @hide discard all textures from tiles */ + protected void discardAllTextures() { + nativeDiscardAllTextures(); } /** @@ -9643,7 +9648,8 @@ public class WebView extends AbsoluteLayout private native void nativeSetFindIsEmpty(); private native void nativeSetFindIsUp(boolean isUp); private native void nativeSetHeightCanMeasure(boolean measure); - private native void nativeSetBaseLayer(int layer, Region invalRegion, + private native void nativeSetBaseLayer(int nativeInstance, + int layer, Region invalRegion, boolean showVisualIndicator, boolean isPictureAfterFirstLayout, boolean registerPageSwapCallback); private native int nativeGetBaseLayer(); @@ -9657,7 +9663,8 @@ public class WebView extends AbsoluteLayout private native void nativeStopGL(); private native Rect nativeSubtractLayers(Rect content); private native int nativeTextGeneration(); - private native void nativeRegisterPageSwapCallback(); + private native void nativeRegisterPageSwapCallback(int nativeInstance); + private native void nativeDiscardAllTextures(); private native void nativeTileProfilingStart(); private native float nativeTileProfilingStop(); private native void nativeTileProfilingClear(); diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 14da23e..d99e264 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -497,6 +497,13 @@ public final class WebViewCore { message.sendToTarget(); } + /** + * Clear the picture set. To be called only on the WebCore thread. + */ + /* package */ void clearContent() { + nativeClearContent(); + } + //------------------------------------------------------------------------- // JNI methods //------------------------------------------------------------------------- @@ -1560,7 +1567,7 @@ public final class WebViewCore { // Clear the view so that onDraw() will draw nothing // but white background // (See public method WebView.clearView) - nativeClearContent(); + clearContent(); break; case MESSAGE_RELAY: @@ -2865,6 +2872,6 @@ public final class WebViewCore { private native ArrayList<Rect> nativeGetTouchHighlightRects(int x, int y, int slop); - private native void nativeAutoFillForm(int queryId); - private native void nativeScrollLayer(int layer, Rect rect); + private native void nativeAutoFillForm(int queryId); + private native void nativeScrollLayer(int layer, Rect rect); } diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 9d541e0..7d0f98e 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -774,6 +774,7 @@ public class NumberPicker extends LinearLayout { mBeginEditOnUpEvent = false; mAdjustScrollerOnUpEvent = true; if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) { + mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA); boolean scrollersFinished = mFlingScroller.isFinished() && mAdjustScroller.isFinished(); if (!scrollersFinished) { @@ -1608,23 +1609,11 @@ public class NumberPicker extends LinearLayout { */ private void fling(int velocityY) { mPreviousScrollerY = 0; - Scroller flingScroller = mFlingScroller; - if (mWrapSelectorWheel) { - if (velocityY > 0) { - flingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE); - } else { - flingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE); - } + if (velocityY > 0) { + mFlingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE); } else { - if (velocityY > 0) { - int maxY = mTextSize * (mValue - mMinValue); - flingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, maxY); - } else { - int startY = mTextSize * (mMaxValue - mValue); - int maxY = startY; - flingScroller.fling(0, startY, 0, velocityY, 0, 0, 0, maxY); - } + mFlingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE); } invalidate(); diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index 4bd7165..a106159 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -19,6 +19,7 @@ package android.widget; import android.content.Context; import android.text.Editable; import android.text.Selection; +import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.method.WordIterator; import android.text.style.SpellCheckSpan; @@ -44,14 +45,15 @@ public class SpellChecker implements SpellCheckerSessionListener { // No more than this number of words will be parsed on each iteration to ensure a minimum // lock of the UI thread - public static final int MAX_NUMBER_OF_WORDS = 10; + public static final int MAX_NUMBER_OF_WORDS = 50; - // Safe estimate, will ensure that the interval below usually does not have to be updated - public static final int AVERAGE_WORD_LENGTH = 10; + // Rough estimate, such that the word iterator interval usually does not need to be shifted + public static final int AVERAGE_WORD_LENGTH = 7; // When parsing, use a character window of that size. Will be shifted if needed public static final int WORD_ITERATOR_INTERVAL = AVERAGE_WORD_LENGTH * MAX_NUMBER_OF_WORDS; + // Pause between each spell check to keep the UI smooth private final static int SPELL_PAUSE_DURATION = 400; // milliseconds private final TextView mTextView; @@ -74,6 +76,14 @@ public class SpellChecker implements SpellCheckerSessionListener { private Locale mCurrentLocale; + // Shared by all SpellParsers. Cannot be shared with TextView since it may be used + // concurrently due to the asynchronous nature of onGetSuggestions. + private WordIterator mWordIterator; + + private TextServicesManager mTextServicesManager; + + private Runnable mSpellRunnable; + public SpellChecker(TextView textView) { mTextView = textView; @@ -87,19 +97,19 @@ public class SpellChecker implements SpellCheckerSessionListener { mCookie = hashCode(); } - private void setLocale(Locale locale) { + private void resetSession() { closeSession(); - final TextServicesManager textServicesManager = (TextServicesManager) - mTextView.getContext().getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); - if (!textServicesManager.isSpellCheckerEnabled()) { + + mTextServicesManager = (TextServicesManager) mTextView.getContext(). + getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); + if (!mTextServicesManager.isSpellCheckerEnabled()) { mSpellCheckerSession = null; } else { - mSpellCheckerSession = textServicesManager.newSpellCheckerSession( + mSpellCheckerSession = mTextServicesManager.newSpellCheckerSession( null /* Bundle not currently used by the textServicesManager */, - locale, this, + mCurrentLocale, this, false /* means any available languages from current spell checker */); } - mCurrentLocale = locale; // Restore SpellCheckSpans in pool for (int i = 0; i < mLength; i++) { @@ -108,9 +118,19 @@ public class SpellChecker implements SpellCheckerSessionListener { } mLength = 0; - mSpellParsers = new SpellParser[0]; + // Remove existing misspelled SuggestionSpans + mTextView.removeMisspelledSpans((Editable) mTextView.getText()); + } - // This class is the global listener for locale change: warn other locale-aware objects + private void setLocale(Locale locale) { + mCurrentLocale = locale; + + resetSession(); + + // Change SpellParsers' wordIterator locale + mWordIterator = new WordIterator(locale); + + // This class is the listener for locale change: warn other locale-aware objects mTextView.onLocaleChanged(); } @@ -127,14 +147,14 @@ public class SpellChecker implements SpellCheckerSessionListener { mSpellCheckerSession.close(); } - stopAllSpellParsers(); - } - - private void stopAllSpellParsers() { final int length = mSpellParsers.length; for (int i = 0; i < length; i++) { mSpellParsers[i].stop(); } + + if (mSpellRunnable != null) { + mTextView.removeCallbacks(mSpellRunnable); + } } private int nextSpellCheckSpanIndex() { @@ -184,6 +204,12 @@ public class SpellChecker implements SpellCheckerSessionListener { // Re-check the entire text start = 0; end = mTextView.getText().length(); + } else { + final boolean spellCheckerActivated = mTextServicesManager.isSpellCheckerEnabled(); + if (isSessionActive() != spellCheckerActivated) { + // Spell checker has been turned of or off since last spellCheck + resetSession(); + } } if (!isSessionActive()) return; @@ -192,7 +218,7 @@ public class SpellChecker implements SpellCheckerSessionListener { final int length = mSpellParsers.length; for (int i = 0; i < length; i++) { final SpellParser spellParser = mSpellParsers[i]; - if (!spellParser.isParsing()) { + if (spellParser.isFinished()) { spellParser.init(start, end); spellParser.parse(); return; @@ -220,7 +246,6 @@ public class SpellChecker implements SpellCheckerSessionListener { TextInfo[] textInfos = new TextInfo[mLength]; int textInfosCount = 0; - final String text = editable.toString(); for (int i = 0; i < mLength; i++) { final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i]; if (spellCheckSpan.isSpellCheckInProgress()) continue; @@ -230,7 +255,9 @@ public class SpellChecker implements SpellCheckerSessionListener { // Do not check this word if the user is currently editing it if (start >= 0 && end > start && (selectionEnd < start || selectionStart > end)) { - final String word = text.substring(start, end); + final String word = (editable instanceof SpannableStringBuilder) ? + ((SpannableStringBuilder) editable).substring(start, end) : + editable.subSequence(start, end).toString(); spellCheckSpan.setSpellCheckInProgress(true); textInfos[textInfosCount++] = new TextInfo(word, mCookie, mIds[i]); } @@ -283,18 +310,29 @@ public class SpellChecker implements SpellCheckerSessionListener { } } - mTextView.postDelayed(new Runnable() { - @Override - public void run() { - final int length = mSpellParsers.length; - for (int i = 0; i < length; i++) { - final SpellParser spellParser = mSpellParsers[i]; - if (spellParser.isParsing()) { - spellParser.parse(); + scheduleNewSpellCheck(); + } + + private void scheduleNewSpellCheck() { + if (mSpellRunnable == null) { + mSpellRunnable = new Runnable() { + @Override + public void run() { + final int length = mSpellParsers.length; + for (int i = 0; i < length; i++) { + final SpellParser spellParser = mSpellParsers[i]; + if (!spellParser.isFinished()) { + spellParser.parse(); + break; // run one spell parser at a time to bound running time + } } } - } - }, SPELL_PAUSE_DURATION); + }; + } else { + mTextView.removeCallbacks(mSpellRunnable); + } + + mTextView.postDelayed(mSpellRunnable, SPELL_PAUSE_DURATION); } private void createMisspelledSuggestionSpan(Editable editable, SuggestionsInfo suggestionsInfo, @@ -359,7 +397,7 @@ public class SpellChecker implements SpellCheckerSessionListener { SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED); editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - mTextView.invalidateRegion(start, end); + mTextView.invalidateRegion(start, end, false /* No cursor involved */); } private class SpellParser { @@ -369,12 +407,12 @@ public class SpellChecker implements SpellCheckerSessionListener { setRangeSpan((Editable) mTextView.getText(), start, end); } - public void stop() { - removeRangeSpan((Editable) mTextView.getText()); + public boolean isFinished() { + return ((Editable) mTextView.getText()).getSpanStart(mRange) < 0; } - public boolean isParsing() { - return ((Editable) mTextView.getText()).getSpanStart(mRange) >= 0; + public void stop() { + removeRangeSpan((Editable) mTextView.getText()); } private void setRangeSpan(Editable editable, int start, int end) { @@ -391,20 +429,19 @@ public class SpellChecker implements SpellCheckerSessionListener { final int start = editable.getSpanStart(mRange); final int end = editable.getSpanEnd(mRange); - final WordIterator wordIterator = mTextView.getWordIterator(); int wordIteratorWindowEnd = Math.min(end, start + WORD_ITERATOR_INTERVAL); - wordIterator.setCharSequence(editable, start, wordIteratorWindowEnd); + mWordIterator.setCharSequence(editable, start, wordIteratorWindowEnd); // Move back to the beginning of the current word, if any - int wordStart = wordIterator.preceding(start); + int wordStart = mWordIterator.preceding(start); int wordEnd; if (wordStart == BreakIterator.DONE) { - wordEnd = wordIterator.following(start); + wordEnd = mWordIterator.following(start); if (wordEnd != BreakIterator.DONE) { - wordStart = wordIterator.getBeginning(wordEnd); + wordStart = mWordIterator.getBeginning(wordEnd); } } else { - wordEnd = wordIterator.getEnd(wordStart); + wordEnd = mWordIterator.getEnd(wordStart); } if (wordEnd == BreakIterator.DONE) { removeRangeSpan(editable); @@ -470,15 +507,15 @@ public class SpellChecker implements SpellCheckerSessionListener { // iterate word by word int originalWordEnd = wordEnd; - wordEnd = wordIterator.following(wordEnd); + wordEnd = mWordIterator.following(wordEnd); if ((wordIteratorWindowEnd < end) && (wordEnd == BreakIterator.DONE || wordEnd >= wordIteratorWindowEnd)) { wordIteratorWindowEnd = Math.min(end, originalWordEnd + WORD_ITERATOR_INTERVAL); - wordIterator.setCharSequence(editable, originalWordEnd, wordIteratorWindowEnd); - wordEnd = wordIterator.following(originalWordEnd); + mWordIterator.setCharSequence(editable, originalWordEnd, wordIteratorWindowEnd); + wordEnd = mWordIterator.following(originalWordEnd); } if (wordEnd == BreakIterator.DONE) break; - wordStart = wordIterator.getBeginning(wordEnd); + wordStart = mWordIterator.getBeginning(wordEnd); if (wordStart == BreakIterator.DONE) { break; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index babd8e7..a178087 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -339,7 +339,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mCursorDrawableRes; private final Drawable[] mCursorDrawable = new Drawable[2]; - private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 + private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 (split) private Drawable mSelectHandleLeft; private Drawable mSelectHandleRight; @@ -3210,7 +3210,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean needEditableForNotification = false; - boolean startSpellCheck = false; if (mListeners != null && mListeners.size() != 0) { needEditableForNotification = true; @@ -3222,7 +3221,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setFilters(t, mFilters); InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) imm.restartInput(this); - startSpellCheck = true; } else if (type == BufferType.SPANNABLE || mMovement != null) { text = mSpannableFactory.newSpannable(text); } else if (!(text instanceof CharWrapper)) { @@ -3311,11 +3309,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener sendOnTextChanged(text, 0, oldlen, textLength); onTextChanged(text, 0, oldlen, textLength); - if (startSpellCheck && mSpellChecker != null) { - // This view has to have been previously attached for mSpellChecker to exist - updateSpellCheckSpans(0, textLength); - } - if (needEditableForNotification) { sendAfterTextChanged((Editable) text); } @@ -4329,7 +4322,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (a >= 0 || b >= 0 || c >= 0) { int start = Math.min(Math.min(a, b), c); int end = Math.max(Math.max(a, b), c); - invalidateRegion(start, end); + invalidateRegion(start, end, true /* Also invalidates blinking cursor */); } } @@ -4338,7 +4331,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @hide */ - void invalidateRegion(int start, int end) { + void invalidateRegion(int start, int end, boolean invalidateCursor) { if (mLayout == null) { invalidate(); } else { @@ -4364,11 +4357,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int bottom = mLayout.getLineBottom(lineEnd); + if (invalidateCursor) { + for (int i = 0; i < mCursorCount; i++) { + Rect bounds = mCursorDrawable[i].getBounds(); + top = Math.min(top, bounds.top); + bottom = Math.max(bottom, bounds.bottom); + } + } + final int compoundPaddingLeft = getCompoundPaddingLeft(); final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true); int left, right; - if (lineStart == lineEnd) { + if (lineStart == lineEnd && !invalidateCursor) { left = (int) mLayout.getPrimaryHorizontal(start); right = (int) (mLayout.getPrimaryHorizontal(end) + 1.0); left += compoundPaddingLeft; @@ -4482,8 +4483,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Resolve drawables as the layout direction has been resolved resolveDrawables(); - - updateSpellCheckSpans(0, mText.length()); + + updateSpellCheckSpans(0, mText.length(), true /* create the spell checker if needed */); } @Override @@ -5517,7 +5518,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * call performClick(), but that won't do anything in * this case.) */ - if (hasOnClickListeners()) { + if (!hasOnClickListeners()) { if (mMovement != null && mText instanceof Editable && mLayout != null && onCheckIsTextEditor()) { InputMethodManager imm = InputMethodManager.peekInstance(); @@ -5554,7 +5555,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * call performClick(), but that won't do anything in * this case.) */ - if (hasOnClickListeners()) { + if (!hasOnClickListeners()) { View v = focusSearch(FOCUS_DOWN); if (v != null) { @@ -7636,7 +7637,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - updateSpellCheckSpans(start, start + after); + updateSpellCheckSpans(start, start + after, false); // Hide the controllers as soon as text is modified (typing, procedural...) // We do not hide the span controllers, since they can be added when a new text is @@ -7794,17 +7795,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - if (newStart < 0 && what instanceof SpellCheckSpan) { - getSpellChecker().removeSpellCheckSpan((SpellCheckSpan) what); + if (mSpellChecker != null && newStart < 0 && what instanceof SpellCheckSpan) { + mSpellChecker.removeSpellCheckSpan((SpellCheckSpan) what); } } /** * Create new SpellCheckSpans on the modified region. */ - private void updateSpellCheckSpans(int start, int end) { - if (isTextEditable() && isSuggestionsEnabled()) { - getSpellChecker().spellCheck(start, end); + private void updateSpellCheckSpans(int start, int end, boolean createSpellChecker) { + if (isTextEditable() && isSuggestionsEnabled() && !(this instanceof ExtractEditText)) { + if (mSpellChecker == null && createSpellChecker) { + mSpellChecker = new SpellChecker(this); + } + if (mSpellChecker != null) { + mSpellChecker.spellCheck(start, end); + } } } @@ -8936,7 +8942,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } void onLocaleChanged() { - removeMisspelledSpans((Editable) mText); // Will be re-created on demand in getWordIterator with the proper new locale mWordIterator = null; } @@ -8976,13 +8981,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return packRangeInLong(offset, offset); } - private SpellChecker getSpellChecker() { - if (mSpellChecker == null) { - mSpellChecker = new SpellChecker(this); - } - return mSpellChecker; - } - private long getLastTouchOffsets() { SelectionModifierCursorController selectionController = getSelectionController(); final int minOffset = selectionController.getMinTouchOffset(); @@ -9835,7 +9833,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener highlightTextDifferences(mSuggestionInfos[i], spanUnionStart, spanUnionEnd); } - // Add to dictionary item is there a span with the misspelled flag + // Add to dictionary item if there is a span with the misspelled flag if (misspelledSpan != null) { final int misspelledStart = spannable.getSpanStart(misspelledSpan); final int misspelledEnd = spannable.getSpanEnd(misspelledSpan); @@ -9921,7 +9919,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int spanStart = editable.getSpanStart(suggestionInfo.suggestionSpan); final int spanEnd = editable.getSpanEnd(suggestionInfo.suggestionSpan); - if (spanStart < 0 || spanEnd < 0) { + if (spanStart < 0 || spanEnd <= spanStart) { // Span has been removed hide(); return; @@ -9937,7 +9935,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // There is no way to know if the word was indeed added. Re-check. // TODO The ExtractEditText should remove the span in the original text instead editable.removeSpan(suggestionInfo.suggestionSpan); - updateSpellCheckSpans(spanStart, spanEnd); + updateSpellCheckSpans(spanStart, spanEnd, false); } else { // SuggestionSpans are removed by replace: save them before SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd, @@ -9989,14 +9987,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // way to assign them a valid range after replacement if (suggestionSpansStarts[i] <= spanStart && suggestionSpansEnds[i] >= spanEnd) { - // TODO The ExtractEditText should restore these spans in the original text - editable.setSpan(suggestionSpans[i], suggestionSpansStarts[i], + setSpan_internal(suggestionSpans[i], suggestionSpansStarts[i], suggestionSpansEnds[i] + lengthDifference, suggestionSpansFlags[i]); } } // Move cursor at the end of the replaced word - Selection.setSelection(editable, spanEnd + lengthDifference); + final int newCursorPosition = spanEnd + lengthDifference; + setCursorPosition_internal(newCursorPosition, newCursorPosition); } hide(); @@ -11469,6 +11467,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ((Editable) mText).replace(start, end, text); } + /** + * Sets a span on the specified range of text + * @hide + */ + protected void setSpan_internal(Object span, int start, int end, int flags) { + ((Editable) mText).setSpan(span, start, end, flags); + } + + /** + * Moves the cursor to the specified offset position in text + * @hide + */ + protected void setCursorPosition_internal(int start, int end) { + Selection.setSelection(((Editable) mText), start, end); + } + @ViewDebug.ExportedProperty(category = "text") private CharSequence mText; private CharSequence mTransformed; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index fec4cbc..86118b1 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -19,6 +19,7 @@ package com.android.internal.os; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.UID_ALL; import static android.text.format.DateUtils.SECOND_IN_MILLIS; +import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; @@ -35,6 +36,7 @@ import android.os.ParcelFormatException; import android.os.Parcelable; import android.os.Process; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.WorkSource; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -5713,11 +5715,17 @@ public final class BatteryStatsImpl extends BatteryStats { synchronized (this) { if (mNetworkSummaryCache == null || mNetworkSummaryCache.getElapsedRealtimeAge() > SECOND_IN_MILLIS) { - try { - mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummary(); - } catch (IllegalStateException e) { - // log problem and return empty object - Log.wtf(TAG, "problem reading network stats", e); + mNetworkSummaryCache = null; + + if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { + try { + mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummary(); + } catch (IllegalStateException e) { + Log.wtf(TAG, "problem reading network stats", e); + } + } + + if (mNetworkSummaryCache == null) { mNetworkSummaryCache = new NetworkStats(SystemClock.elapsedRealtime(), 0); } } @@ -5730,12 +5738,18 @@ public final class BatteryStatsImpl extends BatteryStats { synchronized (this) { if (mNetworkDetailCache == null || mNetworkDetailCache.getElapsedRealtimeAge() > SECOND_IN_MILLIS) { - try { - mNetworkDetailCache = mNetworkStatsFactory - .readNetworkStatsDetail().groupedByUid(); - } catch (IllegalStateException e) { - // log problem and return empty object - Log.wtf(TAG, "problem reading network stats", e); + mNetworkDetailCache = null; + + if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { + try { + mNetworkDetailCache = mNetworkStatsFactory + .readNetworkStatsDetail().groupedByUid(); + } catch (IllegalStateException e) { + Log.wtf(TAG, "problem reading network stats", e); + } + } + + if (mNetworkDetailCache == null) { mNetworkDetailCache = new NetworkStats(SystemClock.elapsedRealtime(), 0); } } diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java index 8445ad1..c77992d 100644 --- a/core/java/com/android/server/NetworkManagementSocketTagger.java +++ b/core/java/com/android/server/NetworkManagementSocketTagger.java @@ -80,14 +80,15 @@ public final class NetworkManagementSocketTagger extends SocketTagger { } private void tagSocketFd(FileDescriptor fd, int tag, int uid) { - int errno; if (tag == -1 && uid == -1) return; - errno = native_tagSocketFd(fd, tag, uid); - if (errno < 0) { - Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", " - + tag + ", " + - + uid + ") failed with errno" + errno); + if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { + final int errno = native_tagSocketFd(fd, tag, uid); + if (errno < 0) { + Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", " + + tag + ", " + + + uid + ") failed with errno" + errno); + } } } @@ -101,12 +102,13 @@ public final class NetworkManagementSocketTagger extends SocketTagger { private void unTagSocketFd(FileDescriptor fd) { final SocketTags options = threadSocketTags.get(); - int errno; if (options.statsTag == -1 && options.statsUid == -1) return; - errno = native_untagSocketFd(fd); - if (errno < 0) { - Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno); + if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { + final int errno = native_untagSocketFd(fd); + if (errno < 0) { + Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno); + } } } @@ -116,16 +118,21 @@ public final class NetworkManagementSocketTagger extends SocketTagger { } public static void setKernelCounterSet(int uid, int counterSet) { - int errno = native_setCounterSet(counterSet, uid); - if (errno < 0) { - Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno " + errno); + if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { + final int errno = native_setCounterSet(counterSet, uid); + if (errno < 0) { + Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno " + + errno); + } } } public static void resetKernelUidStats(int uid) { - int errno = native_deleteTagData(0, uid); - if (errno < 0) { - Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno); + if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { + int errno = native_deleteTagData(0, uid); + if (errno < 0) { + Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno); + } } } |