diff options
233 files changed, 4889 insertions, 1936 deletions
@@ -71,6 +71,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ core/java/android/app/INotificationManager.aidl \ + core/java/android/app/IProcessObserver.aidl \ core/java/android/app/ISearchManager.aidl \ core/java/android/app/ISearchManagerCallback.aidl \ core/java/android/app/IServiceConnection.aidl \ @@ -113,6 +114,7 @@ LOCAL_SRC_FILES += \ core/java/android/nfc/ILlcpConnectionlessSocket.aidl \ core/java/android/nfc/ILlcpServiceSocket.aidl \ core/java/android/nfc/ILlcpSocket.aidl \ + core/java/android/nfc/INdefPushCallback.aidl \ core/java/android/nfc/INfcAdapter.aidl \ core/java/android/nfc/INfcAdapterExtras.aidl \ core/java/android/nfc/INfcTag.aidl \ @@ -160,6 +162,7 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/view/IInputMethodSession.aidl \ core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \ core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \ + keystore/java/android/security/IKeyChainAliasResponse.aidl \ keystore/java/android/security/IKeyChainService.aidl \ location/java/android/location/ICountryDetector.aidl \ location/java/android/location/ICountryListener.aidl \ @@ -361,7 +364,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \ -werror -hide 113 \ -overview $(LOCAL_PATH)/core/java/overview.html -framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= $(call intermediates-dir-for,JAVA_LIBRARIES,framework) +framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= $(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON) framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \ frameworks/base/docs/knowntags.txt @@ -584,7 +587,7 @@ LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES) framework LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS) LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH) LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR) -LOCAL_ADDITIONAL_JAVA_DIR:=$(call intermediates-dir-for,JAVA_LIBRARIES,framework) +LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR) LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) LOCAL_MODULE := hidden diff --git a/api/current.txt b/api/current.txt index 7d87425..6854965 100644 --- a/api/current.txt +++ b/api/current.txt @@ -866,6 +866,7 @@ package android { field public static final int subtitleTextStyle = 16843513; // 0x10102f9 field public static final int suggestActionMsg = 16843228; // 0x10101dc field public static final int suggestActionMsgColumn = 16843229; // 0x10101dd + field public static final int suggestionsEnabled = 16843630; // 0x101036e field public static final int summary = 16843241; // 0x10101e9 field public static final int summaryColumn = 16843426; // 0x10102a2 field public static final int summaryOff = 16843248; // 0x10101f0 @@ -1977,6 +1978,7 @@ package android.animation { method public boolean isRunning(); method public void removeChild(android.view.ViewGroup, android.view.View); method public void removeTransitionListener(android.animation.LayoutTransition.TransitionListener); + method public void setAnimateParentHierarchy(boolean); method public void setAnimator(int, android.animation.Animator); method public void setDuration(long); method public void setDuration(int, long); @@ -6003,6 +6005,7 @@ package android.content.res { field public static final int UI_MODE_TYPE_DESK = 2; // 0x2 field public static final int UI_MODE_TYPE_MASK = 15; // 0xf field public static final int UI_MODE_TYPE_NORMAL = 1; // 0x1 + field public static final int UI_MODE_TYPE_TELEVISION = 4; // 0x4 field public static final int UI_MODE_TYPE_UNDEFINED = 0; // 0x0 field public float fontScale; field public int hardKeyboardHidden; @@ -9684,7 +9687,8 @@ package android.media { method public void unloadSoundEffects(); method public void unregisterMediaButtonEventReceiver(android.content.ComponentName); field public static final java.lang.String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY"; - field public static final java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED"; + field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED"; + field public static final java.lang.String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED"; field public static final int ADJUST_LOWER = -1; // 0xffffffff field public static final int ADJUST_RAISE = 1; // 0x1 field public static final int ADJUST_SAME = 0; // 0x0 @@ -9697,6 +9701,7 @@ package android.media { field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0 field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1 field public static final java.lang.String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE"; + field public static final java.lang.String EXTRA_SCO_AUDIO_PREVIOUS_STATE = "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"; field public static final java.lang.String EXTRA_SCO_AUDIO_STATE = "android.media.extra.SCO_AUDIO_STATE"; field public static final java.lang.String EXTRA_VIBRATE_SETTING = "android.media.EXTRA_VIBRATE_SETTING"; field public static final java.lang.String EXTRA_VIBRATE_TYPE = "android.media.EXTRA_VIBRATE_TYPE"; @@ -9733,6 +9738,7 @@ package android.media { field public static final deprecated int ROUTE_HEADSET = 8; // 0x8 field public static final deprecated int ROUTE_SPEAKER = 2; // 0x2 field public static final int SCO_AUDIO_STATE_CONNECTED = 1; // 0x1 + field public static final int SCO_AUDIO_STATE_CONNECTING = 2; // 0x2 field public static final int SCO_AUDIO_STATE_DISCONNECTED = 0; // 0x0 field public static final int SCO_AUDIO_STATE_ERROR = -1; // 0xffffffff field public static final int STREAM_ALARM = 4; // 0x4 @@ -10058,6 +10064,7 @@ package android.media { method public void setOnSeekCompleteListener(android.media.MediaPlayer.OnSeekCompleteListener); method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener); method public void setScreenOnWhilePlaying(boolean); + method public void setTexture(android.graphics.SurfaceTexture); method public void setVolume(float, float); method public void setWakeMode(android.content.Context, int); method public void start() throws java.lang.IllegalStateException; @@ -11640,6 +11647,7 @@ package android.nfc { method public void disableForegroundNdefPush(android.app.Activity); method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], java.lang.String[][]); method public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage); + method public void enableForegroundNdefPush(android.app.Activity, android.nfc.NfcAdapter.NdefPushCallback); method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context); method public static deprecated android.nfc.NfcAdapter getDefaultAdapter(); method public boolean isEnabled(); @@ -11651,6 +11659,11 @@ package android.nfc { field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG"; } + public static abstract interface NfcAdapter.NdefPushCallback { + method public abstract android.nfc.NdefMessage createMessage(); + method public abstract void onMessagePushed(); + } + public final class NfcManager { method public android.nfc.NfcAdapter getDefaultAdapter(); } @@ -13923,12 +13936,14 @@ package android.os { public class ParcelFileDescriptor implements android.os.Parcelable { ctor public ParcelFileDescriptor(android.os.ParcelFileDescriptor); + method public static android.os.ParcelFileDescriptor adoptFd(int); method public void close() throws java.io.IOException; method public static android.os.ParcelFileDescriptor[] createPipe() throws java.io.IOException; method public int describeContents(); method public int detachFd(); method public static android.os.ParcelFileDescriptor dup(java.io.FileDescriptor) throws java.io.IOException; method public static android.os.ParcelFileDescriptor fromDatagramSocket(java.net.DatagramSocket); + method public static android.os.ParcelFileDescriptor fromFd(int) throws java.io.IOException; method public static android.os.ParcelFileDescriptor fromSocket(java.net.Socket); method public int getFd(); method public java.io.FileDescriptor getFileDescriptor(); @@ -21430,6 +21445,8 @@ package android.view { method public void setScaleY(float); method public void setScrollBarStyle(int); method public void setScrollContainer(boolean); + method public void setScrollX(int); + method public void setScrollY(int); method public void setScrollbarFadingEnabled(boolean); method public void setSelected(boolean); method public void setSoundEffectsEnabled(boolean); @@ -21847,6 +21864,9 @@ package android.view { public class ViewPropertyAnimator { method public android.view.ViewPropertyAnimator alpha(float); method public android.view.ViewPropertyAnimator alphaBy(float); + method public void cancel(); + method public long getDuration(); + method public long getStartDelay(); method public android.view.ViewPropertyAnimator rotation(float); method public android.view.ViewPropertyAnimator rotationBy(float); method public android.view.ViewPropertyAnimator rotationX(float); @@ -21860,6 +21880,8 @@ package android.view { method public android.view.ViewPropertyAnimator setDuration(long); method public android.view.ViewPropertyAnimator setInterpolator(android.animation.TimeInterpolator); method public android.view.ViewPropertyAnimator setListener(android.animation.Animator.AnimatorListener); + method public android.view.ViewPropertyAnimator setStartDelay(long); + method public void start(); method public android.view.ViewPropertyAnimator translationX(float); method public android.view.ViewPropertyAnimator translationXBy(float); method public android.view.ViewPropertyAnimator translationY(float); @@ -25204,6 +25226,7 @@ package android.widget { method public final android.content.res.ColorStateList getLinkTextColors(); method public final boolean getLinksClickable(); method public final android.text.method.MovementMethod getMovementMethod(); + method public int getOffsetForPosition(float, float); method public android.text.TextPaint getPaint(); method public int getPaintFlags(); method public java.lang.String getPrivateImeOptions(); @@ -25224,6 +25247,7 @@ package android.widget { method public android.text.style.URLSpan[] getUrls(); method public boolean hasSelection(); method public boolean isInputMethodTarget(); + method public boolean isSuggestionsEnabled(); method public boolean isTextSelectable(); method public int length(); method public boolean moveCursorToVisibleOffset(); @@ -25295,6 +25319,7 @@ package android.widget { method public void setSingleLine(); method public void setSingleLine(boolean); method public final void setSpannableFactory(android.text.Spannable.Factory); + method public void setSuggestionsEnabled(boolean); method public final void setText(java.lang.CharSequence); method public void setText(java.lang.CharSequence, android.widget.TextView.BufferType); method public final void setText(char[], int, int); diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 371268f..152a7cb 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -149,10 +149,7 @@ int main(int argc, const char* const argv[]) mArgLen--; AppRuntime runtime; - const char *arg; - const char *argv0; - - argv0 = argv[0]; + const char* argv0 = argv[0]; // Process command line arguments // ignore argv[0] @@ -163,39 +160,53 @@ int main(int argc, const char* const argv[]) int i = runtime.addVmArguments(argc, argv); - // Next arg is parent directory - if (i < argc) { - runtime.mParentDir = argv[i++]; - } - - // Next arg is startup classname or "--zygote" - if (i < argc) { - arg = argv[i++]; - if (0 == strcmp("--zygote", arg)) { - bool startSystemServer = (i < argc) ? - strcmp(argv[i], "--start-system-server") == 0 : false; - setArgv0(argv0, "zygote"); - set_process_name("zygote"); - runtime.start("com.android.internal.os.ZygoteInit", - startSystemServer); + // Parse runtime arguments. Stop at first unrecognized option. + bool zygote = false; + bool startSystemServer = false; + bool application = false; + const char* parentDir = NULL; + const char* niceName = NULL; + const char* className = NULL; + while (i < argc) { + const char* arg = argv[i++]; + if (!parentDir) { + parentDir = arg; + } else if (strcmp(arg, "--zygote") == 0) { + zygote = true; + niceName = "zygote"; + } else if (strcmp(arg, "--start-system-server") == 0) { + startSystemServer = true; + } else if (strcmp(arg, "--application") == 0) { + application = true; + } else if (strncmp(arg, "--nice-name=", 12) == 0) { + niceName = arg + 12; } else { - set_process_name(argv0); - - runtime.mClassName = arg; + className = arg; + break; + } + } - // Remainder of args get passed to startup class main() - runtime.mArgC = argc-i; - runtime.mArgV = argv+i; + if (niceName && *niceName) { + setArgv0(argv0, niceName); + set_process_name(niceName); + } - LOGV("App process is starting with pid=%d, class=%s.\n", - getpid(), runtime.getClassName()); - runtime.start(); - } + runtime.mParentDir = parentDir; + + if (zygote) { + runtime.start("com.android.internal.os.ZygoteInit", + startSystemServer ? "start-system-server" : ""); + } else if (className) { + // Remainder of args get passed to startup class main() + runtime.mClassName = className; + runtime.mArgC = argc - i; + runtime.mArgV = argv + i; + runtime.start("com.android.internal.os.RuntimeInit", + application ? "application" : "tool"); } else { fprintf(stderr, "Error: no class name or --zygote supplied.\n"); app_usage(); LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); return 10; } - } diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp index 785e4cc..dbff095 100644 --- a/cmds/runtime/main_runtime.cpp +++ b/cmds/runtime/main_runtime.cpp @@ -497,7 +497,7 @@ int main(int argc, char* const argv[]) #ifndef HAVE_ANDROID_OS QuickRuntime* runt = new QuickRuntime(); runt->start("com/android/server/SystemServer", - false /* spontaneously fork system server from zygote */); + "" /* spontaneously fork system server from zygote */); #endif } diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index adfda8e..d25de97 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -18,6 +18,7 @@ package android.animation; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.DecelerateInterpolator; @@ -70,8 +71,9 @@ import java.util.List; * moving as a result of the layout event) as well as the values that are changing (such as the * position and size of that object). The actual values that are pushed to each animation * depends on what properties are specified for the animation. For example, the default - * CHANGE_APPEARING animation animates <code>left</code>, <code>top</code>, <code>right</code>, - * and <code>bottom</code>. Values for these properties are updated with the pre- and post-layout + * CHANGE_APPEARING animation animates the <code>left</code>, <code>top</code>, <code>right</code>, + * <code>bottom</code>, <code>scrollX</code>, and <code>scrollY</code> properties. + * Values for these properties are updated with the pre- and post-layout * values when the transition begins. Custom animations will be similarly populated with * the target and values being animated, assuming they use ObjectAnimator objects with * property names that are known on the target object.</p> @@ -210,6 +212,14 @@ public class LayoutTransition { */ private ArrayList<TransitionListener> mListeners; + /** + * Controls whether changing animations automatically animate the parent hierarchy as well. + * This behavior prevents artifacts when wrap_content layouts snap to the end state as the + * transition begins, causing visual glitches and clipping. + * Default value is true. + */ + private boolean mAnimateParentHierarchy = true; + /** * Constructs a LayoutTransition object. By default, the object will listen to layout @@ -223,14 +233,17 @@ public class LayoutTransition { PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1); PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 1); PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1); + PropertyValuesHolder pvhScrollX = PropertyValuesHolder.ofInt("scrollX", 0, 1); + PropertyValuesHolder pvhScrollY = PropertyValuesHolder.ofInt("scrollY", 0, 1); defaultChangeIn = ObjectAnimator.ofPropertyValuesHolder(this, - pvhLeft, pvhTop, pvhRight, pvhBottom); + pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScrollX, pvhScrollY); defaultChangeIn.setDuration(DEFAULT_DURATION); defaultChangeIn.setStartDelay(mChangingAppearingDelay); defaultChangeIn.setInterpolator(mChangingAppearingInterpolator); defaultChangeOut = defaultChangeIn.clone(); defaultChangeOut.setStartDelay(mChangingDisappearingDelay); defaultChangeOut.setInterpolator(mChangingDisappearingInterpolator); + defaultFadeIn = ObjectAnimator.ofFloat(this, "alpha", 0f, 1f); defaultFadeIn.setDuration(DEFAULT_DURATION); defaultFadeIn.setStartDelay(mAppearingDelay); @@ -572,122 +585,24 @@ public class LayoutTransition { // only animate the views not being added or removed if (child != newView) { - - - // Make a copy of the appropriate animation - final Animator anim = baseAnimator.clone(); - - // Set the target object for the animation - anim.setTarget(child); - - // A ObjectAnimator (or AnimatorSet of them) can extract start values from - // its target object - anim.setupStartValues(); - - // If there's an animation running on this view already, cancel it - Animator currentAnimation = pendingAnimations.get(child); - if (currentAnimation != null) { - currentAnimation.cancel(); - pendingAnimations.remove(child); + setupChangeAnimation(parent, changeReason, baseAnimator, duration, child); + } + } + if (mAnimateParentHierarchy) { + ViewGroup tempParent = parent; + while (tempParent != null) { + ViewParent parentParent = tempParent.getParent(); + if (parentParent instanceof ViewGroup) { + setupChangeAnimation((ViewGroup)parentParent, changeReason, baseAnimator, + duration, tempParent); + tempParent = (ViewGroup) parentParent; + } else { + tempParent = null; } - // Cache the animation in case we need to cancel it later - pendingAnimations.put(child, anim); - - // For the animations which don't get started, we have to have a means of - // removing them from the cache, lest we leak them and their target objects. - // We run an animator for the default duration+100 (an arbitrary time, but one - // which should far surpass the delay between setting them up here and - // handling layout events which start them. - ValueAnimator pendingAnimRemover = ValueAnimator.ofFloat(0f, 1f). - setDuration(duration+100); - pendingAnimRemover.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - pendingAnimations.remove(child); - } - }); - pendingAnimRemover.start(); - - // Add a listener to track layout changes on this view. If we don't get a callback, - // then there's nothing to animate. - final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() { - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - - // Tell the animation to extract end values from the changed object - anim.setupEndValues(); - - long startDelay; - if (changeReason == APPEARING) { - startDelay = mChangingAppearingDelay + staggerDelay; - staggerDelay += mChangingAppearingStagger; - } else { - startDelay = mChangingDisappearingDelay + staggerDelay; - staggerDelay += mChangingDisappearingStagger; - } - anim.setStartDelay(startDelay); - anim.setDuration(duration); - - Animator prevAnimation = currentChangingAnimations.get(child); - if (prevAnimation != null) { - prevAnimation.cancel(); - } - Animator pendingAnimation = pendingAnimations.get(child); - if (pendingAnimation != null) { - pendingAnimations.remove(child); - } - // Cache the animation in case we need to cancel it later - currentChangingAnimations.put(child, anim); - - if (anim instanceof ObjectAnimator) { - ((ObjectAnimator) anim).setCurrentPlayTime(0); - } - anim.start(); - - // this only removes listeners whose views changed - must clear the - // other listeners later - child.removeOnLayoutChangeListener(this); - layoutChangeListenerMap.remove(child); - } - }; - // Remove the animation from the cache when it ends - anim.addListener(new AnimatorListenerAdapter() { - - @Override - public void onAnimationStart(Animator animator) { - if (mListeners != null) { - for (TransitionListener listener : mListeners) { - listener.startTransition(LayoutTransition.this, parent, child, - changeReason == APPEARING ? - CHANGE_APPEARING : CHANGE_DISAPPEARING); - } - } - } - @Override - public void onAnimationCancel(Animator animator) { - child.removeOnLayoutChangeListener(listener); - layoutChangeListenerMap.remove(child); - } - - @Override - public void onAnimationEnd(Animator animator) { - currentChangingAnimations.remove(child); - if (mListeners != null) { - for (TransitionListener listener : mListeners) { - listener.endTransition(LayoutTransition.this, parent, child, - changeReason == APPEARING ? - CHANGE_APPEARING : CHANGE_DISAPPEARING); - } - } - } - }); - - child.addOnLayoutChangeListener(listener); - // cache the listener for later removal - layoutChangeListenerMap.put(child, listener); } } + // This is the cleanup step. When we get this rendering event, we know that all of // the appropriate animations have been set up and run. Now we can clear out the // layout listeners. @@ -706,6 +621,175 @@ public class LayoutTransition { } /** + * This flag controls whether CHANGE_APPEARING or CHANGE_DISAPPEARING animations will + * cause the same changing animation to be run on the parent hierarchy as well. This allows + * containers of transitioning views to also transition, which may be necessary in situations + * where the containers bounds change between the before/after states and may clip their + * children during the transition animations. For example, layouts with wrap_content will + * adjust their bounds according to the dimensions of their children. + * + * @param animateParentHierarchy A boolean value indicating whether the parents of + * transitioning views should also be animated during the transition. Default value is true. + */ + public void setAnimateParentHierarchy(boolean animateParentHierarchy) { + mAnimateParentHierarchy = animateParentHierarchy; + } + + /** + * Utility function called by runChangingTransition for both the children and the parent + * hierarchy. + */ + private void setupChangeAnimation(final ViewGroup parent, final int changeReason, + Animator baseAnimator, final long duration, final View child) { + // Make a copy of the appropriate animation + final Animator anim = baseAnimator.clone(); + + // Set the target object for the animation + anim.setTarget(child); + + // A ObjectAnimator (or AnimatorSet of them) can extract start values from + // its target object + anim.setupStartValues(); + + // If there's an animation running on this view already, cancel it + Animator currentAnimation = pendingAnimations.get(child); + if (currentAnimation != null) { + currentAnimation.cancel(); + pendingAnimations.remove(child); + } + // Cache the animation in case we need to cancel it later + pendingAnimations.put(child, anim); + + // For the animations which don't get started, we have to have a means of + // removing them from the cache, lest we leak them and their target objects. + // We run an animator for the default duration+100 (an arbitrary time, but one + // which should far surpass the delay between setting them up here and + // handling layout events which start them. + ValueAnimator pendingAnimRemover = ValueAnimator.ofFloat(0f, 1f). + setDuration(duration + 100); + pendingAnimRemover.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + pendingAnimations.remove(child); + } + }); + pendingAnimRemover.start(); + + // Add a listener to track layout changes on this view. If we don't get a callback, + // then there's nothing to animate. + final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() { + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + + // Tell the animation to extract end values from the changed object + anim.setupEndValues(); + if (anim instanceof ValueAnimator) { + boolean valuesDiffer = false; + ValueAnimator valueAnim = (ValueAnimator)anim; + PropertyValuesHolder[] oldValues = valueAnim.getValues(); + for (int i = 0; i < oldValues.length; ++i) { + PropertyValuesHolder pvh = oldValues[i]; + KeyframeSet keyframeSet = pvh.mKeyframeSet; + if (keyframeSet.mFirstKeyframe == null || + keyframeSet.mLastKeyframe == null || + !keyframeSet.mFirstKeyframe.getValue().equals( + keyframeSet.mLastKeyframe.getValue())) { + valuesDiffer = true; + } + } + if (!valuesDiffer) { + return; + } + } + + long startDelay; + if (changeReason == APPEARING) { + startDelay = mChangingAppearingDelay + staggerDelay; + staggerDelay += mChangingAppearingStagger; + } else { + startDelay = mChangingDisappearingDelay + staggerDelay; + staggerDelay += mChangingDisappearingStagger; + } + anim.setStartDelay(startDelay); + anim.setDuration(duration); + + Animator prevAnimation = currentChangingAnimations.get(child); + if (prevAnimation != null) { + prevAnimation.cancel(); + } + Animator pendingAnimation = pendingAnimations.get(child); + if (pendingAnimation != null) { + pendingAnimations.remove(child); + } + // Cache the animation in case we need to cancel it later + currentChangingAnimations.put(child, anim); + + parent.requestTransitionStart(LayoutTransition.this); + + // this only removes listeners whose views changed - must clear the + // other listeners later + child.removeOnLayoutChangeListener(this); + layoutChangeListenerMap.remove(child); + } + }; + // Remove the animation from the cache when it ends + anim.addListener(new AnimatorListenerAdapter() { + + @Override + public void onAnimationStart(Animator animator) { + if (mListeners != null) { + for (TransitionListener listener : mListeners) { + listener.startTransition(LayoutTransition.this, parent, child, + changeReason == APPEARING ? + CHANGE_APPEARING : CHANGE_DISAPPEARING); + } + } + } + + @Override + public void onAnimationCancel(Animator animator) { + child.removeOnLayoutChangeListener(listener); + layoutChangeListenerMap.remove(child); + } + + @Override + public void onAnimationEnd(Animator animator) { + currentChangingAnimations.remove(child); + if (mListeners != null) { + for (TransitionListener listener : mListeners) { + listener.endTransition(LayoutTransition.this, parent, child, + changeReason == APPEARING ? + CHANGE_APPEARING : CHANGE_DISAPPEARING); + } + } + } + }); + + child.addOnLayoutChangeListener(listener); + // cache the listener for later removal + layoutChangeListenerMap.put(child, listener); + } + + /** + * Starts the animations set up for a CHANGING transition. We separate the setup of these + * animations from actually starting them, to avoid side-effects that starting the animations + * may have on the properties of the affected objects. After setup, we tell the affected parent + * that this transition should be started. The parent informs its ViewAncestor, which then + * starts the transition after the current layout/measurement phase, just prior to drawing + * the view hierarchy. + * + * @hide + */ + public void startChangingAnimations() { + for (Animator anim : currentChangingAnimations.values()) { + if (anim instanceof ObjectAnimator) { + ((ObjectAnimator) anim).setCurrentPlayTime(0); + } + anim.start(); + } + } + + /** * Returns true if animations are running which animate layout-related properties. This * essentially means that either CHANGE_APPEARING or CHANGE_DISAPPEARING animations * are running, since these animations operate on layout-related properties. diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 87369ab..3877bd0 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1398,6 +1398,10 @@ public class Activity extends ContextThemeWrapper public void onConfigurationChanged(Configuration newConfig) { mCalled = true; + if (mActionBar != null) { + mActionBar.onConfigurationChanged(newConfig); + } + mFragments.dispatchConfigurationChanged(newConfig); if (mWindow != null) { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index f2c9796..2a0d798 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1467,6 +1467,22 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case REGISTER_PROCESS_OBSERVER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IProcessObserver observer = IProcessObserver.Stub.asInterface( + data.readStrongBinder()); + registerProcessObserver(observer); + return true; + } + + case UNREGISTER_PROCESS_OBSERVER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IProcessObserver observer = IProcessObserver.Stub.asInterface( + data.readStrongBinder()); + unregisterProcessObserver(observer); + return true; + } + } return super.onTransact(code, data, reply, flags); @@ -3300,5 +3316,27 @@ class ActivityManagerProxy implements IActivityManager return result; } + public void registerProcessObserver(IProcessObserver observer) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(observer != null ? observer.asBinder() : null); + mRemote.transact(REGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(observer != null ? observer.asBinder() : null); + mRemote.transact(UNREGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 54c3422..1f53c0e 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -355,6 +355,9 @@ public interface IActivityManager extends IInterface { public boolean removeTask(int taskId, int flags) throws RemoteException; + public void registerProcessObserver(IProcessObserver observer) throws RemoteException; + public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -577,4 +580,6 @@ public interface IActivityManager extends IInterface { int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+127; int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+128; int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+129; + int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+130; + int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131; } diff --git a/core/java/android/app/IProcessObserver.aidl b/core/java/android/app/IProcessObserver.aidl new file mode 100644 index 0000000..2094294 --- /dev/null +++ b/core/java/android/app/IProcessObserver.aidl @@ -0,0 +1,25 @@ +/* + * 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.app; + +/** {@hide} */ +oneway interface IProcessObserver { + + void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities); + void onProcessDied(int pid, int uid); + +} diff --git a/core/java/android/app/IThumbnailRetriever.aidl b/core/java/android/app/IThumbnailRetriever.aidl index 2a6737d..410cc20 100644 --- a/core/java/android/app/IThumbnailRetriever.aidl +++ b/core/java/android/app/IThumbnailRetriever.aidl @@ -18,6 +18,7 @@ import android.graphics.Bitmap; /** * System private API for retrieving thumbnails + * {@hide} */ interface IThumbnailRetriever { Bitmap getThumbnail(int index); diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 95451d6..71f6445 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -167,7 +167,8 @@ public class UiModeManager { * Return the current running mode type. May be one of * {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL}, * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, or - * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, + * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, or + * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_TV}. */ public int getCurrentModeType() { if (mService != null) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 4c7d87f..a660bd7 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1544,6 +1544,9 @@ public abstract class Context { */ public static final String NETWORKMANAGEMENT_SERVICE = "network_management"; + /** {@hide} */ + public static final String NETWORK_POLICY_SERVICE = "netpolicy"; + /** * Use with {@link #getSystemService} to retrieve a {@link * android.net.wifi.WifiManager} for handling management of diff --git a/core/java/android/content/IOnPrimaryClipChangedListener.aidl b/core/java/android/content/IOnPrimaryClipChangedListener.aidl index fb42a45..46d7f7c 100644 --- a/core/java/android/content/IOnPrimaryClipChangedListener.aidl +++ b/core/java/android/content/IOnPrimaryClipChangedListener.aidl @@ -16,6 +16,9 @@ package android.content; +/** + * {@hide} + */ oneway interface IOnPrimaryClipChangedListener { void dispatchPrimaryClipChanged(); } diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 12ec258..51a7115 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -226,6 +226,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration public static final int UI_MODE_TYPE_NORMAL = 0x01; public static final int UI_MODE_TYPE_DESK = 0x02; public static final int UI_MODE_TYPE_CAR = 0x03; + public static final int UI_MODE_TYPE_TELEVISION = 0x04; public static final int UI_MODE_NIGHT_MASK = 0x30; public static final int UI_MODE_NIGHT_UNDEFINED = 0x00; @@ -367,6 +368,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break; case UI_MODE_TYPE_DESK: sb.append(" desk"); break; case UI_MODE_TYPE_CAR: sb.append(" car"); break; + case UI_MODE_TYPE_TELEVISION: sb.append(" television"); break; default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break; } switch ((uiMode&UI_MODE_NIGHT_MASK)) { diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index fa6eae5..d9351ee 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -23,9 +23,6 @@ package android.net; */ interface INetworkPolicyManager { - void onForegroundActivitiesChanged(int uid, int pid, boolean foregroundActivities); - void onProcessDied(int uid, int pid); - void setUidPolicy(int uid, int policy); int getUidPolicy(int uid); diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 2312bd9..1913aa7 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -16,6 +16,7 @@ package android.net; +import android.content.Context; import android.os.RemoteException; /** @@ -43,6 +44,10 @@ public class NetworkPolicyManager { mService = service; } + public static NetworkPolicyManager getSystemService(Context context) { + return (NetworkPolicyManager) context.getSystemService(Context.NETWORK_POLICY_SERVICE); + } + /** * Set policy flags for specific UID. * diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 4430e00..0f207bc 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -36,6 +36,9 @@ public class NetworkStats implements Parcelable { /** {@link #uid} value when entry is summarized over all UIDs. */ public static final int UID_ALL = 0; + // NOTE: data should only be accounted for once in this structure; if data + // is broken out, the summarized version should not be included. + /** * {@link SystemClock#elapsedRealtime()} timestamp when this data was * generated. @@ -81,12 +84,13 @@ public class NetworkStats implements Parcelable { mTx = new long[size]; } - public void addEntry(String iface, int uid, long rx, long tx) { + public Builder addEntry(String iface, int uid, long rx, long tx) { mIface[mIndex] = iface; mUid[mIndex] = uid; mRx[mIndex] = rx; mTx[mIndex] = tx; mIndex++; + return this; } public NetworkStats build() { @@ -97,11 +101,17 @@ public class NetworkStats implements Parcelable { } } + public int length() { + // length is identical for all fields + return iface.length; + } + /** * Find first stats index that matches the requested parameters. */ public int findIndex(String iface, int uid) { - for (int i = 0; i < this.iface.length; i++) { + final int length = length(); + for (int i = 0; i < length; i++) { if (equal(iface, this.iface[i]) && uid == this.uid[i]) { return i; } @@ -109,13 +119,38 @@ public class NetworkStats implements Parcelable { return -1; } - private static boolean equal(Object a, Object b) { - return a == b || (a != null && a.equals(b)); + /** + * Subtract the given {@link NetworkStats}, effectively leaving the delta + * between two snapshots in time. Assumes that statistics rows collect over + * time, and that none of them have disappeared. + */ + public NetworkStats subtract(NetworkStats value) { + // result will have our rows, but no meaningful timestamp + final int length = length(); + final NetworkStats.Builder result = new NetworkStats.Builder(-1, length); + + for (int i = 0; i < length; i++) { + final String iface = this.iface[i]; + final int uid = this.uid[i]; + + // find remote row that matches, and subtract + final int j = value.findIndex(iface, uid); + if (j == -1) { + // newly appearing row, return entire value + result.addEntry(iface, uid, this.rx[i], this.tx[i]); + } else { + // existing row, subtract remote value + final long rx = this.rx[i] - value.rx[j]; + final long tx = this.tx[i] - value.tx[j]; + result.addEntry(iface, uid, rx, tx); + } + } + + return result.build(); } - /** {@inheritDoc} */ - public int describeContents() { - return 0; + private static boolean equal(Object a, Object b) { + return a == b || (a != null && a.equals(b)); } public void dump(String prefix, PrintWriter pw) { @@ -138,6 +173,11 @@ public class NetworkStats implements Parcelable { } /** {@inheritDoc} */ + public int describeContents() { + return 0; + } + + /** {@inheritDoc} */ public void writeToParcel(Parcel dest, int flags) { dest.writeLong(elapsedRealtime); dest.writeStringArray(iface); diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 7ee7a81..c0ff734 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -16,6 +16,12 @@ package android.net; +import android.content.Context; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.RemoteException; +import android.os.ServiceManager; + import dalvik.system.BlockGuard; import java.net.Socket; @@ -36,6 +42,17 @@ public class TrafficStats { public final static int UNSUPPORTED = -1; /** + * Snapshot of {@link NetworkStats} when the currently active profiling + * session started, or {@code null} if no session active. + * + * @see #startDataProfiling(Context) + * @see #stopDataProfiling(Context) + */ + private static NetworkStats sActiveProfilingStart; + + private static Object sProfilingLock = new Object(); + + /** * Set active tag to use when accounting {@link Socket} traffic originating * from the current thread. Only one active tag per thread is supported. * <p> @@ -93,6 +110,44 @@ public class TrafficStats { } /** + * Start profiling data usage for current UID. Only one profiling session + * can be active at a time. + * + * @hide + */ + public static void startDataProfiling(Context context) { + synchronized (sProfilingLock) { + if (sActiveProfilingStart != null) { + throw new IllegalStateException("already profiling data"); + } + + // take snapshot in time; we calculate delta later + sActiveProfilingStart = getNetworkStatsForUid(context); + } + } + + /** + * Stop profiling data usage for current UID. + * + * @return Detailed {@link NetworkStats} of data that occurred since last + * {@link #startDataProfiling(Context)} call. + * @hide + */ + public static NetworkStats stopDataProfiling(Context context) { + synchronized (sProfilingLock) { + if (sActiveProfilingStart == null) { + throw new IllegalStateException("not profiling data"); + } + + // subtract starting values and return delta + final NetworkStats profilingStop = getNetworkStatsForUid(context); + final NetworkStats profilingDelta = profilingStop.subtract(sActiveProfilingStart); + sActiveProfilingStart = null; + return profilingDelta; + } + } + + /** * Get the total number of packets transmitted through the mobile interface. * * @return number of packets. If the statistics are not supported by this device, @@ -350,4 +405,21 @@ public class TrafficStats { * {@link #UNSUPPORTED} will be returned. */ public static native long getUidUdpRxPackets(int uid); + + /** + * Return detailed {@link NetworkStats} for the current UID. Requires no + * special permission. + */ + private static NetworkStats getNetworkStatsForUid(Context context) { + final IBinder binder = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + final INetworkManagementService service = INetworkManagementService.Stub.asInterface( + binder); + + final int uid = android.os.Process.myUid(); + try { + return service.getNetworkStatsUidDetail(uid); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } } diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/INdefPushCallback.aidl new file mode 100644 index 0000000..80ba2ed --- /dev/null +++ b/core/java/android/nfc/INdefPushCallback.aidl @@ -0,0 +1,28 @@ +/* + * 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.nfc; + +import android.nfc.NdefMessage; + +/** + * @hide + */ +interface INdefPushCallback +{ + NdefMessage onConnect(); + void onMessagePushed(); +} diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index 870127c..d11fea0 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -25,6 +25,7 @@ import android.nfc.TechListParcel; import android.nfc.ILlcpSocket; import android.nfc.ILlcpServiceSocket; import android.nfc.ILlcpConnectionlessSocket; +import android.nfc.INdefPushCallback; import android.nfc.INfcTag; import android.nfc.IP2pTarget; import android.nfc.IP2pInitiator; @@ -51,6 +52,7 @@ interface INfcAdapter in IntentFilter[] filters, in TechListParcel techLists); void disableForegroundDispatch(in ComponentName activity); void enableForegroundNdefPush(in ComponentName activity, in NdefMessage msg); + void enableForegroundNdefPushWithCallback(in ComponentName activity, in INdefPushCallback callback); void disableForegroundNdefPush(in ComponentName activity); // Non-public methods diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 4689804..738e75f 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -124,7 +124,7 @@ public final class NfcAdapter { * Intent to start an activity when a tag is discovered. * * <p>This intent will not be started when a tag is discovered if any activities respond to - * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag. + * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"; @@ -235,6 +235,37 @@ public final class NfcAdapter { */ private static final int DISCOVERY_MODE_CARD_EMULATION = 2; + /** + * Callback passed into {@link #enableForegroundNdefPush(Activity,NdefPushCallback)}. This + */ + public interface NdefPushCallback { + /** + * Called when a P2P connection is created. + */ + NdefMessage createMessage(); + /** + * Called when the message is pushed. + */ + void onMessagePushed(); + } + + private static class NdefPushCallbackWrapper extends INdefPushCallback.Stub { + private NdefPushCallback mCallback; + + public NdefPushCallbackWrapper(NdefPushCallback callback) { + mCallback = callback; + } + + @Override + public NdefMessage onConnect() { + return mCallback.createMessage(); + } + + @Override + public void onMessagePushed() { + mCallback.onMessagePushed(); + } + } // Guarded by NfcAdapter.class private static boolean sIsInitialized = false; @@ -575,6 +606,44 @@ public final class NfcAdapter { } /** + * Enable NDEF message push over P2P while this Activity is in the foreground. + * + * <p>For this to function properly the other NFC device being scanned must + * support the "com.android.npp" NDEF push protocol. Support for this + * protocol is currently optional for Android NFC devices. + * + * <p>This method must be called from the main thread. + * + * <p class="note"><em>NOTE:</em> While foreground NDEF push is active standard tag dispatch is disabled. + * Only the foreground activity may receive tag discovered dispatches via + * {@link #enableForegroundDispatch}. + * + * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. + * + * @param activity the foreground Activity + * @param callback is called on when the P2P connection is established + * @throws IllegalStateException if the Activity is not currently in the foreground + * @throws OperationNotSupportedException if this Android device does not support NDEF push + */ + public void enableForegroundNdefPush(Activity activity, NdefPushCallback callback) { + if (activity == null || callback == null) { + throw new NullPointerException(); + } + if (!activity.isResumed()) { + throw new IllegalStateException("Foregorund NDEF push can only be enabled " + + "when your activity is resumed"); + } + try { + ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, + mForegroundNdefPushListener); + sService.enableForegroundNdefPushWithCallback(activity.getComponentName(), + new NdefPushCallbackWrapper(callback)); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + } + } + + /** * Disable NDEF message push over P2P. * * <p>After calling {@link #enableForegroundNdefPush}, an activity diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index ecc111b..f17a6f2 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -214,6 +214,12 @@ interface INetworkManagementService NetworkStats getNetworkStatsDetail(); /** + * Return detailed network statistics for the requested UID, + * including interface and tag details. + */ + NetworkStats getNetworkStatsUidDetail(int uid); + + /** * Configures bandwidth throttling on an interface. */ void setInterfaceThrottle(String iface, int rxKbps, int txKbps); diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 727fcca..3ea3f56 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -129,7 +129,46 @@ public class ParcelFileDescriptor implements Parcelable { } /** - * Create a new ParcelFileDescriptor from the specified Socket. + * Create a new ParcelFileDescriptor from a raw native fd. The new + * ParcelFileDescriptor holds a dup of the original fd passed in here, + * so you must still close that fd as well as the new ParcelFileDescriptor. + * + * @param fd The native fd that the ParcelFileDescriptor should dup. + * + * @return Returns a new ParcelFileDescriptor holding a FileDescriptor + * for a dup of the given fd. + */ + public static ParcelFileDescriptor fromFd(int fd) throws IOException { + FileDescriptor fdesc = getFileDescriptorFromFd(fd); + return new ParcelFileDescriptor(fdesc); + } + + // Extracts the file descriptor from the specified socket and returns it untouched + private static native FileDescriptor getFileDescriptorFromFd(int fd) throws IOException; + + /** + * Take ownership of a raw native fd in to a new ParcelFileDescriptor. + * The returned ParcelFileDescriptor now owns the given fd, and will be + * responsible for closing it. You must not close the fd yourself. + * + * @param fd The native fd that the ParcelFileDescriptor should adopt. + * + * @return Returns a new ParcelFileDescriptor holding a FileDescriptor + * for the given fd. + */ + public static ParcelFileDescriptor adoptFd(int fd) { + FileDescriptor fdesc = getFileDescriptorFromFdNoDup(fd); + return new ParcelFileDescriptor(fdesc); + } + + // Extracts the file descriptor from the specified socket and returns it untouched + private static native FileDescriptor getFileDescriptorFromFdNoDup(int fd); + + /** + * Create a new ParcelFileDescriptor from the specified Socket. The new + * ParcelFileDescriptor holds a dup of the original FileDescriptor in + * the Socket, so you must still close the Socket as well as the new + * ParcelFileDescriptor. * * @param socket The Socket whose FileDescriptor is used to create * a new ParcelFileDescriptor. @@ -163,17 +202,14 @@ public class ParcelFileDescriptor implements Parcelable { */ public static ParcelFileDescriptor[] createPipe() throws IOException { FileDescriptor[] fds = new FileDescriptor[2]; - int res = createPipeNative(fds); - if (res == 0) { - ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2]; - pfds[0] = new ParcelFileDescriptor(fds[0]); - pfds[1] = new ParcelFileDescriptor(fds[1]); - return pfds; - } - throw new IOException("Unable to create pipe: errno=" + -res); + createPipeNative(fds); + ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2]; + pfds[0] = new ParcelFileDescriptor(fds[0]); + pfds[1] = new ParcelFileDescriptor(fds[1]); + return pfds; } - private static native int createPipeNative(FileDescriptor[] outFds); + private static native void createPipeNative(FileDescriptor[] outFds) throws IOException; /** * @hide Please use createPipe() or ContentProvider.openPipeHelper(). diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index f85df6c..d475f36 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -634,6 +634,20 @@ public class Process { } /** + * Returns the parent process id for a currently running process. + * @param pid the process id + * @return the parent process id of the process, or -1 if the process is not running. + * @hide + */ + public static final int getParentPid(int pid) { + String[] procStatusLabels = { "PPid:" }; + long[] procStatusValues = new long[1]; + procStatusValues[0] = -1; + Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues); + return (int) procStatusValues[0]; + } + + /** * Set the priority of a thread, based on Linux priorities. * * @param tid The identifier of the thread/process to change. diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 1375a29..01c640a 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -1481,6 +1481,13 @@ public final class StrictMode { onVmPolicyViolation(message, originStack); } + /** + * @hide + */ + public static void onWebViewMethodCalledOnWrongThread(Throwable originStack) { + onVmPolicyViolation(null, originStack); + } + // Map from VM violation fingerprint to uptime millis. private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>(); diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index d68e6fb..bc6e993 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -36,6 +36,11 @@ public class StorageVolume implements Parcelable { private final int mMtpReserveSpace; private int mStorageId; + // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING, + // ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED, + // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts. + public static final String EXTRA_STORAGE_VOLUME = "storage_volume"; + public StorageVolume(String path, String description, boolean removable, boolean emulated, int mtpReserveSpace) { mPath = path; diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java index 4141879..20614dc 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -616,18 +616,6 @@ public final class Calendar { public static final String DTEND = "dtend"; /** - * The time the event starts with allDay events in a local tz - * <P>Type: INTEGER (long; millis since epoch)</P> - */ - public static final String DTSTART2 = "dtstart2"; - - /** - * The time the event ends with allDay events in a local tz - * <P>Type: INTEGER (long; millis since epoch)</P> - */ - public static final String DTEND2 = "dtend2"; - - /** * The duration of the event * <P>Type: TEXT (duration in RFC2445 format)</P> */ @@ -734,8 +722,16 @@ public final class Calendar { public static final String EXDATE = "exdate"; /** + * The _id of the original recurring event for which this event is an + * exception. + * <P>Type: TEXT</P> + */ + public static final String ORIGINAL_ID = "original_id"; + + /** * The _sync_id of the original recurring event for which this event is - * an exception. + * an exception. The provider should keep the original_id in sync when + * this is updated. * <P>Type: TEXT</P> */ public static final String ORIGINAL_SYNC_ID = "original_sync_id"; @@ -899,6 +895,7 @@ public final class Calendar { DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DTEND); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DURATION); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_TIMEZONE); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_END_TIMEZONE); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ALL_DAY); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, AVAILABILITY); @@ -910,6 +907,7 @@ public final class Calendar { DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXRULE); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXDATE); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_SYNC_ID); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_ID); DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, ORIGINAL_INSTANCE_TIME); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ORIGINAL_ALL_DAY); @@ -925,7 +923,7 @@ public final class Calendar { DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED); - DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC1); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Events.SYNC_DATA1); diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 2c79385..60bee9a 100644..100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -1104,7 +1104,7 @@ public class BluetoothService extends IBluetooth.Stub { } /*package*/ synchronized boolean setBondState(String address, int state, int reason) { - mBondState.setBondState(address.toUpperCase(), state); + mBondState.setBondState(address.toUpperCase(), state, reason); return true; } diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java index 6f4c15b..d698b54 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java +++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java @@ -18,6 +18,7 @@ package android.speech.tts; import android.media.AudioFormat; import android.media.AudioTrack; import android.os.Bundle; +import android.os.Handler; import android.util.Log; /** @@ -49,16 +50,20 @@ class PlaybackSynthesisRequest extends SynthesisRequest { private final float mPan; private final Object mStateLock = new Object(); - private AudioTrack mAudioTrack = null; + private final Handler mAudioTrackHandler; + private volatile AudioTrack mAudioTrack = null; private boolean mStopped = false; private boolean mDone = false; + private volatile boolean mWriteErrorOccured; PlaybackSynthesisRequest(String text, Bundle params, - int streamType, float volume, float pan) { + int streamType, float volume, float pan, Handler audioTrackHandler) { super(text, params); mStreamType = streamType; mVolume = volume; mPan = pan; + mAudioTrackHandler = audioTrackHandler; + mWriteErrorOccured = false; } @Override @@ -70,14 +75,28 @@ class PlaybackSynthesisRequest extends SynthesisRequest { } } + // Always guarded by mStateLock. private void cleanUp() { if (DBG) Log.d(TAG, "cleanUp()"); - if (mAudioTrack != null) { - mAudioTrack.flush(); - mAudioTrack.stop(); - mAudioTrack.release(); - mAudioTrack = null; + if (mAudioTrack == null) { + return; } + + final AudioTrack audioTrack = mAudioTrack; + mAudioTrack = null; + + // Clean up on the audiotrack handler thread. + // + // NOTE: It isn't very clear whether AudioTrack is thread safe. + // If it is we can clean up on the current (synthesis) thread. + mAudioTrackHandler.post(new Runnable() { + @Override + public void run() { + audioTrack.flush(); + audioTrack.stop(); + audioTrack.release(); + } + }); } @Override @@ -146,10 +165,15 @@ class PlaybackSynthesisRequest extends SynthesisRequest { Log.d(TAG, "audioAvailable(byte[" + buffer.length + "]," + offset + "," + length + ")"); } - if (length > getMaxBufferSize()) { - throw new IllegalArgumentException("buffer is too large (" + length + " bytes)"); + if (length > getMaxBufferSize() || length <= 0) { + throw new IllegalArgumentException("buffer is too large or of zero length (" + + + length + " bytes)"); } synchronized (mStateLock) { + if (mWriteErrorOccured) { + if (DBG) Log.d(TAG, "Error writing to audio track, count < 0"); + return TextToSpeech.ERROR; + } if (mStopped) { if (DBG) Log.d(TAG, "Request has been aborted."); return TextToSpeech.ERROR; @@ -158,22 +182,33 @@ class PlaybackSynthesisRequest extends SynthesisRequest { Log.e(TAG, "audioAvailable(): Not started"); return TextToSpeech.ERROR; } - int playState = mAudioTrack.getPlayState(); - if (playState == AudioTrack.PLAYSTATE_STOPPED) { - if (DBG) Log.d(TAG, "AudioTrack stopped, restarting"); - mAudioTrack.play(); - } - // TODO: loop until all data is written? - if (DBG) Log.d(TAG, "AudioTrack.write()"); - int count = mAudioTrack.write(buffer, offset, length); - if (DBG) Log.d(TAG, "AudioTrack.write() returned " + count); - if (count < 0) { - Log.e(TAG, "Writing to AudioTrack failed: " + count); - cleanUp(); - return TextToSpeech.ERROR; - } else { - return TextToSpeech.SUCCESS; - } + final AudioTrack audioTrack = mAudioTrack; + // Sigh, another copy. + final byte[] bufferCopy = new byte[length]; + System.arraycopy(buffer, offset, bufferCopy, 0, length); + + mAudioTrackHandler.post(new Runnable() { + @Override + public void run() { + int playState = audioTrack.getPlayState(); + if (playState == AudioTrack.PLAYSTATE_STOPPED) { + if (DBG) Log.d(TAG, "AudioTrack stopped, restarting"); + audioTrack.play(); + } + // TODO: loop until all data is written? + if (DBG) Log.d(TAG, "AudioTrack.write()"); + int count = audioTrack.write(bufferCopy, 0, bufferCopy.length); + // The semantics of this change very slightly. Earlier, we would + // report an error immediately, Now we will return an error on + // the next API call, usually done( ) or another audioAvailable( ) + // call. + if (count < 0) { + mWriteErrorOccured = true; + } + } + }); + + return TextToSpeech.SUCCESS; } } @@ -181,6 +216,10 @@ class PlaybackSynthesisRequest extends SynthesisRequest { public int done() { if (DBG) Log.d(TAG, "done()"); synchronized (mStateLock) { + if (mWriteErrorOccured) { + if (DBG) Log.d(TAG, "Error writing to audio track, count < 0"); + return TextToSpeech.ERROR; + } if (mStopped) { if (DBG) Log.d(TAG, "Request has been aborted."); return TextToSpeech.ERROR; diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index f32474f..717dde8 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -51,6 +51,7 @@ public abstract class TextToSpeechService extends Service { private static final String SYNTH_THREAD_NAME = "SynthThread"; private SynthHandler mSynthHandler; + private Handler mAudioTrackHandler; private CallbackMap mCallbacks; @@ -63,6 +64,10 @@ public abstract class TextToSpeechService extends Service { synthThread.start(); mSynthHandler = new SynthHandler(synthThread.getLooper()); + HandlerThread audioTrackThread = new HandlerThread("TTS.audioTrackThread"); + audioTrackThread.start(); + mAudioTrackHandler = new Handler(audioTrackThread.getLooper()); + mCallbacks = new CallbackMap(); // Load default language @@ -75,6 +80,7 @@ public abstract class TextToSpeechService extends Service { // Tell the synthesizer to stop mSynthHandler.quit(); + mAudioTrackHandler.getLooper().quit(); // Unregister all callbacks. mCallbacks.kill(); @@ -444,7 +450,7 @@ public abstract class TextToSpeechService extends Service { protected SynthesisRequest createSynthesisRequest() { return new PlaybackSynthesisRequest(mText, mParams, - getStreamType(), getVolume(), getPan()); + getStreamType(), getVolume(), getPan(), mAudioTrackHandler); } private void setRequestParams(SynthesisRequest request) { diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java index d432dee..fe96565 100644 --- a/core/java/android/text/method/ArrowKeyMovementMethod.java +++ b/core/java/android/text/method/ArrowKeyMovementMethod.java @@ -234,7 +234,7 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme if (action == MotionEvent.ACTION_DOWN) { boolean cap = isSelecting(buffer); if (cap) { - int offset = widget.getOffset((int) event.getX(), (int) event.getY()); + int offset = widget.getOffsetForPosition(event.getX(), event.getY()); buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT); @@ -259,7 +259,7 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme // Update selection as we're moving the selection area. // Get the current touch position - int offset = widget.getOffset((int) event.getX(), (int) event.getY()); + int offset = widget.getOffsetForPosition(event.getX(), event.getY()); Selection.extendSelection(buffer, offset); return true; @@ -275,7 +275,7 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme return true; } - int offset = widget.getOffset((int) event.getX(), (int) event.getY()); + int offset = widget.getOffsetForPosition(event.getX(), event.getY()); if (isSelecting(buffer)) { buffer.removeSpan(LAST_TAP_DOWN); Selection.extendSelection(buffer, offset); diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 845fbc3..61a24a0 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -17,7 +17,6 @@ package android.view; -import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.SurfaceTexture; @@ -137,14 +136,14 @@ public abstract class HardwareRenderer { * * @param canvas The Canvas used to render the view. */ - void onHardwarePreDraw(Canvas canvas); + void onHardwarePreDraw(HardwareCanvas canvas); /** * Invoked after a view is drawn by a hardware renderer. * * @param canvas The Canvas used to render the view. */ - void onHardwarePostDraw(Canvas canvas); + void onHardwarePostDraw(HardwareCanvas canvas); } /** diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 82fd581..3436cd1 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -883,8 +883,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { * <p> * <ul> * <li>For a stylus, reports the distance of the stylus from the screen. - * The value is normalized to a range from 0.0 (direct contact) to 1.0 (furthest measurable - * distance). + * The value is nominally measured in millimeters where 0.0 indicates direct contact + * and larger values indicate increasing distance from the surface. * </ul> * </p> * diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index d5f573c..98d07c4 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6048,6 +6048,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Set the horizontal scrolled position of your view. This will cause a call to + * {@link #onScrollChanged(int, int, int, int)} and the view will be + * invalidated. + * @param value the x position to scroll to + */ + public void setScrollX(int value) { + scrollTo(value, mScrollY); + } + + /** + * Set the vertical scrolled position of your view. This will cause a call to + * {@link #onScrollChanged(int, int, int, int)} and the view will be + * invalidated. + * @param value the y position to scroll to + */ + public void setScrollY(int value) { + scrollTo(mScrollX, value); + } + + /** * Return the scrolled left position of this view. This is the left edge of * the displayed part of your view. You do not need to draw any pixels * farther left, since those are outside of the frame of your view on diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java index 8085ea8..bf04502 100644 --- a/core/java/android/view/ViewAncestor.java +++ b/core/java/android/view/ViewAncestor.java @@ -17,6 +17,7 @@ package android.view; import android.Manifest; +import android.animation.LayoutTransition; import android.app.ActivityManagerNative; import android.content.ClipDescription; import android.content.ComponentCallbacks; @@ -25,7 +26,6 @@ import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PixelFormat; @@ -45,9 +45,7 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; -import android.os.SystemProperties; import android.util.AndroidRuntimeException; import android.util.DisplayMetrics; import android.util.EventLog; @@ -230,10 +228,11 @@ public final class ViewAncestor extends Handler implements ViewParent, int mScrollY; int mCurScrollY; Scroller mScroller; - Bitmap mResizeBitmap; - long mResizeBitmapStartTime; - int mResizeBitmapDuration; + HardwareLayer mResizeBuffer; + long mResizeBufferStartTime; + int mResizeBufferDuration; static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator(); + private ArrayList<LayoutTransition> mPendingTransitions; final ViewConfiguration mViewConfiguration; @@ -672,6 +671,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (!mTraversalScheduled) { mTraversalScheduled = true; + //noinspection ConstantConditions if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) { final long now = System.nanoTime(); Log.d(TAG, "Latency: Scheduled traversal, it has been " @@ -694,10 +694,32 @@ public final class ViewAncestor extends Handler implements ViewParent, return mAppVisible ? mView.getVisibility() : View.GONE; } - void disposeResizeBitmap() { - if (mResizeBitmap != null) { - mResizeBitmap.recycle(); - mResizeBitmap = null; + void disposeResizeBuffer() { + if (mResizeBuffer != null) { + mResizeBuffer.destroy(); + mResizeBuffer = null; + } + } + + /** + * Add LayoutTransition to the list of transitions to be started in the next traversal. + * This list will be cleared after the transitions on the list are start()'ed. These + * transitionsa re added by LayoutTransition itself when it sets up animations. The setup + * happens during the layout phase of traversal, which we want to complete before any of the + * animations are started (because those animations may side-effect properties that layout + * depends upon, like the bounding rectangles of the affected views). So we add the transition + * to the list and it is started just prior to starting the drawing phase of traversal. + * + * @param transition The LayoutTransition to be started on the next traversal. + * + * @hide + */ + public void requestTransitionStart(LayoutTransition transition) { + if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) { + if (mPendingTransitions == null) { + mPendingTransitions = new ArrayList<LayoutTransition>(); + } + mPendingTransitions.add(transition); } } @@ -819,15 +841,25 @@ public final class ViewAncestor extends Handler implements ViewParent, mAttachInfo.mHardwareRenderer.isEnabled() && lp != null && !PixelFormat.formatHasAlpha(lp.format)) { - disposeResizeBitmap(); + disposeResizeBuffer(); boolean completed = false; + HardwareCanvas canvas = null; try { - mResizeBitmap = Bitmap.createBitmap(mWidth, mHeight, - Bitmap.Config.ARGB_8888); - mResizeBitmap.setHasAlpha(false); - Canvas canvas = new Canvas(mResizeBitmap); + if (mResizeBuffer == null) { + mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer( + mWidth, mHeight, false); + } else if (mResizeBuffer.getWidth() != mWidth || + mResizeBuffer.getHeight() != mHeight) { + mResizeBuffer.resize(mWidth, mHeight); + } + canvas = mResizeBuffer.start(mAttachInfo.mHardwareCanvas); + canvas.setViewport(mWidth, mHeight); + canvas.onPreDraw(null); + final int restoreCount = canvas.save(); + canvas.drawColor(0xff000000, PorterDuff.Mode.SRC); + int yoff; final boolean scrolling = mScroller != null && mScroller.computeScrollOffset(); @@ -837,22 +869,32 @@ public final class ViewAncestor extends Handler implements ViewParent, } else { yoff = mScrollY; } + canvas.translate(0, -yoff); if (mTranslator != null) { mTranslator.translateCanvas(canvas); } - canvas.setScreenDensity(mAttachInfo.mScalingRequired - ? DisplayMetrics.DENSITY_DEVICE : 0); + mView.draw(canvas); - mResizeBitmapStartTime = SystemClock.uptimeMillis(); - mResizeBitmapDuration = mView.getResources().getInteger( + + mResizeBufferStartTime = SystemClock.uptimeMillis(); + mResizeBufferDuration = mView.getResources().getInteger( com.android.internal.R.integer.config_mediumAnimTime); completed = true; + + canvas.restoreToCount(restoreCount); } catch (OutOfMemoryError e) { Log.w(TAG, "Not enough memory for content change anim buffer", e); } finally { - if (!completed) { - mResizeBitmap = null; + if (canvas != null) { + canvas.onPostDraw(); + } + if (mResizeBuffer != null) { + mResizeBuffer.end(mAttachInfo.mHardwareCanvas); + if (!completed) { + mResizeBuffer.destroy(); + mResizeBuffer = null; + } } } } @@ -1114,7 +1156,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (mScroller != null) { mScroller.abortAnimation(); } - disposeResizeBitmap(); + disposeResizeBuffer(); } else if (surfaceGenerationId != mSurface.getGenerationId() && mSurfaceHolder == null && mAttachInfo.mHardwareRenderer != null) { fullRedrawNeeded = true; @@ -1396,6 +1438,12 @@ public final class ViewAncestor extends Handler implements ViewParent, boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw(); if (!cancelDraw && !newSurface) { + if (mPendingTransitions != null && mPendingTransitions.size() > 0) { + for (int i = 0; i < mPendingTransitions.size(); ++i) { + mPendingTransitions.get(i).startChangingAnimations(); + } + mPendingTransitions.clear(); + } mFullRedrawNeeded = false; final long drawStartTime; @@ -1495,15 +1543,14 @@ public final class ViewAncestor extends Handler implements ViewParent, int mResizeAlpha; final Paint mResizePaint = new Paint(); - public void onHardwarePreDraw(Canvas canvas) { + public void onHardwarePreDraw(HardwareCanvas canvas) { canvas.translate(0, -mHardwareYOffset); } - public void onHardwarePostDraw(Canvas canvas) { - if (mResizeBitmap != null) { - canvas.translate(0, mHardwareYOffset); + public void onHardwarePostDraw(HardwareCanvas canvas) { + if (mResizeBuffer != null) { mResizePaint.setAlpha(mResizeAlpha); - canvas.drawBitmap(mResizeBitmap, 0, 0, mResizePaint); + canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint); } } @@ -1559,15 +1606,15 @@ public final class ViewAncestor extends Handler implements ViewParent, boolean scalingRequired = mAttachInfo.mScalingRequired; int resizeAlpha = 0; - if (mResizeBitmap != null) { - long deltaTime = SystemClock.uptimeMillis() - mResizeBitmapStartTime; - if (deltaTime < mResizeBitmapDuration) { - float amt = deltaTime/(float)mResizeBitmapDuration; + if (mResizeBuffer != null) { + long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime; + if (deltaTime < mResizeBufferDuration) { + float amt = deltaTime/(float) mResizeBufferDuration; amt = mResizeInterpolator.getInterpolation(amt); animating = true; resizeAlpha = 255 - (int)(amt*255); } else { - disposeResizeBitmap(); + disposeResizeBuffer(); } } @@ -1579,7 +1626,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (mScroller != null) { mScroller.abortAnimation(); } - disposeResizeBitmap(); + disposeResizeBuffer(); } return; } @@ -1863,7 +1910,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (scrollY != mScrollY) { if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old=" + mScrollY + " , new=" + scrollY); - if (!immediate && mResizeBitmap == null) { + if (!immediate && mResizeBuffer == null) { if (mScroller == null) { mScroller = new Scroller(mView.getContext()); } @@ -1909,20 +1956,22 @@ public final class ViewAncestor extends Handler implements ViewParent, public void focusableViewAvailable(View v) { checkThread(); - if (mView != null && !mView.hasFocus()) { - v.requestFocus(); - } else { - // the one case where will transfer focus away from the current one - // is if the current view is a view group that prefers to give focus - // to its children first AND the view is a descendant of it. - mFocusedView = mView.findFocus(); - boolean descendantsHaveDibsOnFocus = - (mFocusedView instanceof ViewGroup) && - (((ViewGroup) mFocusedView).getDescendantFocusability() == - ViewGroup.FOCUS_AFTER_DESCENDANTS); - if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) { - // If a view gets the focus, the listener will be invoked from requestChildFocus() + if (mView != null) { + if (!mView.hasFocus()) { v.requestFocus(); + } else { + // the one case where will transfer focus away from the current one + // is if the current view is a view group that prefers to give focus + // to its children first AND the view is a descendant of it. + mFocusedView = mView.findFocus(); + boolean descendantsHaveDibsOnFocus = + (mFocusedView instanceof ViewGroup) && + (((ViewGroup) mFocusedView).getDescendantFocusability() == + ViewGroup.FOCUS_AFTER_DESCENDANTS); + if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) { + // If a view gets the focus, the listener will be invoked from requestChildFocus() + v.requestFocus(); + } } } } @@ -1986,9 +2035,7 @@ public final class ViewAncestor extends Handler implements ViewParent, // At this point the resources have been updated to // have the most recent config, whatever that is. Use // the on in them which may be newer. - if (mView != null) { - config = mView.getResources().getConfiguration(); - } + config = mView.getResources().getConfiguration(); if (force || mLastConfiguration.diff(config) != 0) { mLastConfiguration.setTo(config); mView.dispatchConfigurationChanged(config); @@ -2207,6 +2254,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) { // The IME is trying to say this event is from the // system! Bad bad bad! + //noinspection UnusedAssignment event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM); } deliverKeyEventPostIme((KeyEvent)msg.obj, false); @@ -2240,7 +2288,7 @@ public final class ViewAncestor extends Handler implements ViewParent, } } - private void startInputEvent(InputEvent event, InputQueue.FinishedCallback finishedCallback) { + private void startInputEvent(InputQueue.FinishedCallback finishedCallback) { if (mFinishedCallback != null) { Slog.w(TAG, "Received a new input event from the input queue but there is " + "already an unfinished input event in progress."); @@ -2454,9 +2502,6 @@ public final class ViewAncestor extends Handler implements ViewParent, if (isDown) { ensureTouchMode(true); } - if(false) { - captureMotionLog("captureDispatchPointer", event); - } // Offset the scroll position. if (mCurScrollY != 0) { @@ -2534,6 +2579,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (sendDone) { finishInputEvent(event, handled); } + //noinspection ConstantConditions if (LOCAL_LOGV || WATCH_POINTER) { if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { Log.i(TAG, "Done dispatching!"); @@ -2859,52 +2905,6 @@ public final class ViewAncestor extends Handler implements ViewParent, return false; } - /** - * log motion events - */ - private static void captureMotionLog(String subTag, MotionEvent ev) { - //check dynamic switch - if (ev == null || - SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) { - return; - } - - StringBuilder sb = new StringBuilder(subTag + ": "); - sb.append(ev.getDownTime()).append(','); - sb.append(ev.getEventTime()).append(','); - sb.append(ev.getAction()).append(','); - sb.append(ev.getX()).append(','); - sb.append(ev.getY()).append(','); - sb.append(ev.getPressure()).append(','); - sb.append(ev.getSize()).append(','); - sb.append(ev.getMetaState()).append(','); - sb.append(ev.getXPrecision()).append(','); - sb.append(ev.getYPrecision()).append(','); - sb.append(ev.getDeviceId()).append(','); - sb.append(ev.getEdgeFlags()); - Log.d(TAG, sb.toString()); - } - /** - * log motion events - */ - private static void captureKeyLog(String subTag, KeyEvent ev) { - //check dynamic switch - if (ev == null || - SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) { - return; - } - StringBuilder sb = new StringBuilder(subTag + ": "); - sb.append(ev.getDownTime()).append(','); - sb.append(ev.getEventTime()).append(','); - sb.append(ev.getAction()).append(','); - sb.append(ev.getKeyCode()).append(','); - sb.append(ev.getRepeatCount()).append(','); - sb.append(ev.getMetaState()).append(','); - sb.append(ev.getDeviceId()).append(','); - sb.append(ev.getScanCode()); - Log.d(TAG, sb.toString()); - } - int enqueuePendingEvent(Object event, boolean sendDone) { int seq = mPendingEventSeq+1; if (seq < 0) seq = 0; @@ -2993,10 +2993,6 @@ public final class ViewAncestor extends Handler implements ViewParent, return; } - if (false) { - captureKeyLog("captureDispatchKeyEvent", event); - } - // Make sure the fallback event policy sees all keys that will be delivered to the // view hierarchy. mFallbackEventHandler.preDispatchKeyEvent(event); @@ -3392,12 +3388,12 @@ public final class ViewAncestor extends Handler implements ViewParent, private final InputHandler mInputHandler = new InputHandler() { public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) { - startInputEvent(event, finishedCallback); + startInputEvent(finishedCallback); dispatchKey(event, true); } public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) { - startInputEvent(event, finishedCallback); + startInputEvent(finishedCallback); dispatchMotion(event, true); } }; @@ -3429,10 +3425,6 @@ public final class ViewAncestor extends Handler implements ViewParent, sendMessageAtTime(msg, event.getEventTime()); } - public void dispatchMotion(MotionEvent event) { - dispatchMotion(event, false); - } - private void dispatchMotion(MotionEvent event, boolean sendDone) { int source = event.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { @@ -3444,10 +3436,6 @@ public final class ViewAncestor extends Handler implements ViewParent, } } - public void dispatchPointer(MotionEvent event) { - dispatchPointer(event, false); - } - private void dispatchPointer(MotionEvent event, boolean sendDone) { Message msg = obtainMessage(DISPATCH_POINTER); msg.obj = event; @@ -3455,10 +3443,6 @@ public final class ViewAncestor extends Handler implements ViewParent, sendMessageAtTime(msg, event.getEventTime()); } - public void dispatchTrackball(MotionEvent event) { - dispatchTrackball(event, false); - } - private void dispatchTrackball(MotionEvent event, boolean sendDone) { Message msg = obtainMessage(DISPATCH_TRACKBALL); msg.obj = event; diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 4aa8727..f014070 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -92,12 +92,6 @@ public class ViewDebug { public static final boolean TRACE_RECYCLER = false; /** - * The system property of dynamic switch for capturing event information - * when it is set, we log key events, touch/motion and trackball events - */ - static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent"; - - /** * Profiles drawing times in the events log. * * @hide diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 6937573..f504b90 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4838,6 +4838,20 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * This method is called by LayoutTransition when there are 'changing' animations that need + * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who + * starts all pending transitions prior to the drawing phase in the current traversal. + * + * @param transition The LayoutTransition to be started on the next traversal. + * + * @hide + */ + public void requestTransitionStart(LayoutTransition transition) { + ViewAncestor viewAncestor = getViewAncestor(); + viewAncestor.requestTransitionStart(transition); + } + + /** * Return true if the pressed state should be delayed for children or descendants of this * ViewGroup. Generally, this should be done for containers that can scroll, such as a List. * This prevents the pressed state from appearing when the user is actually trying to scroll diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index 1d56e9d..9eddf23 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -67,6 +67,19 @@ public class ViewPropertyAnimator { private boolean mDurationSet = false; /** + * The startDelay of the underlying Animator object. By default, we don't set the startDelay + * on the Animator and just use its default startDelay. If the startDelay is ever set on this + * Animator, then we use the startDelay that it was set to. + */ + private long mStartDelay = 0; + + /** + * A flag indicating whether the startDelay has been set on this object. If not, we don't set + * the startDelay on the underlying Animator, but instead just use its default startDelay. + */ + private boolean mStartDelaySet = false; + + /** * The interpolator of the underlying Animator object. By default, we don't set the interpolator * on the Animator and just use its default interpolator. If the interpolator is ever set on * this Animator, then we use the interpolator that it was set to. @@ -233,6 +246,60 @@ public class ViewPropertyAnimator { } /** + * Returns the current duration of property animations. If the duration was set on this + * object, that value is returned. Otherwise, the default value of the underlying Animator + * is returned. + * + * @see #setDuration(long) + * @return The duration of animations, in milliseconds. + */ + public long getDuration() { + if (mStartDelaySet) { + return mStartDelay; + } else { + // Just return the default from ValueAnimator, since that's what we'd get if + // the value has not been set otherwise + return new ValueAnimator().getDuration(); + } + } + + /** + * Returns the current startDelay of property animations. If the startDelay was set on this + * object, that value is returned. Otherwise, the default value of the underlying Animator + * is returned. + * + * @see #setStartDelay(long) + * @return The startDelay of animations, in milliseconds. + */ + public long getStartDelay() { + if (mStartDelaySet) { + return mStartDelay; + } else { + // Just return the default from ValueAnimator (0), since that's what we'd get if + // the value has not been set otherwise + return 0; + } + } + + /** + * Sets the startDelay for the underlying animator that animates the requested properties. + * By default, the animator uses the default value for ValueAnimator. Calling this method + * will cause the declared value to be used instead. + * @param startDelay The delay of ensuing property animations, in milliseconds. The value + * cannot be negative. + * @return This object, allowing calls to methods in this class to be chained. + */ + public ViewPropertyAnimator setStartDelay(long startDelay) { + if (startDelay < 0) { + throw new IllegalArgumentException("Animators cannot have negative duration: " + + startDelay); + } + mStartDelaySet = true; + mStartDelay = startDelay; + return this; + } + + /** * Sets the interpolator for the underlying animator that animates the requested properties. * By default, the animator uses the default interpolator for ValueAnimator. Calling this method * will cause the declared object to be used instead. @@ -259,6 +326,33 @@ public class ViewPropertyAnimator { } /** + * Starts the currently pending property animations immediately. Calling <code>start()</code> + * is optional because all animations start automatically at the next opportunity. However, + * if the animations are needed to start immediately and synchronously (not at the time when + * the next event is processed by the hierarchy, which is when the animations would begin + * otherwise), then this method can be used. + */ + public void start() { + startAnimation(); + } + + /** + * Cancels all property animations that are currently running or pending. + */ + public void cancel() { + if (mAnimatorMap.size() > 0) { + HashMap<Animator, PropertyBundle> mAnimatorMapCopy = + (HashMap<Animator, PropertyBundle>)mAnimatorMap.clone(); + Set<Animator> animatorSet = mAnimatorMapCopy.keySet(); + for (Animator runningAnim : animatorSet) { + runningAnim.cancel(); + } + } + mPendingAnimations.clear(); + mView.getHandler().removeCallbacks(mAnimationStarter); + } + + /** * This method will cause the View's <code>x</code> property to be animated to the * specified value. Animations already running on the property will be canceled. * @@ -598,7 +692,7 @@ public class ViewPropertyAnimator { // on a property will cancel a previous animation on that property, so // there can only ever be one such animation running. if (bundle.mPropertyMask == NONE) { - // the animation is not longer changing anything - cancel it + // the animation is no longer changing anything - cancel it animatorToCancel = runningAnim; break; } diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java index 976e786..12391df 100644 --- a/core/java/android/webkit/JWebCoreJavaBridge.java +++ b/core/java/android/webkit/JWebCoreJavaBridge.java @@ -39,9 +39,6 @@ final class JWebCoreJavaBridge extends Handler { // immediately. private boolean mHasInstantTimer; - // Reference count the pause/resume of timers - private int mPauseTimerRefCount; - private boolean mTimerPaused; private boolean mHasDeferredTimers; @@ -136,7 +133,7 @@ final class JWebCoreJavaBridge extends Handler { * Pause all timers. */ public void pause() { - if (--mPauseTimerRefCount == 0) { + if (!mTimerPaused) { mTimerPaused = true; mHasDeferredTimers = false; } @@ -146,7 +143,7 @@ final class JWebCoreJavaBridge extends Handler { * Resume all timers. */ public void resume() { - if (++mPauseTimerRefCount == 1) { + if (mTimerPaused) { mTimerPaused = false; if (mHasDeferredTimers) { mHasDeferredTimers = false; diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java index f59d7d0..5b4fb1d 100644 --- a/core/java/android/webkit/L10nUtils.java +++ b/core/java/android/webkit/L10nUtils.java @@ -69,7 +69,8 @@ public class L10nUtils { com.android.internal.R.string.autofill_card_number_re, // IDS_AUTOFILL_CARD_NUMBER_RE com.android.internal.R.string.autofill_expiration_month_re, // IDS_AUTOFILL_EXPIRATION_MONTH_RE com.android.internal.R.string.autofill_expiration_date_re, // IDS_AUTOFILL_EXPIRATION_DATE_RE - com.android.internal.R.string.autofill_card_ignored_re // IDS_AUTOFILL_CARD_IGNORED_RE + com.android.internal.R.string.autofill_card_ignored_re, // IDS_AUTOFILL_CARD_IGNORED_RE + com.android.internal.R.string.autofill_fax_re // IDS_AUTOFILL_FAX_RE }; private static Context mApplicationContext; diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 8ffbda2..66fcc27 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -183,7 +183,6 @@ public class WebSettings { private boolean mJavaScriptCanOpenWindowsAutomatically = false; private boolean mUseDoubleTree = false; private boolean mUseWideViewport = false; - private boolean mUseFixedViewport = false; private boolean mSupportMultipleWindows = false; private boolean mShrinksStandaloneImagesToFit = false; private long mMaximumDecodedImageSize = 0; // 0 means default @@ -361,10 +360,9 @@ public class WebSettings { } // User agent strings. - private static final String DESKTOP_USERAGENT = - "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)" - + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0" - + " Safari/530.17"; + private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (X11; " + + "Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) " + + "Chrome/11.0.696.34 Safari/534.24"; private static final String IPHONE_USERAGENT = "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)" + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0" @@ -383,13 +381,6 @@ public class WebSettings { mDefaultTextEncoding = context.getString(com.android.internal. R.string.default_text_encoding); - // Detect tablet device for fixed viewport mode. - final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - final int landscapeWidth = Math.max(metrics.widthPixels, metrics.heightPixels); - final int minTabletWidth = context.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.min_xlarge_screen_width); - mUseFixedViewport = (metrics.density == 1.0f && landscapeWidth >= minTabletWidth); - if (sLockForLocaleSettings == null) { sLockForLocaleSettings = new Object(); sLocale = Locale.getDefault(); @@ -1652,11 +1643,11 @@ public class WebSettings { } /** - * Returns whether to use fixed viewport. Fixed viewport should operate only - * when wide viewport is on. + * Returns whether to use fixed viewport. Use fixed viewport + * whenever wide viewport is on. */ /* package */ boolean getUseFixedViewport() { - return getUseWideViewPort() && mUseFixedViewport; + return getUseWideViewPort(); } /** diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index f774803..61a69ca 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -54,7 +54,9 @@ import android.net.http.SslCertificate; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.os.Message; +import android.os.StrictMode; import android.provider.Settings; import android.speech.tts.TextToSpeech; import android.text.Selection; @@ -8933,15 +8935,14 @@ public class WebView extends AbsoluteLayout } private static void checkThread() { - if (!"main".equals(Thread.currentThread().getName())) { - try { - throw new RuntimeException("A WebView method was called on thread '" + - Thread.currentThread().getName() + "'. " + - "All WebView methods must be called on the UI thread. " + - "Future versions of WebView may not support use on other threads."); - } catch (RuntimeException e) { - Log.e(LOGTAG, Log.getStackTraceString(e)); - } + if (Looper.myLooper() != Looper.getMainLooper()) { + RuntimeException exception = new RuntimeException( + "A WebView method was called on thread '" + + Thread.currentThread().getName() + "'. " + + "All WebView methods must be called on the UI thread. " + + "Future versions of WebView may not support use on other threads."); + Log.e(LOGTAG, Log.getStackTraceString(exception)); + StrictMode.onWebViewMethodCalledOnWrongThread(exception); } } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index e8083eb..db5ff54 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -1865,7 +1865,7 @@ public final class WebViewCore { Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); return; } - int width = calculateWindowWidth(w, textwrapWidth); + int width = calculateWindowWidth(w); int height = h; if (width != w) { float heightWidthRatio = data.mHeightWidthRatio; @@ -1891,41 +1891,18 @@ public final class WebViewCore { } // Calculate width to be used in webkit window. - private int calculateWindowWidth(int viewWidth, int textwrapWidth) { + private int calculateWindowWidth(int viewWidth) { int width = viewWidth; if (mSettings.getUseWideViewPort()) { if (mViewportWidth == -1) { - if (mSettings.getLayoutAlgorithm() == - WebSettings.LayoutAlgorithm.NORMAL || mSettings.getUseFixedViewport()) { - width = WebView.DEFAULT_VIEWPORT_WIDTH; - } else { - /* - * if a page's minimum preferred width is wider than the - * given "w", use it instead to get better layout result. If - * we start a page with MAX_ZOOM_WIDTH, "w" will be always - * wider. If we start a page with screen width, due to the - * delay between {@link #didFirstLayout} and - * {@link #viewSizeChanged}, - * {@link #nativeGetContentMinPrefWidth} will return a more - * accurate value than initial 0 to result a better layout. - * In the worse case, the native width will be adjusted when - * next zoom or screen orientation change happens. - */ - width = Math.min(WebView.sMaxViewportWidth, Math.max(viewWidth, - Math.max(WebView.DEFAULT_VIEWPORT_WIDTH, - nativeGetContentMinPrefWidth()))); - } + // Fixed viewport width. + width = WebView.DEFAULT_VIEWPORT_WIDTH; } else if (mViewportWidth > 0) { - if (mSettings.getUseFixedViewport()) { - // Use website specified or desired fixed viewport width. - width = mViewportWidth; - } else { - width = Math.max(viewWidth, mViewportWidth); - } - } else if (mSettings.getUseFixedViewport()) { - width = mWebView.getViewWidth(); + // Use website specified or desired fixed viewport width. + width = mViewportWidth; } else { - width = textwrapWidth; + // For mobile web site. + width = viewWidth; } } return width; @@ -2025,7 +2002,8 @@ public final class WebViewCore { if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start"); draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mContentSize); if (draw.mBaseLayer == 0) { - if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort"); + if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message"); + mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); return; } webkitDraw(draw); @@ -2439,8 +2417,7 @@ public final class WebViewCore { // in zoom overview mode. tentativeScale = mInitialViewState.mTextWrapScale; int tentativeViewWidth = Math.round(webViewWidth / tentativeScale); - int windowWidth = calculateWindowWidth(tentativeViewWidth, - tentativeViewWidth); + int windowWidth = calculateWindowWidth(tentativeViewWidth); // In viewport setup time, since no content width is known, we assume // the windowWidth will be the content width, to get a more likely // zoom overview scale. @@ -2449,8 +2426,7 @@ public final class WebViewCore { // If user choose non-overview mode. data.mScale = Math.max(data.mScale, tentativeScale); } - if (mSettings.isNarrowColumnLayout() && - mSettings.getUseFixedViewport()) { + if (mSettings.isNarrowColumnLayout()) { // In case of automatic text reflow in fixed view port mode. mInitialViewState.mTextWrapScale = ZoomManager.computeReadingLevelScale(data.mScale); diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index f2a1ec3..e330737 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -1019,19 +1019,11 @@ class ZoomManager { WebSettings settings = mWebView.getSettings(); int newZoomOverviewWidth = mZoomOverviewWidth; if (settings.getUseWideViewPort()) { - if (!settings.getUseFixedViewport()) { - // limit mZoomOverviewWidth upper bound to - // sMaxViewportWidth so that if the page doesn't behave - // well, the WebView won't go insane. limit the lower - // bound to match the default scale for mobile sites. - newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth, - Math.max((int) (viewWidth * mInvDefaultScale), - Math.max(drawData.mMinPrefWidth, drawData.mViewSize.x))); - } else if (drawData.mContentSize.x > 0) { + if (drawData.mContentSize.x > 0) { // The webkitDraw for layers will not populate contentSize, and it'll be // ignored for zoom overview width update. - final int contentWidth = Math.max(drawData.mContentSize.x, drawData.mMinPrefWidth); - newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth, contentWidth); + newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth, + drawData.mContentSize.x); } } else { // If not use wide viewport, use view width as the zoom overview width. diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 86fefaf..6f30452 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -696,7 +696,8 @@ public class LinearLayout extends ViewGroup { mTotalLength += mDividerHeight; } - if (useLargestChild && heightMode == MeasureSpec.AT_MOST) { + if (useLargestChild && + (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) { mTotalLength = 0; for (int i = 0; i < count; ++i) { @@ -809,6 +810,31 @@ public class LinearLayout extends ViewGroup { } else { alternativeMaxWidth = Math.max(alternativeMaxWidth, weightedMaxWidth); + + + // We have no limit, so make all weighted views as tall as the largest child. + // Children will have already been measured once. + if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) { + for (int i = 0; i < count; i++) { + final View child = getVirtualChildAt(i); + + if (child == null || child.getVisibility() == View.GONE) { + continue; + } + + final LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) child.getLayoutParams(); + + float childExtra = lp.weight; + if (childExtra > 0) { + child.measure( + MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(), + MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(largestChildHeight, + MeasureSpec.EXACTLY)); + } + } + } } if (!allFillParent && widthMode != MeasureSpec.EXACTLY) { @@ -1044,7 +1070,8 @@ public class LinearLayout extends ViewGroup { maxHeight = Math.max(maxHeight, ascent + descent); } - if (useLargestChild && widthMode == MeasureSpec.AT_MOST) { + if (useLargestChild && + (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) { mTotalLength = 0; for (int i = 0; i < count; ++i) { @@ -1200,6 +1227,29 @@ public class LinearLayout extends ViewGroup { } } else { alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight); + + // We have no limit, so make all weighted views as wide as the largest child. + // Children will have already been measured once. + if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) { + for (int i = 0; i < count; i++) { + final View child = getVirtualChildAt(i); + + if (child == null || child.getVisibility() == View.GONE) { + continue; + } + + final LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) child.getLayoutParams(); + + float childExtra = lp.weight; + if (childExtra > 0) { + child.measure( + MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), + MeasureSpec.EXACTLY)); + } + } + } } if (!allFillParent && heightMode != MeasureSpec.EXACTLY) { @@ -1331,7 +1381,7 @@ public class LinearLayout extends ViewGroup { void layoutVertical() { final int paddingLeft = mPaddingLeft; - int childTop = mPaddingTop; + int childTop; int childLeft; // Where right end of child should go @@ -1346,19 +1396,21 @@ public class LinearLayout extends ViewGroup { final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; final int minorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK; - if (majorGravity != Gravity.TOP) { - switch (majorGravity) { - case Gravity.BOTTOM: - // mTotalLength contains the padding already, we add the top - // padding to compensate - childTop = mBottom - mTop + mPaddingTop - mTotalLength; - break; - - case Gravity.CENTER_VERTICAL: - childTop += ((mBottom - mTop) - mTotalLength) / 2; - break; - } - + switch (majorGravity) { + case Gravity.BOTTOM: + // mTotalLength contains the padding already + childTop = mPaddingTop + mBottom - mTop - mTotalLength; + break; + + // mTotalLength contains the padding already + case Gravity.CENTER_VERTICAL: + childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2; + break; + + case Gravity.TOP: + default: + childTop = mPaddingTop; + break; } for (int i = 0; i < count; i++) { @@ -1376,12 +1428,8 @@ public class LinearLayout extends ViewGroup { if (gravity < 0) { gravity = minorGravity; } - - switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.LEFT: - childLeft = paddingLeft + lp.leftMargin; - break; + switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: childLeft = paddingLeft + ((childSpace - childWidth) / 2) + lp.leftMargin - lp.rightMargin; @@ -1390,11 +1438,13 @@ public class LinearLayout extends ViewGroup { case Gravity.RIGHT: childLeft = childRight - childWidth - lp.rightMargin; break; + + case Gravity.LEFT: default: - childLeft = paddingLeft; + childLeft = paddingLeft + lp.leftMargin; break; } - + if (hasDividerBeforeChildAt(i)) { childTop += mDividerHeight; } @@ -1418,10 +1468,11 @@ public class LinearLayout extends ViewGroup { * @see #onLayout(boolean, int, int, int, int) */ void layoutHorizontal() { + final boolean isLayoutRtl = isLayoutRtl(); final int paddingTop = mPaddingTop; int childTop; - int childLeft = mPaddingLeft; + int childLeft; // Where bottom of child should go final int height = mBottom - mTop; @@ -1440,25 +1491,37 @@ public class LinearLayout extends ViewGroup { final int[] maxAscent = mMaxAscent; final int[] maxDescent = mMaxDescent; - if (majorGravity != Gravity.LEFT) { - switch (majorGravity) { - case Gravity.RIGHT: - // mTotalLength contains the padding already, we add the left - // padding to compensate - childLeft = mRight - mLeft + mPaddingLeft - mTotalLength; - break; - - case Gravity.CENTER_HORIZONTAL: - childLeft += ((mRight - mLeft) - mTotalLength) / 2; - break; - } + switch (majorGravity) { + case Gravity.RIGHT: + // mTotalLength contains the padding already + childLeft = mPaddingLeft + mRight - mLeft - mTotalLength; + break; + + case Gravity.CENTER_HORIZONTAL: + // mTotalLength contains the padding already + childLeft = mPaddingLeft + (mRight - mLeft - mTotalLength) / 2; + break; + + case Gravity.LEFT: + default: + childLeft = mPaddingLeft; + break; + } + + int start = 0; + int dir = 1; + //In case of RTL, start drawing from the last child. + if (isLayoutRtl) { + start = count - 1; + dir = -1; } for (int i = 0; i < count; i++) { - final View child = getVirtualChildAt(i); + int childIndex = start + dir * i; + final View child = getVirtualChildAt(childIndex); if (child == null) { - childLeft += measureNullChild(i); + childLeft += measureNullChild(childIndex); } else if (child.getVisibility() != GONE) { final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); @@ -1512,7 +1575,7 @@ public class LinearLayout extends ViewGroup { break; } - if (hasDividerBeforeChildAt(i)) { + if (hasDividerBeforeChildAt(childIndex)) { childLeft += mDividerWidth; } @@ -1522,7 +1585,7 @@ public class LinearLayout extends ViewGroup { childLeft += childWidth + lp.rightMargin + getNextLocationOffset(child); - i += getChildrenSkipCount(child, i); + i += getChildrenSkipCount(child, childIndex); } } } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 1e1a043..563fc26 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1171,8 +1171,7 @@ public class PopupWindow { int bottomEdge = displayFrame.bottom; if (ignoreBottomDecorations) { Resources res = anchor.getContext().getResources(); - bottomEdge = res.getDisplayMetrics().heightPixels - - (int) res.getDimension(com.android.internal.R.dimen.screen_margin_bottom); + bottomEdge = res.getDisplayMetrics().heightPixels; } final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset; final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset; diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java index 71c91e1..c4ba7c8 100644 --- a/core/java/android/widget/StackView.java +++ b/core/java/android/widget/StackView.java @@ -116,7 +116,7 @@ public class StackView extends AdapterViewAnimator { private static final int MIN_TIME_BETWEEN_INTERACTION_AND_AUTOADVANCE = 5000; - private static long MIN_TIME_BETWEEN_SCROLLS = 100; + private static final long MIN_TIME_BETWEEN_SCROLLS = 100; /** * These variables are all related to the current state of touch interaction @@ -213,8 +213,7 @@ public class StackView extends AdapterViewAnimator { * Animate the views between different relative indexes within the {@link AdapterViewAnimator} */ void transformViewForTransition(int fromIndex, int toIndex, final View view, boolean animate) { - ObjectAnimator alphaOa = null; - ObjectAnimator oldAlphaOa = null; + ObjectAnimator alphaOa; if (!animate) { ((StackFrame) view).cancelSliderAnimator(); @@ -1276,13 +1275,11 @@ public class StackView extends AdapterViewAnimator { boolean firstPass = true; parentRect.set(0, 0, 0, 0); - int depth = 0; while (p.getParent() != null && p.getParent() instanceof View && !parentRect.contains(globalInvalidateRect)) { if (!firstPass) { globalInvalidateRect.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY()); - depth++; } firstPass = false; p = (View) p.getParent(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index d58c72b..3875765 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -16,11 +16,6 @@ package android.widget; -import com.android.internal.util.FastMath; -import com.android.internal.widget.EditableInputConnection; - -import org.xmlpull.v1.XmlPullParserException; - import android.R; import android.content.ClipData; import android.content.ClipData.Item; @@ -129,6 +124,11 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.RemoteViews.RemoteView; +import com.android.internal.util.FastMath; +import com.android.internal.widget.EditableInputConnection; + +import org.xmlpull.v1.XmlPullParserException; + import java.io.IOException; import java.lang.ref.WeakReference; import java.text.BreakIterator; @@ -320,6 +320,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mTextEditSuggestionItemLayout; private SuggestionsPopupWindow mSuggestionsPopupWindow; private SuggestionRangeSpan mSuggestionRangeSpan; + private boolean mSuggestionsEnabled = true; private int mCursorDrawableRes; private final Drawable[] mCursorDrawable = new Drawable[2]; @@ -329,7 +330,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private Drawable mSelectHandleRight; private Drawable mSelectHandleCenter; - private int mLastDownPositionX, mLastDownPositionY; + private float mLastDownPositionX, mLastDownPositionY; private Callback mCustomSelectionActionModeCallback; private final int mSquaredTouchSlopDistance; @@ -806,6 +807,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextView_textIsSelectable: mTextIsSelectable = a.getBoolean(attr, false); break; + + case com.android.internal.R.styleable.TextView_suggestionsEnabled: + mSuggestionsEnabled = a.getBoolean(attr, true); + break; } } a.recycle(); @@ -2898,8 +2903,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setText(mCharWrapper, mBufferType, false, oldlen); } - private static class CharWrapper - implements CharSequence, GetChars, GraphicsOperations { + private static class CharWrapper implements CharSequence, GetChars, GraphicsOperations { private char[] mChars; private int mStart, mLength; @@ -7323,8 +7327,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (action == MotionEvent.ACTION_DOWN) { - mLastDownPositionX = (int) event.getX(); - mLastDownPositionY = (int) event.getY(); + mLastDownPositionX = event.getX(); + mLastDownPositionY = event.getY(); // Reset this state; it will be re-set if super.onTouchEvent // causes focus to move to the view. @@ -7758,16 +7762,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener hasPrimaryClip()); } - private boolean isWordCharacter(int c, int type) { - return (c == '\'' || c == '"' || - type == Character.UPPERCASE_LETTER || - type == Character.LOWERCASE_LETTER || - type == Character.TITLECASE_LETTER || - type == Character.MODIFIER_LETTER || - type == Character.OTHER_LETTER || // Should handle asian characters - type == Character.DECIMAL_DIGIT_NUMBER); - } - private static long packRangeInLong(int start, int end) { return (((long) start) << 32) | end; } @@ -8140,7 +8134,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Long press in empty space moves cursor and shows the Paste affordance if available. if (!isPositionOnText(mLastDownPositionX, mLastDownPositionY) && mInsertionControllerEnabled) { - final int offset = getOffset(mLastDownPositionX, mLastDownPositionY); + final int offset = getOffsetForPosition(mLastDownPositionX, mLastDownPositionY); stopSelectionActionMode(); Selection.setSelection((Spannable) mText, offset); getInsertionController().showWithPaste(); @@ -8208,7 +8202,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private final ViewGroup[] mSuggestionViews = new ViewGroup[2]; private final int[] mSuggestionViewLayouts = new int[] { mTextEditSuggestionsBottomWindowLayout, mTextEditSuggestionsTopWindowLayout}; - private WordIterator mWordIterator; + private WordIterator mSuggestionWordIterator; private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan[0]; public SuggestionsPopupWindow() { @@ -8344,26 +8338,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private long[] getWordLimits(CharSequence text) { - if (mWordIterator == null) mWordIterator = new WordIterator(); // TODO locale - mWordIterator.setCharSequence(text); + // TODO locale for mSuggestionWordIterator + if (mSuggestionWordIterator == null) mSuggestionWordIterator = new WordIterator(); + mSuggestionWordIterator.setCharSequence(text); // First pass will simply count the number of words to be able to create an array // Not too expensive since previous break positions are cached by the BreakIterator int nbWords = 0; - int position = mWordIterator.following(0); + int position = mSuggestionWordIterator.following(0); while (position != BreakIterator.DONE) { nbWords++; - position = mWordIterator.following(position); + position = mSuggestionWordIterator.following(position); } int index = 0; long[] result = new long[nbWords]; - position = mWordIterator.following(0); + position = mSuggestionWordIterator.following(0); while (position != BreakIterator.DONE) { - int wordStart = mWordIterator.getBeginning(position); + int wordStart = mSuggestionWordIterator.getBeginning(position); result[index++] = packRangeInLong(wordStart, position); - position = mWordIterator.following(position); + position = mSuggestionWordIterator.following(position); } return result; @@ -8601,6 +8596,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } void showSuggestions() { + if (!mSuggestionsEnabled || !isTextEditable()) return; + if (mSuggestionsPopupWindow == null) { mSuggestionsPopupWindow = new SuggestionsPopupWindow(); } @@ -8615,6 +8612,31 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Some parts of the text can have alternate suggestion text attached. This is typically done by + * the IME by adding {@link SuggestionSpan}s to the text. + * + * When suggestions are enabled (default), this list of suggestions will be displayed when the + * user double taps on these parts of the text. No suggestions are displayed when this value is + * false. Use {@link #setSuggestionsEnabled(boolean)} to change this value. + * + * @return true if the suggestions popup window is enabled. + * + * @attr ref android.R.styleable#TextView_suggestionsEnabled + */ + public boolean isSuggestionsEnabled() { + return mSuggestionsEnabled; + } + + /** + * Enables or disables the suggestion popup. See {@link #isSuggestionsEnabled()}. + * + * @param enabled Whether or not suggestions are enabled. + */ + public void setSuggestionsEnabled(boolean enabled) { + mSuggestionsEnabled = enabled; + } + + /** * If provided, this ActionMode.Callback will be used to create the ActionMode when text * selection is initiated in this View. * @@ -9115,7 +9137,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public abstract void updateOffset(int offset); - public abstract void updatePosition(int x, int y); + public abstract void updatePosition(float x, float y); protected void positionAtCursorOffset(int offset) { addPositionToTouchUpFilter(offset); @@ -9215,7 +9237,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX; final float newPosY = rawY - mTouchToWindowOffsetY + mTouchOffsetY; - updatePosition(Math.round(newPosX), Math.round(newPosY)); + updatePosition(newPosX, newPosY); break; } @@ -9366,8 +9388,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public void updatePosition(int x, int y) { - updateOffset(getOffset(x, y)); + public void updatePosition(float x, float y) { + updateOffset(getOffsetForPosition(x, y)); } void showPastePopupWindow() { @@ -9421,11 +9443,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public void updatePosition(int x, int y) { + public void updatePosition(float x, float y) { final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); - int offset = getOffset(x, y); + int offset = getOffsetForPosition(x, y); // No need to redraw when the offset is unchanged if (offset == selectionStart) return; @@ -9458,11 +9480,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public void updatePosition(int x, int y) { + public void updatePosition(float x, float y) { final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); - int offset = getOffset(x, y); + int offset = getOffsetForPosition(x, y); // No need to redraw when the offset is unchanged if (offset == selectionEnd) return; @@ -9560,7 +9582,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Double tap detection private long mPreviousTapUpTime = 0; - private int mPreviousTapPositionX, mPreviousTapPositionY; + private float mPreviousTapPositionX, mPreviousTapPositionY; SelectionModifierCursorController() { resetTouchOffsets(); @@ -9593,19 +9615,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (isTextEditable() || mTextIsSelectable) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: - final int x = (int) event.getX(); - final int y = (int) event.getY(); + final float x = event.getX(); + final float y = event.getY(); // Remember finger down position, to be able to start selection from there - mMinTouchOffset = mMaxTouchOffset = getOffset(x, y); + mMinTouchOffset = mMaxTouchOffset = getOffsetForPosition(x, y); // Double tap detection long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime; if (duration <= ViewConfiguration.getDoubleTapTimeout() && isPositionOnText(x, y)) { - final int deltaX = x - mPreviousTapPositionX; - final int deltaY = y - mPreviousTapPositionY; - final int distanceSquared = deltaX * deltaX + deltaY * deltaY; + final float deltaX = x - mPreviousTapPositionX; + final float deltaY = y - mPreviousTapPositionY; + final float distanceSquared = deltaX * deltaX + deltaY * deltaY; if (distanceSquared < mSquaredTouchSlopDistance) { showSuggestions(); mDiscardNextActionUp = true; @@ -9641,9 +9663,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void updateMinAndMaxOffsets(MotionEvent event) { int pointerCount = event.getPointerCount(); for (int index = 0; index < pointerCount; index++) { - final int x = (int) event.getX(index); - final int y = (int) event.getY(index); - int offset = getOffset(x, y); + int offset = getOffsetForPosition(event.getX(index), event.getY(index)); if (offset < mMinTouchOffset) mMinTouchOffset = offset; if (offset > mMaxTouchOffset) mMaxTouchOffset = offset; } @@ -9701,41 +9721,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Get the offset character closest to the specified absolute position. + * Get the character offset closest to the specified absolute position. A typical use case is to + * pass the result of {@link MotionEvent#getX()} and {@link MotionEvent#getY()} to this method. * * @param x The horizontal absolute position of a point on screen * @param y The vertical absolute position of a point on screen * @return the character offset for the character whose position is closest to the specified * position. Returns -1 if there is no layout. - * - * @hide */ - public int getOffset(int x, int y) { + public int getOffsetForPosition(float x, float y) { if (getLayout() == null) return -1; final int line = getLineAtCoordinate(y); final int offset = getOffsetAtCoordinate(line, x); return offset; } - private int convertToLocalHorizontalCoordinate(int x) { + private float convertToLocalHorizontalCoordinate(float x) { x -= getTotalPaddingLeft(); // Clamp the position to inside of the view. - x = Math.max(0, x); + x = Math.max(0.0f, x); x = Math.min(getWidth() - getTotalPaddingRight() - 1, x); x += getScrollX(); return x; } - private int getLineAtCoordinate(int y) { + private int getLineAtCoordinate(float y) { y -= getTotalPaddingTop(); // Clamp the position to inside of the view. - y = Math.max(0, y); + y = Math.max(0.0f, y); y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y); y += getScrollY(); - return getLayout().getLineForVertical(y); + return getLayout().getLineForVertical((int) y); } - private int getOffsetAtCoordinate(int line, int x) { + private int getOffsetAtCoordinate(int line, float x) { x = convertToLocalHorizontalCoordinate(x); return getLayout().getOffsetForHorizontal(line, x); } @@ -9743,7 +9762,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** Returns true if the screen coordinates position (x,y) corresponds to a character displayed * in the view. Returns false when the position is in the empty space of left/right of text. */ - private boolean isPositionOnText(int x, int y) { + private boolean isPositionOnText(float x, float y) { if (getLayout() == null) return false; final int line = getLineAtCoordinate(y); @@ -9765,7 +9784,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; case DragEvent.ACTION_DRAG_LOCATION: - final int offset = getOffset((int) event.getX(), (int) event.getY()); + final int offset = getOffsetForPosition(event.getX(), event.getY()); Selection.setSelection((Spannable)mText, offset); return true; @@ -9789,7 +9808,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener content.append(item.coerceToText(TextView.this.mContext)); } - final int offset = getOffset((int) event.getX(), (int) event.getY()); + final int offset = getOffsetForPosition(event.getX(), event.getY()); Object localState = event.getLocalState(); DragLocalState dragLocalState = null; diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 1e576ce..183cfbd 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -16,25 +16,27 @@ package com.android.internal.app; +import com.android.internal.R; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuPopupHelper; import com.android.internal.view.menu.SubMenuBuilder; -import com.android.internal.widget.AbsActionBarView; import com.android.internal.widget.ActionBarContainer; import com.android.internal.widget.ActionBarContextView; import com.android.internal.widget.ActionBarView; +import com.android.internal.widget.ScrollingTabContainerView; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.animation.TimeInterpolator; import android.app.ActionBar; import android.app.Activity; import android.app.Dialog; import android.app.FragmentTransaction; import android.content.Context; +import android.content.res.Configuration; +import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Handler; import android.view.ActionMode; @@ -43,10 +45,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.view.Window; -import android.view.animation.DecelerateInterpolator; -import android.widget.HorizontalScrollView; import android.widget.SpinnerAdapter; import java.lang.ref.WeakReference; @@ -71,7 +70,7 @@ public class ActionBarImpl extends ActionBar { private ActionBarContextView mContextView; private ActionBarContainer mSplitView; private View mContentView; - private ViewGroup mExternalTabView; + private ScrollingTabContainerView mTabScrollView; private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>(); @@ -90,16 +89,17 @@ public class ActionBarImpl extends ActionBar { private static final int INVALID_POSITION = -1; private int mContextDisplayMode; + private boolean mHasEmbeddedTabs; + private int mContentHeight; final Handler mHandler = new Handler(); + Runnable mTabSelector; private Animator mCurrentShowAnim; private Animator mCurrentModeAnim; private boolean mShowHideAnimationEnabled; boolean mWasHiddenBeforeMode; - private static final TimeInterpolator sFadeOutInterpolator = new DecelerateInterpolator(); - final AnimatorListener mHideListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -150,21 +150,59 @@ public class ActionBarImpl extends ActionBar { "with a compatible window decor layout"); } + mHasEmbeddedTabs = mContext.getResources().getBoolean( + com.android.internal.R.bool.action_bar_embed_tabs); mActionView.setContextView(mContextView); mContextDisplayMode = mActionView.isSplitActionBar() ? CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL; - if (!mActionView.hasEmbeddedTabs()) { - HorizontalScrollView tabScroller = new HorizontalScrollView(mContext); - ViewGroup tabContainer = mActionView.createTabContainer(); - tabScroller.setHorizontalFadingEdgeEnabled(true); - tabScroller.addView(tabContainer); + TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar); + mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0); + a.recycle(); + } + + public void onConfigurationChanged(Configuration newConfig) { + mHasEmbeddedTabs = mContext.getResources().getBoolean( + com.android.internal.R.bool.action_bar_embed_tabs); + + // Switch tab layout configuration if needed + if (!mHasEmbeddedTabs) { + mActionView.setEmbeddedTabView(null); + mContainerView.setTabContainer(mTabScrollView); + } else { + mContainerView.setTabContainer(null); + if (mTabScrollView != null) { + mTabScrollView.setVisibility(View.VISIBLE); + } + mActionView.setEmbeddedTabView(mTabScrollView); + } + + TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar); + mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0); + a.recycle(); + + if (mTabScrollView != null) { + mTabScrollView.getLayoutParams().height = mContentHeight; + mTabScrollView.requestLayout(); + } + } + + private void ensureTabsExist() { + if (mTabScrollView != null) { + return; + } + + ScrollingTabContainerView tabScroller = mActionView.createTabContainer(); + + if (mHasEmbeddedTabs) { + tabScroller.setVisibility(View.VISIBLE); + mActionView.setEmbeddedTabView(tabScroller); + } else { tabScroller.setVisibility(getNavigationMode() == NAVIGATION_MODE_TABS ? View.VISIBLE : View.GONE); - mActionView.setExternalTabLayout(tabContainer); mContainerView.setTabContainer(tabScroller); - mExternalTabView = tabScroller; } + mTabScrollView = tabScroller; } /** @@ -269,7 +307,7 @@ public class ActionBarImpl extends ActionBar { selectTab(null); } mTabs.clear(); - mActionView.removeAllTabs(); + mTabScrollView.removeAllTabs(); mSavedTabPosition = INVALID_POSITION; } @@ -365,7 +403,8 @@ public class ActionBarImpl extends ActionBar { @Override public void addTab(Tab tab, boolean setSelected) { - mActionView.addTab(tab, setSelected); + ensureTabsExist(); + mTabScrollView.addTab(tab, setSelected); configureTab(tab, mTabs.size()); if (setSelected) { selectTab(tab); @@ -374,7 +413,8 @@ public class ActionBarImpl extends ActionBar { @Override public void addTab(Tab tab, int position, boolean setSelected) { - mActionView.addTab(tab, position, setSelected); + ensureTabsExist(); + mTabScrollView.addTab(tab, position, setSelected); configureTab(tab, position); if (setSelected) { selectTab(tab); @@ -393,9 +433,14 @@ public class ActionBarImpl extends ActionBar { @Override public void removeTabAt(int position) { + if (mTabScrollView == null) { + // No tabs around to remove + return; + } + int selectedTabPosition = mSelectedTab != null ? mSelectedTab.getPosition() : mSavedTabPosition; - mActionView.removeTabAt(position); + mTabScrollView.removeTabAt(position); TabImpl removedTab = mTabs.remove(position); if (removedTab != null) { removedTab.setPosition(-1); @@ -424,9 +469,10 @@ public class ActionBarImpl extends ActionBar { if (mSelectedTab == tab) { if (mSelectedTab != null) { mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans); + mTabScrollView.animateToTab(tab.getPosition()); } } else { - mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION); + mTabScrollView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION); if (mSelectedTab != null) { mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans); } @@ -705,7 +751,9 @@ public class ActionBarImpl extends ActionBar { @Override public Tab setCustomView(View view) { mCustomView = view; - if (mPosition >= 0) mActionView.updateTab(mPosition); + if (mPosition >= 0) { + mTabScrollView.updateTab(mPosition); + } return this; } @@ -736,7 +784,9 @@ public class ActionBarImpl extends ActionBar { @Override public Tab setIcon(Drawable icon) { mIcon = icon; - if (mPosition >= 0) mActionView.updateTab(mPosition); + if (mPosition >= 0) { + mTabScrollView.updateTab(mPosition); + } return this; } @@ -748,7 +798,9 @@ public class ActionBarImpl extends ActionBar { @Override public Tab setText(CharSequence text) { mText = text; - if (mPosition >= 0) mActionView.updateTab(mPosition); + if (mPosition >= 0) { + mTabScrollView.updateTab(mPosition); + } return this; } @@ -818,15 +870,16 @@ public class ActionBarImpl extends ActionBar { mSavedTabPosition = getSelectedNavigationIndex(); selectTab(null); if (!mActionView.hasEmbeddedTabs()) { - mExternalTabView.setVisibility(View.GONE); + mTabScrollView.setVisibility(View.GONE); } break; } mActionView.setNavigationMode(mode); switch (mode) { case NAVIGATION_MODE_TABS: + ensureTabsExist(); if (!mActionView.hasEmbeddedTabs()) { - mExternalTabView.setVisibility(View.VISIBLE); + mTabScrollView.setVisibility(View.VISIBLE); } if (mSavedTabPosition != INVALID_POSITION) { setSelectedNavigationItem(mSavedTabPosition); diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 0f086f6..5e9cd23 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -32,7 +32,6 @@ import dalvik.system.VMRuntime; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.LogManager; import java.util.TimeZone; @@ -45,6 +44,7 @@ import org.apache.harmony.luni.internal.util.TimezoneGetter; */ public class RuntimeInit { private final static String TAG = "AndroidRuntime"; + private final static boolean DEBUG = false; /** true if commonInit() has been called */ private static boolean initialized; @@ -89,14 +89,14 @@ public class RuntimeInit { } private static final void commonInit() { - if (false) Slog.d(TAG, "Entered RuntimeInit!"); + if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!"); /* set default handler; this applies to all threads in the VM */ Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); int hasQwerty = getQwertyKeyboard(); - if (false) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty); + if (DEBUG) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty); if (hasQwerty == 1) { System.setProperty("qwerty", "1"); } @@ -183,11 +183,6 @@ public class RuntimeInit { */ private static void invokeStaticMain(String className, String[] argv) throws ZygoteInit.MethodAndArgsCaller { - - // We want to be fairly aggressive about heap utilization, to avoid - // holding on to a lot of memory that isn't needed. - VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); - Class<?> cl; try { @@ -225,6 +220,13 @@ public class RuntimeInit { } public static final void main(String[] argv) { + if (argv.length == 2 && argv[1].equals("application")) { + if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application"); + redirectLogStreams(); + } else { + if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool"); + } + commonInit(); /* @@ -233,7 +235,7 @@ public class RuntimeInit { */ finishInit(); - if (false) Slog.d(TAG, "Leaving RuntimeInit!"); + if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!"); } public static final native void finishInit(); @@ -245,7 +247,6 @@ public class RuntimeInit { * * Current recognized args: * <ul> - * <li> --nice-name=<i>nice name to appear in ps</i> * <li> <code> [--] <start class name> <args> * </ul> * @@ -253,45 +254,60 @@ public class RuntimeInit { */ public static final void zygoteInit(String[] argv) throws ZygoteInit.MethodAndArgsCaller { - // TODO: Doing this here works, but it seems kind of arbitrary. Find - // a better place. The goal is to set it up for applications, but not - // tools like am. - System.out.close(); - System.setOut(new AndroidPrintStream(Log.INFO, "System.out")); - System.err.close(); - System.setErr(new AndroidPrintStream(Log.WARN, "System.err")); + if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote"); + + redirectLogStreams(); commonInit(); zygoteInitNative(); - int curArg = 0; - for ( /* curArg */ ; curArg < argv.length; curArg++) { - String arg = argv[curArg]; - - if (arg.equals("--")) { - curArg++; - break; - } else if (!arg.startsWith("--")) { - break; - } else if (arg.startsWith("--nice-name=")) { - String niceName = arg.substring(arg.indexOf('=') + 1); - Process.setArgV0(niceName); - } - } + applicationInit(argv); + } - if (curArg == argv.length) { - Slog.e(TAG, "Missing classname argument to RuntimeInit!"); + /** + * The main function called when an application is started through a + * wrapper process. + * + * When the wrapper starts, the runtime starts {@link RuntimeInit#main} + * which calls {@link WrapperInit#main} which then calls this method. + * So we don't need to call commonInit() here. + * + * @param argv arg strings + */ + public static void wrapperInit(String[] argv) + throws ZygoteInit.MethodAndArgsCaller { + if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper"); + + applicationInit(argv); + } + + private static void applicationInit(String[] argv) + throws ZygoteInit.MethodAndArgsCaller { + // We want to be fairly aggressive about heap utilization, to avoid + // holding on to a lot of memory that isn't needed. + VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); + + final Arguments args; + try { + args = new Arguments(argv); + } catch (IllegalArgumentException ex) { + Slog.e(TAG, ex.getMessage()); // let the process exit return; } // Remaining arguments are passed to the start class's static main + invokeStaticMain(args.startClass, args.startArgs); + } - String startClass = argv[curArg++]; - String[] startArgs = new String[argv.length - curArg]; - - System.arraycopy(argv, curArg, startArgs, 0, startArgs.length); - invokeStaticMain(startClass, startArgs); + /** + * Redirect System.out and System.err to the Android log. + */ + public static void redirectLogStreams() { + System.out.close(); + System.setOut(new AndroidPrintStream(Log.INFO, "System.out")); + System.err.close(); + System.setErr(new AndroidPrintStream(Log.WARN, "System.err")); } public static final native void zygoteInitNative(); @@ -351,4 +367,55 @@ public class RuntimeInit { // Register handlers for DDM messages. android.ddm.DdmRegister.registerHandlers(); } + + /** + * Handles argument parsing for args related to the runtime. + * + * Current recognized args: + * <ul> + * <li> <code> [--] <start class name> <args> + * </ul> + */ + static class Arguments { + /** first non-option argument */ + String startClass; + + /** all following arguments */ + String[] startArgs; + + /** + * Constructs instance and parses args + * @param args runtime command-line args + * @throws IllegalArgumentException + */ + Arguments(String args[]) throws IllegalArgumentException { + parseArgs(args); + } + + /** + * Parses the commandline arguments intended for the Runtime. + */ + private void parseArgs(String args[]) + throws IllegalArgumentException { + int curArg = 0; + for (; curArg < args.length; curArg++) { + String arg = args[curArg]; + + if (arg.equals("--")) { + curArg++; + break; + } else if (!arg.startsWith("--")) { + break; + } + } + + if (curArg == args.length) { + throw new IllegalArgumentException("Missing classname argument to RuntimeInit!"); + } + + startClass = args[curArg++]; + startArgs = new String[args.length - curArg]; + System.arraycopy(args, curArg, startArgs, 0, startArgs.length); + } + } } diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java new file mode 100644 index 0000000..18d6caa --- /dev/null +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -0,0 +1,117 @@ +/* + * 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 com.android.internal.os; + +import android.os.Process; +import android.util.Slog; + +import java.io.DataOutputStream; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; + +import libcore.io.IoUtils; +import libcore.io.Libcore; + +import dalvik.system.Zygote; + +/** + * Startup class for the wrapper process. + * @hide + */ +public class WrapperInit { + private final static String TAG = "AndroidRuntime"; + + /** + * Class not instantiable. + */ + private WrapperInit() { + } + + /** + * The main function called when starting a runtime application through a + * wrapper process instead of by forking Zygote. + * + * The first argument specifies the file descriptor for a pipe that should receive + * the pid of this process, or 0 if none. The remaining arguments are passed to + * the runtime. + * + * @param args The command-line arguments. + */ + public static void main(String[] args) { + try { + int fdNum = Integer.parseInt(args[0], 10); + if (fdNum != 0) { + try { + FileDescriptor fd = ZygoteInit.createFileDescriptor(fdNum); + DataOutputStream os = new DataOutputStream(new FileOutputStream(fd)); + os.writeInt(Process.myPid()); + os.close(); + IoUtils.closeQuietly(fd); + } catch (IOException ex) { + Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex); + } + } + + String[] runtimeArgs = new String[args.length - 1]; + System.arraycopy(args, 1, runtimeArgs, 0, runtimeArgs.length); + RuntimeInit.wrapperInit(runtimeArgs); + } catch (ZygoteInit.MethodAndArgsCaller caller) { + caller.run(); + } + } + + /** + * Executes a runtime application with a wrapper command. + * This method never returns. + * + * @param invokeWith The wrapper command. + * @param niceName The nice name for the application, or null if none. + * @param pipeFd The pipe to which the application's pid should be written, or null if none. + * @param args Arguments for {@link RuntimeInit.main}. + */ + public static void execApplication(String invokeWith, String niceName, + FileDescriptor pipeFd, String[] args) { + StringBuilder command = new StringBuilder(invokeWith); + command.append(" /system/bin/app_process /system/bin --application"); + if (niceName != null) { + command.append(" '--nice-name=").append(niceName).append("'"); + } + command.append(" com.android.internal.os.WrapperInit "); + command.append(pipeFd != null ? pipeFd.getInt$() : 0); + Zygote.appendQuotedShellArgs(command, args); + Zygote.execShell(command.toString()); + } + + /** + * Executes a standalone application with a wrapper command. + * This method never returns. + * + * @param invokeWith The wrapper command. + * @param classPath The class path. + * @param className The class name to invoke. + * @param args Arguments for the main() method of the specified class. + */ + public static void execStandalone(String invokeWith, String classPath, String className, + String[] args) { + StringBuilder command = new StringBuilder(invokeWith); + command.append(" /system/bin/dalvikvm -classpath '").append(classPath); + command.append("' ").append(className); + Zygote.appendQuotedShellArgs(command, args); + Zygote.execShell(command.toString()); + } +} diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index c473fd2..b872e22 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -26,14 +26,20 @@ import dalvik.system.PathClassLoader; import dalvik.system.Zygote; import java.io.BufferedReader; +import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileDescriptor; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.util.ArrayList; +import libcore.io.ErrnoException; +import libcore.io.IoUtils; +import libcore.io.Libcore; + /** * A connection that can make spawn requests. */ @@ -193,15 +199,20 @@ class ZygoteConnection { new FileOutputStream(descriptors[2])); } - int pid; + int pid = -1; + FileDescriptor childPipeFd = null; + FileDescriptor serverPipeFd = null; try { parsedArgs = new Arguments(args); applyUidSecurityPolicy(parsedArgs, peer); - applyDebuggerSecurityPolicy(parsedArgs); applyRlimitSecurityPolicy(parsedArgs, peer); applyCapabilitiesSecurityPolicy(parsedArgs, peer); + applyInvokeWithSecurityPolicy(parsedArgs, peer); + + applyDebuggerSystemProperty(parsedArgs); + applyInvokeWithSystemProperty(parsedArgs); int[][] rlimits = null; @@ -209,25 +220,45 @@ class ZygoteConnection { rlimits = parsedArgs.rlimits.toArray(intArray2d); } + if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) { + FileDescriptor[] pipeFds = Libcore.os.pipe(); + childPipeFd = pipeFds[1]; + serverPipeFd = pipeFds[0]; + ZygoteInit.setCloseOnExec(serverPipeFd, true); + } + pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits); + } catch (IOException ex) { + logAndPrintError(newStderr, "Exception creating pipe", ex); + } catch (ErrnoException ex) { + logAndPrintError(newStderr, "Exception creating pipe", ex); } catch (IllegalArgumentException ex) { - logAndPrintError (newStderr, "Invalid zygote arguments", ex); - pid = -1; + logAndPrintError(newStderr, "Invalid zygote arguments", ex); } catch (ZygoteSecurityException ex) { logAndPrintError(newStderr, "Zygote security policy prevents request: ", ex); - pid = -1; } - if (pid == 0) { - // in child - handleChildProc(parsedArgs, descriptors, newStderr); - // should never happen - return true; - } else { /* pid != 0 */ - // in parent...pid of < 0 means failure - return handleParentProc(pid, descriptors, parsedArgs); + try { + if (pid == 0) { + // in child + IoUtils.closeQuietly(serverPipeFd); + serverPipeFd = null; + handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); + + // should never get here, the child is expected to either + // throw ZygoteInit.MethodAndArgsCaller or exec(). + return true; + } else { + // in parent...pid of < 0 means failure + IoUtils.closeQuietly(childPipeFd); + childPipeFd = null; + return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); + } + } finally { + IoUtils.closeQuietly(childPipeFd); + IoUtils.closeQuietly(serverPipeFd); } } @@ -244,8 +275,8 @@ class ZygoteConnection { } /** - * Handles argument parsing for args related to the zygote spawner.<p> - + * Handles argument parsing for args related to the zygote spawner. + * * Current recognized args: * <ul> * <li> --setuid=<i>uid of child process, defaults to 0</i> @@ -274,6 +305,7 @@ class ZygoteConnection { * be handed off to com.android.internal.os.RuntimeInit, rather than * processed directly * Android runtime startup (eg, Binder initialization) is also eschewed. + * <li> --nice-name=<i>nice name to appear in ps</i> * <li> If <code>--runtime-init</code> is present: * [--] <args for RuntimeInit > * <li> If <code>--runtime-init</code> is absent: @@ -307,6 +339,9 @@ class ZygoteConnection { /** from --runtime-init */ boolean runtimeInit; + /** from --nice-name */ + String niceName; + /** from --capabilities */ boolean capabilitiesSpecified; long permittedCapabilities; @@ -315,6 +350,9 @@ class ZygoteConnection { /** from all --rlimit=r,c,m */ ArrayList<int[]> rlimits; + /** from --invoke-with */ + String invokeWith; + /** * Any args after and including the first non-option arg * (or after a '--') @@ -438,6 +476,23 @@ class ZygoteConnection { for (int i = params.length - 1; i >= 0 ; i--) { gids[i] = Integer.parseInt(params[i]); } + } else if (arg.equals("--invoke-with")) { + if (invokeWith != null) { + throw new IllegalArgumentException( + "Duplicate arg specified"); + } + try { + invokeWith = args[++curArg]; + } catch (IndexOutOfBoundsException ex) { + throw new IllegalArgumentException( + "--invoke-with requires argument"); + } + } else if (arg.startsWith("--nice-name=")) { + if (niceName != null) { + throw new IllegalArgumentException( + "Duplicate arg specified"); + } + niceName = arg.substring(arg.indexOf('=') + 1); } else { break; } @@ -567,14 +622,15 @@ class ZygoteConnection { /** - * Applies debugger security policy. + * Applies debugger system properties to the zygote arguments. + * * If "ro.debuggable" is "1", all apps are debuggable. Otherwise, * the debugger state is specified via the "--enable-debugger" flag * in the spawn request. * * @param args non-null; zygote spawner args */ - private static void applyDebuggerSecurityPolicy(Arguments args) { + public static void applyDebuggerSystemProperty(Arguments args) { if ("1".equals(SystemProperties.get("ro.debuggable"))) { args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; } @@ -664,12 +720,56 @@ class ZygoteConnection { } /** + * Applies zygote security policy. + * Based on the credentials of the process issuing a zygote command: + * <ol> + * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a + * wrapper command. + * <li> Any other uid may not specify any invoke-with argument. + * </ul> + * + * @param args non-null; zygote spawner arguments + * @param peer non-null; peer credentials + * @throws ZygoteSecurityException + */ + private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer) + throws ZygoteSecurityException { + int peerUid = peer.getUid(); + + if (args.invokeWith != null && peerUid != 0) { + throw new ZygoteSecurityException("Peer is not permitted to specify " + + "an explicit invoke-with wrapper command"); + } + } + + /** + * Applies invoke-with system properties to the zygote arguments. + * + * @param parsedArgs non-null; zygote args + */ + public static void applyInvokeWithSystemProperty(Arguments args) { + if (args.invokeWith == null && args.niceName != null) { + if (args.niceName != null) { + String property = "wrap." + args.niceName; + if (property.length() > 31) { + property = property.substring(0, 31); + } + args.invokeWith = SystemProperties.get(property); + if (args.invokeWith != null && args.invokeWith.length() == 0) { + args.invokeWith = null; + } + } + } + } + + /** * Handles post-fork setup of child proc, closing sockets as appropriate, * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller * if successful or returning if failed. * * @param parsedArgs non-null; zygote args * @param descriptors null-ok; new file descriptors for stdio if available. + * @param pipeFd null-ok; pipe for communication back to Zygote. * @param newStderr null-ok; stream to use for stderr until stdio * is reopened. * @@ -677,7 +777,7 @@ class ZygoteConnection { * trampoline to code that invokes static main. */ private void handleChildProc(Arguments parsedArgs, - FileDescriptor[] descriptors, PrintStream newStderr) + FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) throws ZygoteInit.MethodAndArgsCaller { /* @@ -704,7 +804,7 @@ class ZygoteConnection { descriptors[1], descriptors[2]); for (FileDescriptor fd: descriptors) { - ZygoteInit.closeDescriptor(fd); + IoUtils.closeQuietly(fd); } newStderr = System.err; } catch (IOException ex) { @@ -712,37 +812,48 @@ class ZygoteConnection { } } - if (parsedArgs.runtimeInit) { - RuntimeInit.zygoteInit(parsedArgs.remainingArgs); - } else { - ClassLoader cloader; + if (parsedArgs.niceName != null) { + Process.setArgV0(parsedArgs.niceName); + } - if (parsedArgs.classpath != null) { - cloader - = new PathClassLoader(parsedArgs.classpath, - ClassLoader.getSystemClassLoader()); + if (parsedArgs.runtimeInit) { + if (parsedArgs.invokeWith != null) { + WrapperInit.execApplication(parsedArgs.invokeWith, + parsedArgs.niceName, pipeFd, parsedArgs.remainingArgs); } else { - cloader = ClassLoader.getSystemClassLoader(); + RuntimeInit.zygoteInit(parsedArgs.remainingArgs); } - + } else { String className; try { className = parsedArgs.remainingArgs[0]; } catch (ArrayIndexOutOfBoundsException ex) { - logAndPrintError (newStderr, + logAndPrintError(newStderr, "Missing required class name argument", null); return; } - String[] mainArgs - = new String[parsedArgs.remainingArgs.length - 1]; + String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1]; System.arraycopy(parsedArgs.remainingArgs, 1, mainArgs, 0, mainArgs.length); - try { - ZygoteInit.invokeStaticMain(cloader, className, mainArgs); - } catch (RuntimeException ex) { - logAndPrintError (newStderr, "Error starting. ", ex); + if (parsedArgs.invokeWith != null) { + WrapperInit.execStandalone(parsedArgs.invokeWith, + parsedArgs.classpath, className, mainArgs); + } else { + ClassLoader cloader; + if (parsedArgs.classpath != null) { + cloader = new PathClassLoader(parsedArgs.classpath, + ClassLoader.getSystemClassLoader()); + } else { + cloader = ClassLoader.getSystemClassLoader(); + } + + try { + ZygoteInit.invokeStaticMain(cloader, className, mainArgs); + } catch (RuntimeException ex) { + logAndPrintError(newStderr, "Error starting.", ex); + } } } } @@ -754,36 +865,54 @@ class ZygoteConnection { * if < 0; * @param descriptors null-ok; file descriptors for child's new stdio if * specified. + * @param pipeFd null-ok; pipe for communication with child. * @param parsedArgs non-null; zygote args * @return true for "exit command loop" and false for "continue command * loop" */ private boolean handleParentProc(int pid, - FileDescriptor[] descriptors, Arguments parsedArgs) { + FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) { + + if (pid > 0) { + setChildPgid(pid); + } + + if (descriptors != null) { + for (FileDescriptor fd: descriptors) { + IoUtils.closeQuietly(fd); + } + } - if(pid > 0) { - // Try to move the new child into the peer's process group. + if (pipeFd != null && pid > 0) { + DataInputStream is = new DataInputStream(new FileInputStream(pipeFd)); + int innerPid = -1; try { - ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid())); + innerPid = is.readInt(); } catch (IOException ex) { - // This exception is expected in the case where - // the peer is not in our session - // TODO get rid of this log message in the case where - // getsid(0) != getsid(peer.getPid()) - Log.i(TAG, "Zygote: setpgid failed. This is " - + "normal if peer is not in our session"); + Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex); + } finally { + try { + is.close(); + } catch (IOException ex) { + } } - } - try { - if (descriptors != null) { - for (FileDescriptor fd: descriptors) { - ZygoteInit.closeDescriptor(fd); + // Ensure that the pid reported by the wrapped process is either the + // child process that we forked, or a descendant of it. + if (innerPid > 0) { + int parentPid = innerPid; + while (parentPid > 0 && parentPid != pid) { + parentPid = Process.getParentPid(parentPid); + } + if (parentPid > 0) { + Log.i(TAG, "Wrapped process has pid " + innerPid); + pid = innerPid; + } else { + Log.w(TAG, "Wrapped process reported a pid that is not a child of " + + "the process that we forked: childPid=" + pid + + " innerPid=" + innerPid); } } - } catch (IOException ex) { - Log.e(TAG, "Error closing passed descriptors in " - + "parent process", ex); } try { @@ -808,6 +937,20 @@ class ZygoteConnection { return false; } + private void setChildPgid(int pid) { + // Try to move the new child into the peer's process group. + try { + ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid())); + } catch (IOException ex) { + // This exception is expected in the case where + // the peer is not in our session + // TODO get rid of this log message in the case where + // getsid(0) != getsid(peer.getPid()) + Log.i(TAG, "Zygote: setpgid failed. This is " + + "normal if peer is not in our session"); + } + } + /** * Logs an error message and prints it to the specified stream, if * provided diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index fbe66e5..157c0bf 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable; import android.net.LocalServerSocket; import android.os.Debug; import android.os.FileUtils; +import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; import android.util.EventLog; @@ -68,7 +69,7 @@ public class ZygoteInit { private static final int PRELOAD_GC_THRESHOLD = 50000; public static final String USAGE_STRING = - " <\"true\"|\"false\" for startSystemServer>"; + " <\"start-system-server\"|\"\" for startSystemServer>"; private static LocalServerSocket sServerSocket; @@ -441,11 +442,20 @@ public class ZygoteInit { // set umask to 0077 so new files and directories will default to owner-only permissions. FileUtils.setUMask(FileUtils.S_IRWXG | FileUtils.S_IRWXO); - /* - * Pass the remaining arguments to SystemServer. - * "--nice-name=system_server com.android.server.SystemServer" - */ - RuntimeInit.zygoteInit(parsedArgs.remainingArgs); + if (parsedArgs.niceName != null) { + Process.setArgV0(parsedArgs.niceName); + } + + if (parsedArgs.invokeWith != null) { + WrapperInit.execApplication(parsedArgs.invokeWith, + parsedArgs.niceName, null, parsedArgs.remainingArgs); + } else { + /* + * Pass the remaining arguments to SystemServer. + */ + RuntimeInit.zygoteInit(parsedArgs.remainingArgs); + } + /* should never reach here */ } @@ -470,20 +480,13 @@ public class ZygoteInit { try { parsedArgs = new ZygoteConnection.Arguments(args); - - /* - * Enable debugging of the system process if *either* the command line flags - * indicate it should be debuggable or the ro.debuggable system property - * is set to "1" - */ - int debugFlags = parsedArgs.debugFlags; - if ("1".equals(SystemProperties.get("ro.debuggable"))) - debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; + ZygoteConnection.applyDebuggerSystemProperty(parsedArgs); + ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs); /* Request to fork the system server process */ pid = Zygote.forkSystemServer( parsedArgs.uid, parsedArgs.gid, - parsedArgs.gids, debugFlags, null, + parsedArgs.gids, parsedArgs.debugFlags, null, parsedArgs.permittedCapabilities, parsedArgs.effectiveCapabilities); } catch (IllegalArgumentException ex) { @@ -522,9 +525,9 @@ public class ZygoteInit { throw new RuntimeException(argv[0] + USAGE_STRING); } - if (argv[1].equals("true")) { + if (argv[1].equals("start-system-server")) { startSystemServer(); - } else if (!argv[1].equals("false")) { + } else if (!argv[1].equals("")) { throw new RuntimeException(argv[0] + USAGE_STRING); } @@ -696,15 +699,6 @@ public class ZygoteInit { FileDescriptor out, FileDescriptor err) throws IOException; /** - * Calls close() on a file descriptor - * - * @param fd descriptor to close - * @throws IOException - */ - static native void closeDescriptor(FileDescriptor fd) - throws IOException; - - /** * Toggles the close-on-exec flag for the specified file descriptor. * * @param fd non-null; file descriptor diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java index 290bf08..7b4f216 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuView.java @@ -89,7 +89,6 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo final int childCount = getChildCount(); final int midVertical = (top + bottom) / 2; final int dividerWidth = getDividerWidth(); - boolean hasOverflow = false; int overflowWidth = 0; int nonOverflowWidth = 0; int nonOverflowCount = 0; @@ -102,7 +101,6 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo LayoutParams p = (LayoutParams) v.getLayoutParams(); if (p.isOverflowButton) { - hasOverflow = true; overflowWidth = v.getMeasuredWidth(); if (hasDividerBeforeChildAt(i)) { overflowWidth += dividerWidth; @@ -125,15 +123,12 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } } - // Try to center non-overflow items with uniformly spaced padding, including on the edges. - // Overflow will always pin to the right edge. If there isn't enough room for that, - // center in the remaining space. + // Fill action items from the left. Overflow will always pin to the right edge. if (nonOverflowWidth <= widthRemaining - overflowWidth) { widthRemaining -= overflowWidth; } - final int spacing = (widthRemaining - nonOverflowWidth) / (nonOverflowCount + 1); - int startLeft = getPaddingLeft() + overflowWidth + spacing; + int startLeft = getPaddingLeft(); for (int i = 0; i < childCount; i++) { final View v = getChildAt(i); final LayoutParams lp = (LayoutParams) v.getLayoutParams(); @@ -146,7 +141,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo int height = v.getMeasuredHeight(); int t = midVertical - (height / 2); v.layout(startLeft, t, startLeft + width, t + height); - startLeft += width + lp.rightMargin + spacing; + startLeft += width + lp.rightMargin; } } diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java index ad773ee..834041f 100644 --- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java +++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java @@ -76,6 +76,12 @@ public class SubMenuBuilder extends MenuBuilder implements SubMenu { return mParentMenu; } + @Override + boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) { + return super.dispatchMenuItemSelected(menu, item) || + mParentMenu.dispatchMenuItemSelected(menu, item); + } + public SubMenu setIcon(Drawable icon) { mItem.setIcon(icon); return this; diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index f1887eb..ff04735 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -78,7 +78,7 @@ public class ActionBarView extends AbsActionBarView { private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.LEFT | Gravity.CENTER_VERTICAL; - private final int mContentHeight; + private int mContentHeight; private int mNavigationMode; private int mDisplayOptions = ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP; @@ -95,8 +95,7 @@ public class ActionBarView extends AbsActionBarView { private TextView mSubtitleView; private Spinner mSpinner; private LinearLayout mListNavLayout; - private HorizontalScrollView mTabScrollView; - private ViewGroup mTabLayout; + private ScrollingTabContainerView mTabScrollView; private View mCustomNavView; private ProgressBar mProgressView; private ProgressBar mIndeterminateProgressView; @@ -122,6 +121,8 @@ public class ActionBarView extends AbsActionBarView { private SpinnerAdapter mSpinnerAdapter; private OnNavigationListener mCallback; + private Runnable mTabSelector; + private final AdapterView.OnItemSelectedListener mNavItemSelectedListener = new AdapterView.OnItemSelectedListener() { public void onItemSelected(AdapterView parent, View view, int position, long id) { @@ -199,8 +200,6 @@ public class ActionBarView extends AbsActionBarView { mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0); mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0); - mIncludeTabs = a.getBoolean(R.styleable.ActionBar_embeddedTabs, true); - setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT)); final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0); @@ -229,6 +228,12 @@ public class ActionBarView extends AbsActionBarView { } @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + removeCallbacks(mTabSelector); + } + + @Override public boolean shouldDelayChildPressedState() { return false; } @@ -247,6 +252,11 @@ public class ActionBarView extends AbsActionBarView { addView(mIndeterminateProgressView); } + public void setContentHeight(int height) { + mContentHeight = height; + requestLayout(); + } + public void setSplitActionBar(boolean splitActionBar) { if (mSplitActionBar != splitActionBar) { if (mMenuView != null) { @@ -271,8 +281,9 @@ public class ActionBarView extends AbsActionBarView { return mIncludeTabs; } - public void setExternalTabLayout(ViewGroup tabLayout) { - mTabLayout = tabLayout; + public void setEmbeddedTabView(ScrollingTabContainerView tabs) { + mTabScrollView = tabs; + mIncludeTabs = tabs != null; } public void setCallback(OnNavigationListener callback) { @@ -489,7 +500,7 @@ public class ActionBarView extends AbsActionBarView { } break; case ActionBar.NAVIGATION_MODE_TABS: - if (mTabScrollView != null) { + if (mTabScrollView != null && mIncludeTabs) { removeView(mTabScrollView); } } @@ -513,8 +524,7 @@ public class ActionBarView extends AbsActionBarView { addView(mListNavLayout); break; case ActionBar.NAVIGATION_MODE_TABS: - ensureTabsExist(); - if (mTabScrollView != null) { + if (mTabScrollView != null && mIncludeTabs) { addView(mTabScrollView); } break; @@ -523,24 +533,17 @@ public class ActionBarView extends AbsActionBarView { requestLayout(); } } - - private void ensureTabsExist() { - if (!mIncludeTabs) return; - - if (mTabScrollView == null) { - mTabScrollView = new HorizontalScrollView(getContext()); - mTabScrollView.setHorizontalFadingEdgeEnabled(true); - mTabLayout = createTabContainer(); - mTabScrollView.addView(mTabLayout); - } - } - public ViewGroup createTabContainer() { - ViewGroup result = new LinearLayout(getContext(), null, + public ScrollingTabContainerView createTabContainer() { + final LinearLayout tabLayout = new LinearLayout(getContext(), null, com.android.internal.R.attr.actionBarTabBarStyle); - result.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, - mContentHeight)); - return result; + tabLayout.setMeasureWithLargestChildEnabled(true); + tabLayout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, mContentHeight)); + + final ScrollingTabContainerView scroller = new ScrollingTabContainerView(mContext); + scroller.setTabLayout(tabLayout); + return scroller; } public void setDropdownAdapter(SpinnerAdapter adapter) { @@ -574,51 +577,6 @@ public class ActionBarView extends AbsActionBarView { return mDisplayOptions; } - private TabView createTabView(ActionBar.Tab tab) { - final TabView tabView = new TabView(getContext(), tab); - tabView.setFocusable(true); - - if (mTabClickListener == null) { - mTabClickListener = new TabClickListener(); - } - tabView.setOnClickListener(mTabClickListener); - return tabView; - } - - public void addTab(ActionBar.Tab tab, boolean setSelected) { - ensureTabsExist(); - View tabView = createTabView(tab); - mTabLayout.addView(tabView); - if (setSelected) { - tabView.setSelected(true); - } - } - - public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { - ensureTabsExist(); - final TabView tabView = createTabView(tab); - mTabLayout.addView(tabView, position); - if (setSelected) { - tabView.setSelected(true); - } - } - - public void updateTab(int position) { - ((TabView) mTabLayout.getChildAt(position)).update(); - } - - public void removeTabAt(int position) { - if (mTabLayout != null) { - mTabLayout.removeViewAt(position); - } - } - - public void removeAllTabs() { - if (mTabLayout != null) { - mTabLayout.removeAllViews(); - } - } - @Override protected LayoutParams generateDefaultLayoutParams() { // Used by custom nav views if they don't supply layout params. Everything else @@ -667,15 +625,6 @@ public class ActionBarView extends AbsActionBarView { addView(mTitleLayout); } - public void setTabSelected(int position) { - ensureTabsExist(); - final int tabCount = mTabLayout.getChildCount(); - for (int i = 0; i < tabCount; i++) { - final View child = mTabLayout.getChildAt(i); - child.setSelected(i == position); - } - } - public void setContextView(ActionBarContextView view) { mContextView = view; } @@ -948,97 +897,6 @@ public class ActionBarView extends AbsActionBarView { } } - private static class TabView extends LinearLayout { - private ActionBar.Tab mTab; - private TextView mTextView; - private ImageView mIconView; - private View mCustomView; - - public TabView(Context context, ActionBar.Tab tab) { - super(context, null, com.android.internal.R.attr.actionBarTabStyle); - mTab = tab; - - update(); - - setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.MATCH_PARENT, 1)); - } - - public void update() { - final ActionBar.Tab tab = mTab; - final View custom = tab.getCustomView(); - if (custom != null) { - addView(custom); - mCustomView = custom; - if (mTextView != null) mTextView.setVisibility(GONE); - if (mIconView != null) { - mIconView.setVisibility(GONE); - mIconView.setImageDrawable(null); - } - } else { - if (mCustomView != null) { - removeView(mCustomView); - mCustomView = null; - } - - final Drawable icon = tab.getIcon(); - final CharSequence text = tab.getText(); - - if (icon != null) { - if (mIconView == null) { - ImageView iconView = new ImageView(getContext()); - LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER_VERTICAL; - iconView.setLayoutParams(lp); - addView(iconView, 0); - mIconView = iconView; - } - mIconView.setImageDrawable(icon); - mIconView.setVisibility(VISIBLE); - } else if (mIconView != null) { - mIconView.setVisibility(GONE); - mIconView.setImageDrawable(null); - } - - if (text != null) { - if (mTextView == null) { - TextView textView = new TextView(getContext(), null, - com.android.internal.R.attr.actionBarTabTextStyle); - textView.setSingleLine(); - textView.setEllipsize(TruncateAt.END); - LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER_VERTICAL; - textView.setLayoutParams(lp); - addView(textView); - mTextView = textView; - } - mTextView.setText(text); - mTextView.setVisibility(VISIBLE); - } else { - mTextView.setVisibility(GONE); - } - } - } - - public ActionBar.Tab getTab() { - return mTab; - } - } - - private class TabClickListener implements OnClickListener { - public void onClick(View view) { - TabView tabView = (TabView) view; - tabView.getTab().select(); - final int tabCount = mTabLayout.getChildCount(); - for (int i = 0; i < tabCount; i++) { - final View child = mTabLayout.getChildAt(i); - child.setSelected(child == view); - } - } - } - private static class HomeView extends FrameLayout { private View mUpView; private View mIconView; diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index d789584..bf1c637 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -385,6 +385,7 @@ public class PointerLocationView extends View { .append(" ToolMinor=").append(coords.toolMinor, 3) .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1) .append("deg") + .append(" Distance=").append(coords.getAxisValue(MotionEvent.AXIS_DISTANCE), 1) .append(" VScroll=").append(coords.getAxisValue(MotionEvent.AXIS_VSCROLL), 1) .append(" HScroll=").append(coords.getAxisValue(MotionEvent.AXIS_HSCROLL), 1) .append(" ToolType=").append(MotionEvent.toolTypeToString(toolType)) diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java new file mode 100644 index 0000000..c7d37f2 --- /dev/null +++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java @@ -0,0 +1,261 @@ +/* + * 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 com.android.internal.widget; + +import android.app.ActionBar; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.TextUtils.TruncateAt; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class ScrollingTabContainerView extends HorizontalScrollView { + Runnable mTabSelector; + private TabClickListener mTabClickListener; + + private LinearLayout mTabLayout; + + int mMaxTabWidth; + + public ScrollingTabContainerView(Context context) { + super(context); + setHorizontalScrollBarEnabled(false); + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + setFillViewport(widthMode == MeasureSpec.EXACTLY); + + final int childCount = getChildCount(); + if (childCount > 1 && + (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) { + if (childCount > 2) { + mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f); + } else { + mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2; + } + } else { + mMaxTabWidth = -1; + } + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + public void setTabSelected(int position) { + if (mTabLayout == null) { + return; + } + + final int tabCount = mTabLayout.getChildCount(); + for (int i = 0; i < tabCount; i++) { + final View child = mTabLayout.getChildAt(i); + final boolean isSelected = i == position; + child.setSelected(isSelected); + if (isSelected) { + animateToTab(position); + } + } + } + + public void animateToTab(int position) { + final View tabView = mTabLayout.getChildAt(position); + if (mTabSelector != null) { + removeCallbacks(mTabSelector); + } + mTabSelector = new Runnable() { + public void run() { + final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2; + smoothScrollTo(scrollPos, 0); + mTabSelector = null; + } + }; + post(mTabSelector); + } + + public void setTabLayout(LinearLayout tabLayout) { + if (mTabLayout != tabLayout) { + if (mTabLayout != null) { + ((ViewGroup) mTabLayout.getParent()).removeView(mTabLayout); + } + if (tabLayout != null) { + addView(tabLayout); + } + mTabLayout = tabLayout; + } + } + + public LinearLayout getTabLayout() { + return mTabLayout; + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mTabSelector != null) { + removeCallbacks(mTabSelector); + } + } + + private TabView createTabView(ActionBar.Tab tab) { + final TabView tabView = new TabView(getContext(), tab); + tabView.setFocusable(true); + + if (mTabClickListener == null) { + mTabClickListener = new TabClickListener(); + } + tabView.setOnClickListener(mTabClickListener); + return tabView; + } + + public void addTab(ActionBar.Tab tab, boolean setSelected) { + View tabView = createTabView(tab); + mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, + LayoutParams.MATCH_PARENT, 1)); + if (setSelected) { + tabView.setSelected(true); + } + } + + public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { + final TabView tabView = createTabView(tab); + mTabLayout.addView(tabView, position, new LinearLayout.LayoutParams( + 0, LayoutParams.MATCH_PARENT, 1)); + if (setSelected) { + tabView.setSelected(true); + } + } + + public void updateTab(int position) { + ((TabView) mTabLayout.getChildAt(position)).update(); + } + + public void removeTabAt(int position) { + if (mTabLayout != null) { + mTabLayout.removeViewAt(position); + } + } + + public void removeAllTabs() { + if (mTabLayout != null) { + mTabLayout.removeAllViews(); + } + } + + private class TabView extends LinearLayout { + private ActionBar.Tab mTab; + private TextView mTextView; + private ImageView mIconView; + private View mCustomView; + + public TabView(Context context, ActionBar.Tab tab) { + super(context, null, com.android.internal.R.attr.actionBarTabStyle); + mTab = tab; + + update(); + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + // Re-measure if we went beyond our maximum size. + if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) { + super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY), + heightMeasureSpec); + } + } + + public void update() { + final ActionBar.Tab tab = mTab; + final View custom = tab.getCustomView(); + if (custom != null) { + addView(custom); + mCustomView = custom; + if (mTextView != null) mTextView.setVisibility(GONE); + if (mIconView != null) { + mIconView.setVisibility(GONE); + mIconView.setImageDrawable(null); + } + } else { + if (mCustomView != null) { + removeView(mCustomView); + mCustomView = null; + } + + final Drawable icon = tab.getIcon(); + final CharSequence text = tab.getText(); + + if (icon != null) { + if (mIconView == null) { + ImageView iconView = new ImageView(getContext()); + LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER_VERTICAL; + iconView.setLayoutParams(lp); + addView(iconView, 0); + mIconView = iconView; + } + mIconView.setImageDrawable(icon); + mIconView.setVisibility(VISIBLE); + } else if (mIconView != null) { + mIconView.setVisibility(GONE); + mIconView.setImageDrawable(null); + } + + if (text != null) { + if (mTextView == null) { + TextView textView = new TextView(getContext(), null, + com.android.internal.R.attr.actionBarTabTextStyle); + textView.setSingleLine(); + textView.setEllipsize(TruncateAt.END); + LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER_VERTICAL; + textView.setLayoutParams(lp); + addView(textView); + mTextView = textView; + } + mTextView.setText(text); + mTextView.setVisibility(VISIBLE); + } else { + mTextView.setVisibility(GONE); + } + } + } + + public ActionBar.Tab getTab() { + return mTab; + } + } + + private class TabClickListener implements OnClickListener { + public void onClick(View view) { + TabView tabView = (TabView) view; + tabView.getTab().select(); + final int tabCount = mTabLayout.getChildCount(); + for (int i = 0; i < tabCount; i++) { + final View child = mTabLayout.getChildAt(i); + child.setSelected(child == view); + } + } + } +} diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index b787e9f..e610640 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -808,8 +808,11 @@ char* AndroidRuntime::toSlashClassName(const char* className) * Start the Android runtime. This involves starting the virtual machine * and calling the "static void main(String[] args)" method in the class * named by "className". + * + * Passes the main function two arguments, the class name and the specified + * options string. */ -void AndroidRuntime::start(const char* className, const bool startSystemServer) +void AndroidRuntime::start(const char* className, const char* options) { LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n", className != NULL ? className : "(unknown)"); @@ -820,7 +823,7 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) * 'startSystemServer == true' means runtime is obsolete and not run from * init.rc anymore, so we print out the boot start event here. */ - if (startSystemServer) { + if (strcmp(options, "start-system-server") == 0) { /* track our progress through the boot sequence */ const int LOG_BOOT_PROGRESS_START = 3000; LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, @@ -857,13 +860,13 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) /* * We want to call main() with a String array with arguments in it. - * At present we only have one argument, the class name. Create an - * array to hold it. + * At present we have two arguments, the class name and an option string. + * Create an array to hold them. */ jclass stringClass; jobjectArray strArray; jstring classNameStr; - jstring startSystemServerStr; + jstring optionsStr; stringClass = env->FindClass("java/lang/String"); assert(stringClass != NULL); @@ -872,9 +875,8 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) classNameStr = env->NewStringUTF(className); assert(classNameStr != NULL); env->SetObjectArrayElement(strArray, 0, classNameStr); - startSystemServerStr = env->NewStringUTF(startSystemServer ? - "true" : "false"); - env->SetObjectArrayElement(strArray, 1, startSystemServerStr); + optionsStr = env->NewStringUTF(options); + env->SetObjectArrayElement(strArray, 1, optionsStr); /* * Start VM. This thread becomes the main thread of the VM, and will @@ -909,12 +911,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) LOGW("Warning: VM did not shut down cleanly\n"); } -void AndroidRuntime::start() -{ - start("com.android.internal.os.RuntimeInit", - false /* Don't start the system server */); -} - void AndroidRuntime::onExit(int code) { LOGV("AndroidRuntime onExit calling exit(%d)", code); diff --git a/core/jni/android_os_ParcelFileDescriptor.cpp b/core/jni/android_os_ParcelFileDescriptor.cpp index 4ec131c..99a2d04 100644 --- a/core/jni/android_os_ParcelFileDescriptor.cpp +++ b/core/jni/android_os_ParcelFileDescriptor.cpp @@ -34,20 +34,37 @@ static struct parcel_file_descriptor_offsets_t jfieldID mFileDescriptor; } gParcelFileDescriptorOffsets; -static int android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env, +static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromFd(JNIEnv* env, + jobject clazz, jint origfd) +{ + int fd = dup(origfd); + if (fd < 0) { + jniThrowException(env, "java/io/IOException", strerror(errno)); + return NULL; + } + return jniCreateFileDescriptor(env, fd); +} + +static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromFdNoDup(JNIEnv* env, + jobject clazz, jint fd) +{ + return jniCreateFileDescriptor(env, fd); +} + +static void android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env, jobject clazz, jobjectArray outFds) { int fds[2]; if (pipe(fds) < 0) { - return -errno; + int therr = errno; + jniThrowException(env, "java/io/IOException", strerror(therr)); + return; } for (int i=0; i<2; i++) { jobject fdObj = jniCreateFileDescriptor(env, fds[i]); env->SetObjectArrayElement(outFds, i, fdObj); } - - return 0; } static jint getFd(JNIEnv* env, jobject clazz) @@ -102,7 +119,11 @@ static jlong android_os_ParcelFileDescriptor_getFdNative(JNIEnv* env, jobject cl } static const JNINativeMethod gParcelFileDescriptorMethods[] = { - {"createPipeNative", "([Ljava/io/FileDescriptor;)I", + {"getFileDescriptorFromFd", "(I)Ljava/io/FileDescriptor;", + (void*)android_os_ParcelFileDescriptor_getFileDescriptorFromFd}, + {"getFileDescriptorFromFdNoDup", "(I)Ljava/io/FileDescriptor;", + (void*)android_os_ParcelFileDescriptor_getFileDescriptorFromFdNoDup}, + {"createPipeNative", "([Ljava/io/FileDescriptor;)V", (void*)android_os_ParcelFileDescriptor_createPipeNative}, {"getStatSize", "()J", (void*)android_os_ParcelFileDescriptor_getStatSize}, diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index dced1a5..59b97c2 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -351,7 +351,7 @@ static int register_agent(native_data_t *nat, { DBusMessage *msg, *reply; DBusError err; - bool oob = TRUE; + dbus_bool_t oob = TRUE; if (!dbus_connection_register_object_path(nat->conn, agent_path, &agent_vtable, nat)) { diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp index e627e4a..86fd9cb 100644 --- a/core/jni/com_android_internal_os_ZygoteInit.cpp +++ b/core/jni/com_android_internal_os_ZygoteInit.cpp @@ -131,28 +131,6 @@ static void com_android_internal_os_ZygoteInit_reopenStdio(JNIEnv* env, } while (err < 0 && errno == EINTR); } -static void com_android_internal_os_ZygoteInit_closeDescriptor(JNIEnv* env, - jobject clazz, jobject descriptor) -{ - int fd; - int err; - - fd = jniGetFDFromFileDescriptor(env, descriptor); - - if (env->ExceptionOccurred() != NULL) { - return; - } - - do { - err = close(fd); - } while (err < 0 && errno == EINTR); - - if (err < 0) { - jniThrowIOException(env, errno); - return; - } -} - static void com_android_internal_os_ZygoteInit_setCloseOnExec (JNIEnv *env, jobject clazz, jobject descriptor, jboolean flag) { @@ -332,8 +310,6 @@ static JNINativeMethod gMethods[] = { "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;" "Ljava/io/FileDescriptor;)V", (void *) com_android_internal_os_ZygoteInit_reopenStdio}, - { "closeDescriptor", "(Ljava/io/FileDescriptor;)V", - (void *) com_android_internal_os_ZygoteInit_closeDescriptor}, { "setCloseOnExec", "(Ljava/io/FileDescriptor;Z)V", (void *) com_android_internal_os_ZygoteInit_setCloseOnExec}, { "setCapabilities", "(JJ)V", diff --git a/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png Binary files differdeleted file mode 100644 index 61db22c..0000000 --- a/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png +++ /dev/null diff --git a/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png Binary files differdeleted file mode 100644 index d6478fb..0000000 --- a/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/btn_code_lock_default.png b/core/res/res/drawable-mdpi/btn_code_lock_default.png Binary files differindex f524317..45cc20d 100755..100644 --- a/core/res/res/drawable-mdpi/btn_code_lock_default.png +++ b/core/res/res/drawable-mdpi/btn_code_lock_default.png diff --git a/core/res/res/drawable-mdpi/btn_code_lock_touched.png b/core/res/res/drawable-mdpi/btn_code_lock_touched.png Binary files differindex 5cd436c..45cc20d 100755..100644 --- a/core/res/res/drawable-mdpi/btn_code_lock_touched.png +++ b/core/res/res/drawable-mdpi/btn_code_lock_touched.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png Binary files differindex 20f3d50..0fbdbfa 100644 --- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png Binary files differindex d09ce53..ae97453 100644 --- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png Binary files differindex a9e008c..4127d1e 100644 --- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png Binary files differindex 1ed3065..525ab8a 100644 --- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png Binary files differindex 5710ebf..eb05820 100644 --- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png Binary files differindex dd7d89e..416b2c7 100644 --- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png Binary files differindex d29c6c3..97ac023 100644 --- a/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png +++ b/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_charging.png b/core/res/res/drawable-mdpi/ic_lock_idle_charging.png Binary files differindex 20d6320..4210db2 100755..100644 --- a/core/res/res/drawable-mdpi/ic_lock_idle_charging.png +++ b/core/res/res/drawable-mdpi/ic_lock_idle_charging.png diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_lock.png b/core/res/res/drawable-mdpi/ic_lock_idle_lock.png Binary files differindex 0206aee..1060f5a 100755..100644 --- a/core/res/res/drawable-mdpi/ic_lock_idle_lock.png +++ b/core/res/res/drawable-mdpi/ic_lock_idle_lock.png diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png b/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png Binary files differindex bb96782..72e4afa 100755..100644 --- a/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png +++ b/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png Binary files differindex 7ddeba5..0bc86c3 100644 --- a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png +++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png Binary files differindex 7201e58..2ab4547 100644 --- a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png +++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png Binary files differindex 8546c5f..fe72d00 100755..100644 --- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png +++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png Binary files differindex a98a29a..be666c6 100755..100644 --- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png +++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png Binary files differindex 6d579cb..9627197 100755..100644 --- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png +++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png diff --git a/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png Binary files differdeleted file mode 100644 index 6e703af..0000000 --- a/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_delete_holo.png b/core/res/res/drawable-mdpi/sym_keyboard_delete_holo.png Binary files differindex 1555791..1555791 100644 --- a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_delete_holo.png +++ b/core/res/res/drawable-mdpi/sym_keyboard_delete_holo.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_shift.png b/core/res/res/drawable-mdpi/sym_keyboard_shift.png Binary files differindex 0566e5a..91d6e32 100644 --- a/core/res/res/drawable-mdpi/sym_keyboard_shift.png +++ b/core/res/res/drawable-mdpi/sym_keyboard_shift.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png b/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png Binary files differindex ccaf05d..2bd0536 100755..100644 --- a/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png +++ b/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_activated_holo_dark.9.png Binary files differindex a233b0d..a233b0d 100644 --- a/core/res/res/drawable-xlarge-mdpi/textfield_bg_activated_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_bg_activated_holo_dark.9.png diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_default_holo_dark.9.png Binary files differindex 403f502..403f502 100644 --- a/core/res/res/drawable-xlarge-mdpi/textfield_bg_default_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_bg_default_holo_dark.9.png diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_disabled_focused_holo_dark.9.png Binary files differindex 0ded801..0ded801 100644 --- a/core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_focused_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_bg_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_disabled_holo_dark.9.png Binary files differindex 27237b8..27237b8 100644 --- a/core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_bg_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_focused_holo_dark.9.png Binary files differindex 0e451f1..0e451f1 100644 --- a/core/res/res/drawable-xlarge-mdpi/textfield_bg_focused_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_bg_focused_holo_dark.9.png diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_default.png b/core/res/res/drawable-mdpi/unlock_default.png Binary files differindex 0a441c0..0a441c0 100644 --- a/core/res/res/drawable-xlarge-mdpi/unlock_default.png +++ b/core/res/res/drawable-mdpi/unlock_default.png diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_halo.png b/core/res/res/drawable-mdpi/unlock_halo.png Binary files differindex 09b0526..09b0526 100644 --- a/core/res/res/drawable-xlarge-mdpi/unlock_halo.png +++ b/core/res/res/drawable-mdpi/unlock_halo.png diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_ring.png b/core/res/res/drawable-mdpi/unlock_ring.png Binary files differindex 0363a8b..0363a8b 100644 --- a/core/res/res/drawable-xlarge-mdpi/unlock_ring.png +++ b/core/res/res/drawable-mdpi/unlock_ring.png diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_wave.png b/core/res/res/drawable-mdpi/unlock_wave.png Binary files differindex 21bfa24..21bfa24 100644 --- a/core/res/res/drawable-xlarge-mdpi/unlock_wave.png +++ b/core/res/res/drawable-mdpi/unlock_wave.png diff --git a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png b/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png Binary files differdeleted file mode 100644 index 45cc20d..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png b/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png Binary files differdeleted file mode 100644 index 45cc20d..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal.9.png Binary files differdeleted file mode 100644 index 0fbdbfa..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png Binary files differdeleted file mode 100644 index ae97453..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png Binary files differdeleted file mode 100644 index 4127d1e..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed.9.png Binary files differdeleted file mode 100644 index 525ab8a..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png Binary files differdeleted file mode 100644 index eb05820..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png Binary files differdeleted file mode 100644 index 416b2c7..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png Binary files differdeleted file mode 100644 index 97ac023..0000000 --- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png Binary files differdeleted file mode 100644 index 4210db2..0000000 --- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png Binary files differdeleted file mode 100644 index 1060f5a..0000000 --- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png Binary files differdeleted file mode 100644 index 72e4afa..0000000 --- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png Binary files differdeleted file mode 100644 index 0bc86c3..0000000 --- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png Binary files differdeleted file mode 100644 index 2ab4547..0000000 --- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png Binary files differdeleted file mode 100644 index fe72d00..0000000 --- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png Binary files differdeleted file mode 100644 index be666c6..0000000 --- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png Binary files differdeleted file mode 100644 index 9627197..0000000 --- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift.png b/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift.png Binary files differdeleted file mode 100644 index 91d6e32..0000000 --- a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift_locked.png b/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift_locked.png Binary files differdeleted file mode 100644 index 2bd0536..0000000 --- a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift_locked.png +++ /dev/null diff --git a/core/res/res/drawable/extract_edit_text.xml b/core/res/res/drawable/extract_edit_text.xml deleted file mode 100644 index c7f66f6..0000000 --- a/core/res/res/drawable/extract_edit_text.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2007 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. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_focused="true" android:drawable="@drawable/keyboard_textfield_selected" /> - <item android:drawable="@drawable/textfield_disabled" /> -</selector> - diff --git a/core/res/res/layout-xlarge/keyguard.xml b/core/res/res/layout-sw600dp/keyguard.xml index ca629f8..ca629f8 100644 --- a/core/res/res/layout-xlarge/keyguard.xml +++ b/core/res/res/layout-sw600dp/keyguard.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_glogin_unlock.xml index 9779074..9779074 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_glogin_unlock.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_lock.xml b/core/res/res/layout-sw600dp/keyguard_screen_lock.xml index c7aa654..c7aa654 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_lock.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_lock.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml index 1783088..1783088 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml index 63241dd..63241dd 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml index b8cbe51..b8cbe51 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml index 009148f..009148f 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_status_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml index 0a485e2..0a485e2 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_status_land.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_status_port.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml index 346b21e..346b21e 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_status_port.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml index c9c1692..c9c1692 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml index fbb9983..fbb9983 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml index 8acb656..e3d7a3f 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml @@ -47,13 +47,12 @@ <RelativeLayout android:layout_weight="1" android:layout_width="0dip" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:gravity="center_vertical|center_horizontal"> <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern" android:layout_width="354dip" android:layout_height="354dip" - android:layout_marginLeft="143dip" - android:layout_marginTop="201dip" android:layout_gravity="center_vertical" /> diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml index f35897e..f35897e 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml diff --git a/core/res/res/layout/input_method_extract_view.xml b/core/res/res/layout/input_method_extract_view.xml index 689ba7b..7d59d02 100644 --- a/core/res/res/layout/input_method_extract_view.xml +++ b/core/res/res/layout/input_method_extract_view.xml @@ -31,7 +31,6 @@ android:gravity="top" android:minLines="1" android:inputType="text" - android:background="@android:drawable/extract_edit_text" > </android.inputmethodservice.ExtractEditText> diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-sw600dp/config.xml index 4c8bbe6..49ace34 100644 --- a/core/res/res/values-xlarge/config.xml +++ b/core/res/res/values-sw600dp/config.xml @@ -27,6 +27,7 @@ <!-- Show sliding tab before lockscreen --> <bool name="config_enableSlidingTabFirst">false</bool> + <!-- Enable lockscreen rotation --> <bool name="config_enableLockScreenRotation">true</bool> diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml index e058442..b906e1a 100644 --- a/core/res/res/values-xlarge/dimens.xml +++ b/core/res/res/values-xlarge/dimens.xml @@ -25,10 +25,6 @@ <!-- Size of the giant number (unread count) in the notifications --> <dimen name="status_bar_content_number_size">48sp</dimen> - <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. --> - <!-- Margin for permanent screen decorations at the bottom. --> - <dimen name="screen_margin_bottom">48dip</dimen> - <!-- Default height of a key in the password keyboard for alpha --> <dimen name="password_keyboard_key_height_alpha">75dip</dimen> <!-- Default height of a key in the password keyboard for numeric --> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 6c18089..c1e81c3 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -823,6 +823,10 @@ Default value is false. EditText content is always selectable. --> <attr name="textIsSelectable" format="boolean" /> + <!-- When true, IME suggestions will be displayed when the user double taps on editable text. + The default value is true. --> + <attr name="suggestionsEnabled" format="boolean" /> + <!-- Where to ellipsize text. --> <attr name="ellipsize"> <enum name="none" value="0" /> @@ -2877,6 +2881,8 @@ <!-- Indicates that the content of a non-editable text can be selected. --> <attr name="textIsSelectable" /> + <!-- Suggestions will be displayed when the user double taps on editable text. --> + <attr name="suggestionsEnabled" /> </declare-styleable> <!-- An <code>input-extras</code> is a container for extra data to supply to an input method. Contains @@ -4907,9 +4913,6 @@ <!-- Specifies padding that should be applied to the left and right sides of system-provided items in the bar. --> <attr name="itemPadding" format="dimension" /> - <!-- Specifies whether tabs should be embedded within the bar itself (true) - or displayed elsewhere (false). --> - <attr name="embeddedTabs" format="boolean" /> </declare-styleable> <declare-styleable name="ActionMode"> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 39d2329..6529fe1 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -77,6 +77,7 @@ <drawable name="dialog_holo_light_frame">@drawable/dialog_full_holo_light</drawable> <drawable name="input_method_fullscreen_background">#fff9f9f9</drawable> + <drawable name="input_method_fullscreen_background_holo">@drawable/screen_background_holo_dark</drawable> <!-- For date picker widget --> <drawable name="selected_day_background">#ff0092f4</drawable> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 8ad8f67..1957b2a 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1670,5 +1670,6 @@ <public type="attr" name="horizontalDirection" /> <public type="attr" name="fullBackupAgent" /> + <public type="attr" name="suggestionsEnabled" /> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 158d524..816546b 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1852,7 +1852,7 @@ <!-- Do not translate. WebView User Agent string --> <string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>) - AppleWebKit/534.16 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.16</string> + AppleWebKit/534.20 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.20</string> <!-- Do not translate. WebView User Agent targeted content --> <string name="web_user_agent_target_content" translatable="false">"Mobile "</string> @@ -1998,6 +1998,9 @@ <!-- Do not translate. Regex used by AutoFill. --> <string name="autofill_card_ignored_re">^card</string> + <!-- Do not translate. Regex used by AutoFill. --> + <string name="autofill_fax_re">fax<!-- fr-FR -->|télécopie|telecopie<!-- ja-JP -->|ファックス<!-- ru -->|факс<!-- zh-CN -->|传真<!-- zh-TW -->|傳真</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_readHistoryBookmarks">read Browser\'s history and bookmarks</string> @@ -2449,6 +2452,20 @@ <!-- See SMS_DIALOG. This is a button choice to disallow sending the SMSes.. --> <string name="sms_control_no">Cancel</string> + <!-- SIM swap and device reboot Dialog --> <skip /> + <!-- See SIM_REMOVED_DIALOG. This is the title of that dialog. --> + <string name="sim_removed_title">SIM card removed</string> + <!-- See SIM_REMOVED_DIALOG. This is the message of that dialog. --> + <string name="sim_removed_message">The mobile network will be unavailable until you replace the SIM card.</string> + <!-- See SIM_REMOVED_DIALOG. This is the button of that dialog. --> + <string name="sim_done_button">Done</string> + <!-- See SIM_ADDED_DIALOG. This is the title of that dialog. --> + <string name="sim_added_title">SIM card added</string> + <!-- See SIM_ADDED_DIALOG. This is the message of that dialog. --> + <string name="sim_added_message">You must restart your device to access the mobile network.</string> + <!-- See SIM_ADDED_DIALOG. This is the button of that dialog. --> + <string name="sim_restart_button">Restart</string> + <!-- Date/Time picker dialogs strings --> <!-- The title of the time picker dialog. [CHAR LIMIT=NONE] --> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 26b5d95..e95094f 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1078,7 +1078,6 @@ <item name="android:progressBarStyle">@android:style/Widget.ProgressBar.Horizontal</item> <item name="android:indeterminateProgressStyle">@android:style/Widget.ProgressBar.Small</item> <item name="android:homeLayout">@android:layout/action_bar_home</item> - <item name="android:embeddedTabs">@android:bool/action_bar_embed_tabs</item> </style> <style name="Widget.ActionMode"> @@ -1122,6 +1121,7 @@ </style> <style name="Widget.ActionBarView_TabView"> + <item name="android:gravity">center_horizontal</item> <item name="android:background">@drawable/minitab_lt</item> <item name="android:paddingLeft">4dip</item> <item name="android:paddingRight">4dip</item> @@ -1795,6 +1795,9 @@ </style> <style name="Widget.Holo.ActionBarView_TabBar" parent="Widget.ActionBarView_TabBar"> + <item name="android:divider">?android:attr/dividerVertical</item> + <item name="android:showDividers">middle</item> + <item name="android:dividerPadding">8dip</item> </style> <style name="Widget.Holo.ActionBarView_TabText" parent="Widget.ActionBarView_TabText"> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index aa9ddff..0a614b2 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -681,7 +681,7 @@ and a few custom attributes. --> <style name="Theme.Holo.InputMethod" parent="Theme.Holo.Panel"> <item name="android:windowAnimationStyle">@android:style/Animation.InputMethod</item> - <item name="android:imeFullscreenBackground">@android:drawable/input_method_fullscreen_background</item> + <item name="android:imeFullscreenBackground">@android:drawable/input_method_fullscreen_background_holo</item> <item name="android:imeExtractEnterAnimation">@android:anim/input_method_extract_enter</item> <item name="android:imeExtractExitAnimation">@android:anim/input_method_extract_exit</item> </style> diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java new file mode 100644 index 0000000..45719c2 --- /dev/null +++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java @@ -0,0 +1,97 @@ +/* + * 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.net; + +import android.os.SystemClock; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +@SmallTest +public class NetworkStatsTest extends TestCase { + + private static final String TEST_IFACE = "test0"; + + public void testFindIndex() throws Exception { + final NetworkStats stats = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3) + .addEntry(TEST_IFACE, 100, 1024, 0) + .addEntry(TEST_IFACE, 101, 0, 1024) + .addEntry(TEST_IFACE, 102, 1024, 1024).build(); + + assertEquals(2, stats.findIndex(TEST_IFACE, 102)); + assertEquals(2, stats.findIndex(TEST_IFACE, 102)); + assertEquals(0, stats.findIndex(TEST_IFACE, 100)); + assertEquals(-1, stats.findIndex(TEST_IFACE, 6)); + } + + public void testSubtractIdenticalData() throws Exception { + final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + .addEntry(TEST_IFACE, 100, 1024, 0) + .addEntry(TEST_IFACE, 101, 0, 1024).build(); + + final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + .addEntry(TEST_IFACE, 100, 1024, 0) + .addEntry(TEST_IFACE, 101, 0, 1024).build(); + + final NetworkStats result = after.subtract(before); + + assertEquals(0, result.rx[0]); + assertEquals(0, result.tx[0]); + assertEquals(0, result.rx[1]); + assertEquals(0, result.tx[1]); + } + + public void testSubtractIdenticalRows() throws Exception { + final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + .addEntry(TEST_IFACE, 100, 1024, 0) + .addEntry(TEST_IFACE, 101, 0, 1024).build(); + + final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + .addEntry(TEST_IFACE, 100, 1025, 2) + .addEntry(TEST_IFACE, 101, 3, 1028).build(); + + final NetworkStats result = after.subtract(before); + + // expect delta between measurements + assertEquals(1, result.rx[0]); + assertEquals(2, result.tx[0]); + assertEquals(3, result.rx[1]); + assertEquals(4, result.tx[1]); + } + + public void testSubtractNewRows() throws Exception { + final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + .addEntry(TEST_IFACE, 100, 1024, 0) + .addEntry(TEST_IFACE, 101, 0, 1024).build(); + + final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3) + .addEntry(TEST_IFACE, 100, 1024, 0) + .addEntry(TEST_IFACE, 101, 0, 1024) + .addEntry(TEST_IFACE, 102, 1024, 1024).build(); + + final NetworkStats result = after.subtract(before); + + // its okay to have new rows + assertEquals(0, result.rx[0]); + assertEquals(0, result.tx[0]); + assertEquals(0, result.rx[1]); + assertEquals(0, result.tx[1]); + assertEquals(1024, result.rx[2]); + assertEquals(1024, result.tx[2]); + } + +} diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h index b02a057..32cd4f5 100644 --- a/include/android_runtime/AndroidRuntime.h +++ b/include/android_runtime/AndroidRuntime.h @@ -39,6 +39,13 @@ public: AndroidRuntime(); virtual ~AndroidRuntime(); + enum StartMode { + Zygote, + SystemServer, + Application, + Tool, + }; + /** * Register a set of methods in the specified class. */ @@ -59,8 +66,7 @@ public: int addVmArguments(int argc, const char* const argv[]); - void start(const char *classname, const bool startSystemServer); - void start(); // start in android.util.RuntimeInit + void start(const char *classname, const char* options); static AndroidRuntime* getRuntime(); diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 9e4e132..2c7cf75 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -953,6 +953,7 @@ struct ResTable_config UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL, UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK, UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR, + UI_MODE_TYPE_TELEVISION = ACONFIGURATION_UI_MODE_TYPE_TELEVISION, // uiMode bits for the night switch. MASK_UI_MODE_NIGHT = 0x30, diff --git a/keystore/java/android/security/IKeyChainAliasResponse.aidl b/keystore/java/android/security/IKeyChainAliasResponse.aidl new file mode 100644 index 0000000..e042001 --- /dev/null +++ b/keystore/java/android/security/IKeyChainAliasResponse.aidl @@ -0,0 +1,26 @@ +/* + * 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.security; + +/** + * Used by the {@code KeyChainActivity} to return alias for {@link KeyStore#chooseAlias}. + * + * @hide + */ +interface IKeyChainAliasResponse { + + void alias(String alias); +} diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index be59f23..2763e46 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -15,8 +15,6 @@ */ package android.security; -import android.os.Bundle; - /** * Caller is required to ensure that {@link KeyStore#unlock * KeyStore.unlock} was successful. diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 08e05ef..ec820cf 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -17,9 +17,11 @@ package android.security; import android.accounts.Account; import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; +import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -56,64 +58,123 @@ public final class KeyChain { public static final String ACCOUNT_TYPE = "com.android.keychain"; /** - * Returns an {@code Intent} for use with {@link - * android.app.Activity#startActivityForResult - * startActivityForResult}. The result will be returned via {@link - * android.app.Activity#onActivityResult onActivityResult} with - * {@link android.app.Activity#RESULT_OK RESULT_OK} and the alias - * in the returned {@code Intent}'s extra data with key {@link - * android.content.Intent#EXTRA_TEXT Intent.EXTRA_TEXT}. + * @hide Also used by KeyChainActivity implementation */ - public static Intent chooseAlias() { - return new Intent("com.android.keychain.CHOOSER"); - } + public static final String EXTRA_RESPONSE = "response"; /** - * Returns a new {@code KeyChainResult} instance. + * Launches an {@code Activity} for the user to select the alias + * for a private key and certificate pair for authentication. The + * selected alias or null will be returned via the + * IKeyChainAliasResponse callback. */ - public static KeyChainResult get(Context context, String alias) - throws InterruptedException, RemoteException { - if (alias == null) { - throw new NullPointerException("alias == null"); + public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasResponse response) { + if (activity == null) { + throw new NullPointerException("activity == null"); } - KeyChainConnection keyChainConnection = bind(context); - try { - // Account is created if necessary during binding of the IKeyChainService - AccountManager accountManager = AccountManager.get(context); - Account account = accountManager.getAccountsByType(ACCOUNT_TYPE)[0]; - AccountManagerFuture<Bundle> future = accountManager.getAuthToken(account, - alias, - false, - null, - null); + if (response == null) { + throw new NullPointerException("response == null"); + } + Intent intent = new Intent("com.android.keychain.CHOOSER"); + intent.putExtra(EXTRA_RESPONSE, new AliasResponse(activity, response)); + activity.startActivity(intent); + } + + private static class AliasResponse extends IKeyChainAliasResponse.Stub { + private final Activity activity; + private final KeyChainAliasResponse keyChainAliasResponse; + private AliasResponse(Activity activity, KeyChainAliasResponse keyChainAliasResponse) { + this.activity = activity; + this.keyChainAliasResponse = keyChainAliasResponse; + } + @Override public void alias(String alias) { + if (alias == null) { + keyChainAliasResponse.alias(null); + return; + } + AccountManager accountManager = AccountManager.get(activity); + accountManager.getAuthToken(getAccount(activity), + alias, + null, + activity, + new AliasAccountManagerCallback(keyChainAliasResponse, + alias), + null); + } + } + + private static class AliasAccountManagerCallback implements AccountManagerCallback<Bundle> { + private final KeyChainAliasResponse keyChainAliasResponse; + private final String alias; + private AliasAccountManagerCallback(KeyChainAliasResponse keyChainAliasResponse, + String alias) { + this.keyChainAliasResponse = keyChainAliasResponse; + this.alias = alias; + } + @Override public void run(AccountManagerFuture<Bundle> future) { Bundle bundle; try { bundle = future.getResult(); } catch (OperationCanceledException e) { - throw new AssertionError(e); + keyChainAliasResponse.alias(null); + return; } catch (IOException e) { - throw new AssertionError(e); + keyChainAliasResponse.alias(null); + return; } catch (AuthenticatorException e) { - throw new AssertionError(e); + keyChainAliasResponse.alias(null); + return; } - Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); - if (intent != null) { - Bundle result = new Bundle(); - // we don't want this Eclair compatability flag, - // it will prevent onActivityResult from being called - intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); - return new KeyChainResult(intent); + String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); + if (authToken != null) { + keyChainAliasResponse.alias(alias); + } else { + keyChainAliasResponse.alias(null); } + } + } - String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); + /** + * Returns the {@code PrivateKey} for the requested alias, or null + * if no there is no result. + */ + public static PrivateKey getPrivateKey(Context context, String alias) + throws InterruptedException, RemoteException { + if (alias == null) { + throw new NullPointerException("alias == null"); + } + KeyChainConnection keyChainConnection = bind(context); + try { + String authToken = authToken(context, alias); if (authToken == null) { - throw new AssertionError("Invalid authtoken"); + return null; } IKeyChainService keyChainService = keyChainConnection.getService(); byte[] privateKeyBytes = keyChainService.getPrivateKey(alias, authToken); + return toPrivateKey(privateKeyBytes); + } finally { + keyChainConnection.close(); + } + } + + /** + * Returns the {@code X509Certificate} chain for the requested + * alias, or null if no there is no result. + */ + public static X509Certificate[] getCertificateChain(Context context, String alias) + throws InterruptedException, RemoteException { + if (alias == null) { + throw new NullPointerException("alias == null"); + } + KeyChainConnection keyChainConnection = bind(context); + try { + String authToken = authToken(context, alias); + if (authToken == null) { + return null; + } + IKeyChainService keyChainService = keyChainConnection.getService(); byte[] certificateBytes = keyChainService.getCertificate(alias, authToken); - return new KeyChainResult(toPrivateKey(privateKeyBytes), - toCertificate(certificateBytes)); + return new X509Certificate[] { toCertificate(certificateBytes) }; } finally { keyChainConnection.close(); } @@ -146,6 +207,50 @@ public final class KeyChain { } } + private static String authToken(Context context, String alias) { + AccountManager accountManager = AccountManager.get(context); + AccountManagerFuture<Bundle> future = accountManager.getAuthToken(getAccount(context), + alias, + false, + null, + null); + Bundle bundle; + try { + bundle = future.getResult(); + } catch (OperationCanceledException e) { + throw new AssertionError(e); + } catch (IOException e) { + // KeyChainAccountAuthenticator doesn't do I/O + throw new AssertionError(e); + } catch (AuthenticatorException e) { + throw new AssertionError(e); + } + Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); + if (intent != null) { + return null; + } + String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); + if (authToken == null) { + throw new AssertionError("Invalid authtoken"); + } + return authToken; + } + + private static Account getAccount(Context context) { + AccountManager accountManager = AccountManager.get(context); + Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE); + if (accounts.length == 0) { + try { + // Account is created if necessary during binding of the IKeyChainService + bind(context).close(); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + accounts = accountManager.getAccountsByType(ACCOUNT_TYPE); + } + return accounts[0]; + } + /** * @hide for reuse by CertInstaller and Settings. * @see KeyChain#bind diff --git a/keystore/java/android/security/KeyChainAliasResponse.java b/keystore/java/android/security/KeyChainAliasResponse.java new file mode 100644 index 0000000..bcca123 --- /dev/null +++ b/keystore/java/android/security/KeyChainAliasResponse.java @@ -0,0 +1,35 @@ +/* + * 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.security; + +import android.content.Intent; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +/** + * The KeyChainAliasResponse is the callback for {@link + * KeyChain#chooseAlias}. + * + * @hide + */ +public interface KeyChainAliasResponse { + + /** + * Called with the alias of the certificate chosen by the user, or + * null if no value was chosen. + */ + public void alias(String alias); +} diff --git a/keystore/java/android/security/KeyChainResult.java b/keystore/java/android/security/KeyChainResult.java deleted file mode 100644 index 85a2921..0000000 --- a/keystore/java/android/security/KeyChainResult.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.security; - -import android.content.Intent; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; - -/** - * The KeyChainResult is the complex result value from {@link - * KeyChain#get}. The caller should first inspect {@link #getIntent} - * to determine if the user needs to grant the application access to - * the protected contents. If {@code getIntent} returns null, access - * has been granted and the methods {@link #getPrivateKey} and {@link - * #getCertificate} can be used to access the credentials. - * - * @hide - */ -public final class KeyChainResult { - - private final Intent intent; - private final PrivateKey privateKey; - private final X509Certificate certificate; - - KeyChainResult(Intent intent) { - this(intent, null, null); - } - - KeyChainResult(PrivateKey privateKey, X509Certificate certificate) { - this(null, privateKey, certificate); - } - - private KeyChainResult(Intent intent, PrivateKey privateKey, X509Certificate certificate) { - this.intent = intent; - this.privateKey = privateKey; - this.certificate = certificate; - } - - public Intent getIntent() { - return intent; - } - - public PrivateKey getPrivateKey() { - checkIntent(); - return privateKey; - } - - public X509Certificate getCertificate() { - checkIntent(); - return certificate; - } - - private void checkIntent() { - if (intent != null) { - throw new IllegalStateException("non-null Intent, check getIntent()"); - } - } - -} diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 9f55a71..dd0052a 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -419,7 +419,8 @@ void RefBase::weakref_type::decWeak(const void* id) if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { if (impl->mStrong == INITIAL_STRONG_VALUE) - delete impl->mBase; + if (impl->mBase) + impl->mBase->destroy(); else { // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); delete impl; @@ -427,7 +428,8 @@ void RefBase::weakref_type::decWeak(const void* id) } else { impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { - delete impl->mBase; + if (impl->mBase) + impl->mBase->destroy(); } } } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index e1daede..253010c 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -828,29 +828,64 @@ public class AudioManager { * or {@link #SCO_AUDIO_STATE_CONNECTED} * * @see #startBluetoothSco() + * @deprecated Use {@link #ACTION_SCO_AUDIO_STATE_UPDATED} instead */ + @Deprecated @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED"; + + /** + * Sticky broadcast intent action indicating that the bluetoooth SCO audio + * connection state has been updated. + * <p>This intent has two extras: + * <ul> + * <li> {@link #EXTRA_SCO_AUDIO_STATE} - The new SCO audio state. </li> + * <li> {@link #EXTRA_SCO_AUDIO_PREVIOUS_STATE}- The previous SCO audio state. </li> + * </ul> + * <p> EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE can be any of: + * <ul> + * <li> {@link #SCO_AUDIO_STATE_DISCONNECTED}, </li> + * <li> {@link #SCO_AUDIO_STATE_CONNECTING} or </li> + * <li> {@link #SCO_AUDIO_STATE_CONNECTED}, </li> + * </ul> + * @see #startBluetoothSco() + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SCO_AUDIO_STATE_UPDATED = + "android.media.ACTION_SCO_AUDIO_STATE_UPDATED"; + /** - * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} containing the new - * bluetooth SCO connection state. + * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} or + * {@link #ACTION_SCO_AUDIO_STATE_UPDATED} containing the new bluetooth SCO connection state. */ public static final String EXTRA_SCO_AUDIO_STATE = "android.media.extra.SCO_AUDIO_STATE"; /** - * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that the - * SCO audio channel is not established + * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED} containing the previous + * bluetooth SCO connection state. + */ + public static final String EXTRA_SCO_AUDIO_PREVIOUS_STATE = + "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"; + + /** + * Value for extra EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE + * indicating that the SCO audio channel is not established */ public static final int SCO_AUDIO_STATE_DISCONNECTED = 0; /** - * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that the - * SCO audio channel is established + * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} or {@link #EXTRA_SCO_AUDIO_PREVIOUS_STATE} + * indicating that the SCO audio channel is established */ public static final int SCO_AUDIO_STATE_CONNECTED = 1; /** - * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that + * Value for extra EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE + * indicating that the SCO audio channel is being established + */ + public static final int SCO_AUDIO_STATE_CONNECTING = 2; + /** + * Value for extra EXTRA_SCO_AUDIO_STATE indicating that * there was an error trying to obtain the state */ public static final int SCO_AUDIO_STATE_ERROR = -1; @@ -878,29 +913,37 @@ public class AudioManager { * to/from a bluetooth SCO headset while the phone is not in call. * <p>As the SCO connection establishment can take several seconds, * applications should not rely on the connection to be available when the method - * returns but instead register to receive the intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} + * returns but instead register to receive the intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED} * and wait for the state to be {@link #SCO_AUDIO_STATE_CONNECTED}. - * <p>As the connection is not guaranteed to succeed, applications must wait for this intent with - * a timeout. - * <p>When finished with the SCO connection or if the establishment times out, - * the application must call {@link #stopBluetoothSco()} to clear the request and turn - * down the bluetooth connection. + * <p>As the ACTION_SCO_AUDIO_STATE_UPDATED intent is sticky, the application can check the SCO + * audio state before calling startBluetoothSco() by reading the intent returned by the receiver + * registration. If the state is already CONNECTED, no state change will be received via the + * intent after calling startBluetoothSco(). It is however useful to call startBluetoothSco() + * so that the connection stays active in case the current initiator stops the connection. + * <p>Unless the connection is already active as described above, the state will always + * transition from DISCONNECTED to CONNECTING and then either to CONNECTED if the connection + * succeeds or back to DISCONNECTED if the connection fails (e.g no headset is connected). + * <p>When finished with the SCO connection or if the establishment fails, the application must + * call {@link #stopBluetoothSco()} to clear the request and turn down the bluetooth connection. * <p>Even if a SCO connection is established, the following restrictions apply on audio * output streams so that they can be routed to SCO headset: - * - the stream type must be {@link #STREAM_VOICE_CALL} - * - the format must be mono - * - the sampling must be 16kHz or 8kHz + * <ul> + * <li> the stream type must be {@link #STREAM_VOICE_CALL} </li> + * <li> the format must be mono </li> + * <li> the sampling must be 16kHz or 8kHz </li> + * </ul> * <p>The following restrictions apply on input streams: - * - the format must be mono - * - the sampling must be 8kHz - * + * <ul> + * <li> the format must be mono </li> + * <li> the sampling must be 8kHz </li> + * </ul> * <p>Note that the phone application always has the priority on the usage of the SCO * connection for telephony. If this method is called while the phone is in call * it will be ignored. Similarly, if a call is received or sent while an application * is using the SCO connection, the connection will be lost for the application and NOT * returned automatically when the call ends. * @see #stopBluetoothSco() - * @see #ACTION_SCO_AUDIO_STATE_CHANGED + * @see #ACTION_SCO_AUDIO_STATE_UPDATED */ public void startBluetoothSco(){ IAudioService service = getService(); @@ -917,7 +960,7 @@ public class AudioManager { * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}. * <p>This method must be called by applications having requested the use of * bluetooth SCO audio with {@link #startBluetoothSco()} - * when finished with the SCO connection or if the establishment times out. + * when finished with the SCO connection or if connection fails. * @see #startBluetoothSco() */ public void stopBluetoothSco(){ diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 504cfde..4e77fcb 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -112,9 +112,12 @@ public class AudioService extends IAudioService.Stub { private static final int MSG_LOAD_SOUND_EFFECTS = 9; private static final int MSG_SET_FORCE_USE = 10; private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11; - + private static final int MSG_BT_HEADSET_CNCT_FAILED = 12; private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000; + // Timeout for connection to bluetooth headset service + private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000; + /** @see AudioSystemThread */ private AudioSystemThread mAudioSystemThread; @@ -273,11 +276,22 @@ public class AudioService extends IAudioService.Stub { private int mScoAudioState; // SCO audio state is not active private static final int SCO_STATE_INACTIVE = 0; + // SCO audio activation request waiting for headset service to connect + private static final int SCO_STATE_ACTIVATE_REQ = 1; // SCO audio state is active or starting due to a local request to start a virtual call - private static final int SCO_STATE_ACTIVE_INTERNAL = 1; + private static final int SCO_STATE_ACTIVE_INTERNAL = 3; + // SCO audio deactivation request waiting for headset service to connect + private static final int SCO_STATE_DEACTIVATE_REQ = 5; + // SCO audio state is active due to an action in BT handsfree (either voice recognition or // in call audio) private static final int SCO_STATE_ACTIVE_EXTERNAL = 2; + // Deactivation request for all SCO connections (initiated by audio mode change) + // waiting for headset service to connect + private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4; + + // Current connection state indicated by bluetooth headset + private int mScoConnectionState; // true if boot sequence has been completed private boolean mBootCompleted; @@ -335,13 +349,6 @@ public class AudioService extends IAudioService.Stub { AudioSystem.setErrorCallback(mAudioSystemCallback); - mBluetoothHeadsetDevice = null; - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null) { - adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, - BluetoothProfile.HEADSET); - } - // Register for device connection intent broadcasts. IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); @@ -768,17 +775,7 @@ public class AudioService extends IAudioService.Stub { if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) { AudioService.this.mMode = mode; if (mode != AudioSystem.MODE_NORMAL) { - synchronized(mScoClients) { - checkScoAudioState(); - if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { - mBluetoothHeadset.stopVoiceRecognition( - mBluetoothHeadsetDevice); - mBluetoothHeadset.stopScoUsingVirtualVoiceCall( - mBluetoothHeadsetDevice); - } else { - clearAllScoClients(mCb, true); - } - } + disconnectBluetoothSco(mCb); } } } @@ -856,16 +853,7 @@ public class AudioService extends IAudioService.Stub { // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode if (mode != AudioSystem.MODE_NORMAL) { - synchronized(mScoClients) { - checkScoAudioState(); - if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { - mBluetoothHeadset.stopVoiceRecognition(mBluetoothHeadsetDevice); - mBluetoothHeadset.stopScoUsingVirtualVoiceCall( - mBluetoothHeadsetDevice); - } else { - clearAllScoClients(cb, true); - } - } + disconnectBluetoothSco(cb); } } } @@ -1213,7 +1201,8 @@ public class AudioService extends IAudioService.Stub { /** @see AudioManager#startBluetoothSco() */ public void startBluetoothSco(IBinder cb){ - if (!checkAudioSettingsPermission("startBluetoothSco()")) { + if (!checkAudioSettingsPermission("startBluetoothSco()") || + !mBootCompleted) { return; } ScoClient client = getScoClient(cb, true); @@ -1222,7 +1211,8 @@ public class AudioService extends IAudioService.Stub { /** @see AudioManager#stopBluetoothSco() */ public void stopBluetoothSco(IBinder cb){ - if (!checkAudioSettingsPermission("stopBluetoothSco()")) { + if (!checkAudioSettingsPermission("stopBluetoothSco()") || + !mBootCompleted) { return; } ScoClient client = getScoClient(cb, false); @@ -1322,25 +1312,57 @@ public class AudioService extends IAudioService.Stub { } private void requestScoState(int state) { - if (mBluetoothHeadset == null) { - return; - } - checkScoAudioState(); - - if (totalCount() == 0 && - mBluetoothHeadsetDevice != null) { - // Accept SCO audio activation only in NORMAL audio mode or if the mode is - // currently controlled by the same client. - if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL || - mSetModeDeathHandlers.get(0).getBinder() == mCb) && - state == BluetoothHeadset.STATE_AUDIO_CONNECTED && - mScoAudioState == SCO_STATE_INACTIVE) { - mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; - mBluetoothHeadset.startScoUsingVirtualVoiceCall(mBluetoothHeadsetDevice); + if (totalCount() == 0) { + if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { + // Make sure that the state transitions to CONNECTING even if we cannot initiate + // the connection. + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); + // Accept SCO audio activation only in NORMAL audio mode or if the mode is + // currently controlled by the same client. + if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL || + mSetModeDeathHandlers.get(0).getBinder() == mCb) && + mBluetoothHeadsetDevice != null && + (mScoAudioState == SCO_STATE_INACTIVE || + mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) { + if (mScoAudioState == SCO_STATE_INACTIVE) { + if (mBluetoothHeadset != null) { + if (mBluetoothHeadset.startScoUsingVirtualVoiceCall( + mBluetoothHeadsetDevice)) { + mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; + } else { + broadcastScoConnectionState( + AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + } + } else if (getBluetoothHeadset()) { + mScoAudioState = SCO_STATE_ACTIVATE_REQ; + } + } else { + mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); + } + } else { + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + } } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED && - mScoAudioState == SCO_STATE_ACTIVE_INTERNAL){ - mBluetoothHeadset.stopScoUsingVirtualVoiceCall(mBluetoothHeadsetDevice); + mBluetoothHeadsetDevice != null && + (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL || + mScoAudioState == SCO_STATE_ACTIVATE_REQ)) { + if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) { + if (mBluetoothHeadset != null) { + if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall( + mBluetoothHeadsetDevice)) { + mScoAudioState = SCO_STATE_INACTIVE; + broadcastScoConnectionState( + AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + } + } else if (getBluetoothHeadset()) { + mScoAudioState = SCO_STATE_DEACTIVATE_REQ; + } + } else { + mScoAudioState = SCO_STATE_INACTIVE; + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + } } } } @@ -1348,7 +1370,7 @@ public class AudioService extends IAudioService.Stub { private void checkScoAudioState() { if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null && - mScoAudioState != SCO_STATE_ACTIVE_INTERNAL && + mScoAudioState == SCO_STATE_INACTIVE && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; @@ -1391,10 +1413,72 @@ public class AudioService extends IAudioService.Stub { } } + private boolean getBluetoothHeadset() { + boolean result = false; + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null) { + result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, + BluetoothProfile.HEADSET); + } + // If we could not get a bluetooth headset proxy, send a failure message + // without delay to reset the SCO audio state and clear SCO clients. + // If we could get a proxy, send a delayed failure message that will reset our state + // in case we don't receive onServiceConnected(). + sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0, + SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0); + return result; + } + + private void disconnectBluetoothSco(IBinder exceptBinder) { + synchronized(mScoClients) { + checkScoAudioState(); + if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL || + mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) { + if (mBluetoothHeadsetDevice != null) { + if (mBluetoothHeadset != null) { + if (!mBluetoothHeadset.stopVoiceRecognition( + mBluetoothHeadsetDevice) || + !mBluetoothHeadset.stopScoUsingVirtualVoiceCall( + mBluetoothHeadsetDevice)) { + sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0, + SENDMSG_REPLACE, 0, 0, null, 0); + } + } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL && + getBluetoothHeadset()) { + mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ; + } + } + } else { + clearAllScoClients(exceptBinder, true); + } + } + } + + private void resetBluetoothSco() { + synchronized(mScoClients) { + clearAllScoClients(null, false); + mScoAudioState = SCO_STATE_INACTIVE; + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + } + } + + private void broadcastScoConnectionState(int state) { + if (state != mScoConnectionState) { + Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); + newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); + newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, + mScoConnectionState); + mContext.sendStickyBroadcast(newIntent); + mScoConnectionState = state; + } + } + private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = new BluetoothProfile.ServiceListener() { public void onServiceConnected(int profile, BluetoothProfile proxy) { synchronized (mScoClients) { + // Discard timeout message + mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED); mBluetoothHeadset = (BluetoothHeadset) proxy; List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices(); if (deviceList.size() > 0) { @@ -1402,19 +1486,41 @@ public class AudioService extends IAudioService.Stub { } else { mBluetoothHeadsetDevice = null; } + // Refresh SCO audio state + checkScoAudioState(); + // Continue pending action if any + if (mScoAudioState == SCO_STATE_ACTIVATE_REQ || + mScoAudioState == SCO_STATE_DEACTIVATE_REQ || + mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) { + boolean status = false; + if (mBluetoothHeadsetDevice != null) { + switch (mScoAudioState) { + case SCO_STATE_ACTIVATE_REQ: + mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; + status = mBluetoothHeadset.startScoUsingVirtualVoiceCall( + mBluetoothHeadsetDevice); + break; + case SCO_STATE_DEACTIVATE_REQ: + status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall( + mBluetoothHeadsetDevice); + break; + case SCO_STATE_DEACTIVATE_EXT_REQ: + status = mBluetoothHeadset.stopVoiceRecognition( + mBluetoothHeadsetDevice) && + mBluetoothHeadset.stopScoUsingVirtualVoiceCall( + mBluetoothHeadsetDevice); + } + } + if (!status) { + sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0, + SENDMSG_REPLACE, 0, 0, null, 0); + } + } } } public void onServiceDisconnected(int profile) { synchronized (mScoClients) { - if (mBluetoothHeadset != null) { - List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices(); - if (devices.size() == 0) { - mBluetoothHeadsetDevice = null; - clearAllScoClients(null, false); - mScoAudioState = SCO_STATE_INACTIVE; - } - mBluetoothHeadset = null; - } + mBluetoothHeadset = null; } } }; @@ -2041,6 +2147,10 @@ public class AudioService extends IAudioService.Stub { case MSG_PERSIST_MEDIABUTTONRECEIVER: persistMediaButtonReceiver( (ComponentName) msg.obj ); break; + + case MSG_BT_HEADSET_CNCT_FAILED: + resetBluetoothSco(); + break; } } } @@ -2241,8 +2351,7 @@ public class AudioService extends IAudioService.Stub { address); mConnectedDevices.remove(device); mBluetoothHeadsetDevice = null; - clearAllScoClients(null, false); - mScoAudioState = SCO_STATE_INACTIVE; + resetBluetoothSco(); } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE, @@ -2332,26 +2441,34 @@ public class AudioService extends IAudioService.Stub { } } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { boolean broadcast = false; - int audioState = AudioManager.SCO_AUDIO_STATE_ERROR; + int state = AudioManager.SCO_AUDIO_STATE_ERROR; synchronized (mScoClients) { int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); - if (!mScoClients.isEmpty() && mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) { + // broadcast intent if the connection was initated by AudioService + if (!mScoClients.isEmpty() && + (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL || + mScoAudioState == SCO_STATE_ACTIVATE_REQ || + mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) { broadcast = true; } switch (btState) { case BluetoothHeadset.STATE_AUDIO_CONNECTED: - audioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; - if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) { + state = AudioManager.SCO_AUDIO_STATE_CONNECTED; + if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL && + mScoAudioState != SCO_STATE_DEACTIVATE_REQ && + mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) { mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; } break; case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: - audioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; + state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; mScoAudioState = SCO_STATE_INACTIVE; clearAllScoClients(null, false); break; case BluetoothHeadset.STATE_AUDIO_CONNECTING: - if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) { + if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL && + mScoAudioState != SCO_STATE_DEACTIVATE_REQ && + mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) { mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; } default: @@ -2361,14 +2478,23 @@ public class AudioService extends IAudioService.Stub { } } if (broadcast) { + broadcastScoConnectionState(state); + //FIXME: this is to maintain compatibility with deprecated intent + // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); - newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, audioState); + newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); mContext.sendStickyBroadcast(newIntent); } } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { mBootCompleted = true; sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP, 0, 0, null, 0); + + mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR; + resetBluetoothSco(); + getBluetoothHeadset(); + //FIXME: this is to maintain compatibility with deprecated intent + // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_DISCONNECTED); diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 84f588e..0e161a8 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -628,9 +628,11 @@ public class MediaPlayer * and cannot be directly compared between different media sources or different * instances of the same media source, or across multiple runs of the same * program. - * @hide */ public void setTexture(SurfaceTexture st) { + if (mScreenOnWhilePlaying && st != null && mSurfaceTexture == null) { + Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for SurfaceTexture"); + } mSurfaceHolder = null; mSurface = null; mSurfaceTexture = st; @@ -960,6 +962,9 @@ public class MediaPlayer */ public void setScreenOnWhilePlaying(boolean screenOn) { if (mScreenOnWhilePlaying != screenOn) { + if (screenOn && mSurfaceTexture != null) { + Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for SurfaceTexture"); + } mScreenOnWhilePlaying = screenOn; updateSurfaceScreenOn(); } diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index fb7a871..70053ea 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -400,6 +400,7 @@ status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) { } void AwesomePlayer::reset() { + LOGI("reset"); Mutex::Autolock autoLock(mLock); reset_l(); } @@ -413,8 +414,10 @@ void AwesomePlayer::reset_l() { Playback::STOP, 0); mDecryptHandle = NULL; mDrmManagerClient = NULL; + LOGI("DRM manager client stopped"); } + if (mFlags & PLAYING) { uint32_t params = IMediaPlayerService::kBatteryDataTrackDecoder; if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) { @@ -447,6 +450,7 @@ void AwesomePlayer::reset_l() { mPreparedCondition.wait(mLock); } + LOGI("cancel player events"); cancelPlayerEvents(); mWVMExtractor.clear(); @@ -1081,6 +1085,7 @@ void AwesomePlayer::shutdownVideoDecoder_l() { usleep(1000); } IPCThreadState::self()->flushCommands(); + LOGI("video decoder shutdown completed"); } void AwesomePlayer::setNativeWindow_l(const sp<ANativeWindow> &native) { diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 0f0ffd4..ba495cc 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -3443,7 +3443,7 @@ status_t OMXCodec::start(MetaData *meta) { } status_t OMXCodec::stop() { - CODEC_LOGV("stop mState=%d", mState); + CODEC_LOGI("stop mState=%d", mState); Mutex::Autolock autoLock(mLock); @@ -3505,6 +3505,7 @@ status_t OMXCodec::stop() { mLeftOverBuffer = NULL; } + CODEC_LOGI("stopping video source"); mSource->stop(); CODEC_LOGI("stopped in state %d", mState); diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp index 83a1eaa..26eda0c 100644 --- a/media/libstagefright/WVMExtractor.cpp +++ b/media/libstagefright/WVMExtractor.cpp @@ -45,8 +45,7 @@ namespace android { static Mutex gWVMutex; WVMExtractor::WVMExtractor(const sp<DataSource> &source) - : mDataSource(source), - mUseAdaptiveStreaming(false) { + : mDataSource(source) { { Mutex::Autolock autoLock(gWVMutex); if (gVendorLibHandle == NULL) { @@ -59,13 +58,12 @@ WVMExtractor::WVMExtractor(const sp<DataSource> &source) } } - typedef MediaExtractor *(*GetInstanceFunc)(sp<DataSource>); + typedef WVMLoadableExtractor *(*GetInstanceFunc)(sp<DataSource>); GetInstanceFunc getInstanceFunc = (GetInstanceFunc) dlsym(gVendorLibHandle, "_ZN7android11GetInstanceENS_2spINS_10DataSourceEEE"); if (getInstanceFunc) { - LOGD("Calling GetInstanceFunc"); mImpl = (*getInstanceFunc)(source); CHECK(mImpl != NULL); } else { @@ -102,19 +100,17 @@ sp<MetaData> WVMExtractor::getMetaData() { } int64_t WVMExtractor::getCachedDurationUs(status_t *finalStatus) { - // TODO: Fill this with life. - - *finalStatus = OK; + if (mImpl == NULL) { + return 0; + } - return 0; + return mImpl->getCachedDurationUs(finalStatus); } void WVMExtractor::setAdaptiveStreamingMode(bool adaptive) { - mUseAdaptiveStreaming = adaptive; -} - -bool WVMExtractor::getAdaptiveStreamingMode() const { - return mUseAdaptiveStreaming; + if (mImpl != NULL) { + mImpl->setAdaptiveStreamingMode(adaptive); + } } } //namespace android diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp index 3e4e493..805bd48 100644 --- a/media/libstagefright/chromium_http/support.cpp +++ b/media/libstagefright/chromium_http/support.cpp @@ -23,7 +23,7 @@ #include "support.h" #include "android/net/android_network_library_impl.h" -#include "base/thread.h" +#include "base/threading/thread.h" #include "net/base/cert_verifier.h" #include "net/base/host_resolver.h" #include "net/base/ssl_config_service.h" diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index 13e1662..cffbfb5 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -360,10 +360,14 @@ void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) { mFramesConfigured = true; } - uint32_t timestamp = 0xFFFFFFFF; + uint32_t useExtTimestamp = (inHeader->nOffset == 0); + + // decoder deals in ms, OMX in us. + uint32_t timestamp = + useExtTimestamp ? (inHeader->nTimeStamp + 500) / 1000 : 0xFFFFFFFF; + int32_t bufferSize = inHeader->nFilledLen; - uint32_t useExtTimestamp = 0; if (PVDecodeVideoFrame( mHandle, &bitstream, ×tamp, &bufferSize, &useExtTimestamp, @@ -379,13 +383,20 @@ void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) { return; } - outHeader->nTimeStamp = inHeader->nTimeStamp; + // decoder deals in ms, OMX in us. + outHeader->nTimeStamp = timestamp * 1000; - inInfo->mOwnedByUs = false; - inQueue.erase(inQueue.begin()); - inInfo = NULL; - notifyEmptyBufferDone(inHeader); - inHeader = NULL; + CHECK_LE(bufferSize, inHeader->nFilledLen); + inHeader->nOffset += inHeader->nFilledLen - bufferSize; + inHeader->nFilledLen = bufferSize; + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } ++mInputBufferCount; diff --git a/media/libstagefright/include/SimpleSoftOMXComponent.h b/media/libstagefright/include/SimpleSoftOMXComponent.h index 2a29a7d..50cd275 100644 --- a/media/libstagefright/include/SimpleSoftOMXComponent.h +++ b/media/libstagefright/include/SimpleSoftOMXComponent.h @@ -36,7 +36,7 @@ struct SimpleSoftOMXComponent : public SoftOMXComponent { OMX_PTR appData, OMX_COMPONENTTYPE **component); - virtual ~SimpleSoftOMXComponent(); + virtual void prepareForDestruction(); void onMessageReceived(const sp<AMessage> &msg); diff --git a/media/libstagefright/include/SoftOMXComponent.h b/media/libstagefright/include/SoftOMXComponent.h index 053bc22..a808611 100644 --- a/media/libstagefright/include/SoftOMXComponent.h +++ b/media/libstagefright/include/SoftOMXComponent.h @@ -38,6 +38,8 @@ struct SoftOMXComponent : public RefBase { void setLibHandle(void *libHandle); void *libHandle() const; + virtual void prepareForDestruction() {} + protected: virtual ~SoftOMXComponent(); diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h index 62e5aa5..deecd25 100644 --- a/media/libstagefright/include/WVMExtractor.h +++ b/media/libstagefright/include/WVMExtractor.h @@ -25,6 +25,15 @@ namespace android { class DataSource; +class WVMLoadableExtractor : public MediaExtractor { +public: + WVMLoadableExtractor() {} + virtual ~WVMLoadableExtractor() {} + + virtual int64_t getCachedDurationUs(status_t *finalStatus) = 0; + virtual void setAdaptiveStreamingMode(bool adaptive) = 0; +}; + class WVMExtractor : public MediaExtractor { public: WVMExtractor(const sp<DataSource> &source); @@ -49,20 +58,15 @@ public: // is used. void setAdaptiveStreamingMode(bool adaptive); - // Retrieve the adaptive streaming mode used by the WV component. - bool getAdaptiveStreamingMode() const; - protected: virtual ~WVMExtractor(); private: sp<DataSource> mDataSource; - sp<MediaExtractor> mImpl; - bool mUseAdaptiveStreaming; + sp<WVMLoadableExtractor> mImpl; WVMExtractor(const WVMExtractor &); WVMExtractor &operator=(const WVMExtractor &); - }; } // namespace android diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp index 179b2a0..f7330f3 100644 --- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp +++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp @@ -45,7 +45,11 @@ SimpleSoftOMXComponent::SimpleSoftOMXComponent( PRIORITY_AUDIO); } -SimpleSoftOMXComponent::~SimpleSoftOMXComponent() { +void SimpleSoftOMXComponent::prepareForDestruction() { + // The looper's queue may still contain messages referencing this + // object. Make sure those are flushed before returning so that + // a subsequent dlunload() does not pull out the rug from under us. + mLooper->unregisterHandler(mHandler->id()); mLooper->stop(); } diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp index 6bd6624..04ca39e 100644 --- a/media/libstagefright/omx/SoftOMXPlugin.cpp +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -21,6 +21,7 @@ #include "SoftOMXPlugin.h" #include "include/SoftOMXComponent.h" +#include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AString.h> #include <dlfcn.h> @@ -126,8 +127,11 @@ OMX_ERRORTYPE SoftOMXPlugin::destroyComponentInstance( (SoftOMXComponent *) ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; + me->prepareForDestruction(); + void *libHandle = me->libHandle(); + CHECK_EQ(me->getStrongCount(), 1); me->decStrong(this); me = NULL; diff --git a/native/include/android/configuration.h b/native/include/android/configuration.h index 91533c8..39fef21 100644 --- a/native/include/android/configuration.h +++ b/native/include/android/configuration.h @@ -77,6 +77,7 @@ enum { ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01, ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02, ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03, + ACONFIGURATION_UI_MODE_TYPE_TELEVISION = 0x04, ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00, ACONFIGURATION_UI_MODE_NIGHT_NO = 0x1, diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk index afefee6..ff45edc 100644 --- a/opengl/libs/Android.mk +++ b/opengl/libs/Android.mk @@ -14,7 +14,6 @@ LOCAL_SRC_FILES:= \ EGL/eglApi.cpp \ EGL/trace.cpp \ EGL/getProcAddress.cpp.arm \ - EGL/hooks.cpp \ EGL/Loader.cpp \ # diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index da26229..e94e50e 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -26,11 +26,10 @@ #include <EGL/egl.h> +#include "egldefs.h" +#include "glesv2dbg.h" #include "hooks.h" -#include "egl_impl.h" - #include "Loader.h" -#include "glesv2dbg.h" // ---------------------------------------------------------------------------- namespace android { diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index 31fe306..b11db32 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -33,6 +33,7 @@ #include <utils/String8.h> +#include "egldefs.h" #include "egl_impl.h" #include "egl_tls.h" #include "glesv2dbg.h" @@ -278,6 +279,71 @@ EGLBoolean egl_init_drivers() { return res; } +void gl_unimplemented() { + LOGE("called unimplemented OpenGL ES API"); +} + +// ---------------------------------------------------------------------------- + +#if USE_FAST_TLS_KEY + +// We have a dedicated TLS slot in bionic +static inline gl_hooks_t const * volatile * get_tls_hooks() { + volatile void *tls_base = __get_tls(); + gl_hooks_t const * volatile * tls_hooks = + reinterpret_cast<gl_hooks_t const * volatile *>(tls_base); + return tls_hooks; +} + +void setGlThreadSpecific(gl_hooks_t const *value) { + gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); + tls_hooks[TLS_SLOT_OPENGL_API] = value; +} + +gl_hooks_t const* getGlThreadSpecific() { + gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); + gl_hooks_t const* hooks = tls_hooks[TLS_SLOT_OPENGL_API]; + if (hooks) return hooks; + return &gHooksNoContext; +} + +#else + +void setGlThreadSpecific(gl_hooks_t const *value) { + pthread_setspecific(gGLWrapperKey, value); +} + +gl_hooks_t const* getGlThreadSpecific() { + gl_hooks_t const* hooks = static_cast<gl_hooks_t*>(pthread_getspecific(gGLWrapperKey)); + if (hooks) return hooks; + return &gHooksNoContext; +} + +#endif + +// ---------------------------------------------------------------------------- +// GL / EGL hooks +// ---------------------------------------------------------------------------- + +#undef GL_ENTRY +#undef EGL_ENTRY +#define GL_ENTRY(_r, _api, ...) #_api, +#define EGL_ENTRY(_r, _api, ...) #_api, + +char const * const gl_names[] = { + #include "entries.in" + NULL +}; + +char const * const egl_names[] = { + #include "egl_entries.in" + NULL +}; + +#undef GL_ENTRY +#undef EGL_ENTRY + + // ---------------------------------------------------------------------------- }; // namespace android // ---------------------------------------------------------------------------- diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h index 8c482c3..113595f 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -30,6 +30,7 @@ #include <utils/SortedVector.h> #include <utils/threads.h> +#include "egldefs.h" #include "hooks.h" // ---------------------------------------------------------------------------- diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h index 8b31468..a7989ef 100644 --- a/opengl/libs/EGL/egl_tls.h +++ b/opengl/libs/EGL/egl_tls.h @@ -21,7 +21,12 @@ #include <EGL/egl.h> +#include "egldefs.h" +#include "hooks.h" + +// ---------------------------------------------------------------------------- namespace android { +// ---------------------------------------------------------------------------- class DbgContext; @@ -58,6 +63,16 @@ public: #define setError(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r) +// ---------------------------------------------------------------------------- + +#if EGL_TRACE + +extern gl_hooks_t const* getGLTraceThreadSpecific(); + +#endif + +// ---------------------------------------------------------------------------- }; // namespace android +// ---------------------------------------------------------------------------- #endif // ANDROID_EGL_TLS_H diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h new file mode 100644 index 0000000..107acd9 --- /dev/null +++ b/opengl/libs/EGL/egldefs.h @@ -0,0 +1,71 @@ +/* + ** Copyright 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. + */ + +#ifndef ANDROID_EGLDEFS_H +#define ANDROID_EGLDEFS_H + +#include "hooks.h" + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 4 + +// EGLDisplay are global, not attached to a given thread +const unsigned int NUM_DISPLAYS = 1; + +enum { + IMPL_HARDWARE = 0, + IMPL_SOFTWARE, + IMPL_NUM_IMPLEMENTATIONS +}; + +enum { + GLESv1_INDEX = 0, + GLESv2_INDEX = 1, +}; + +// ---------------------------------------------------------------------------- + +struct egl_connection_t +{ + inline egl_connection_t() : dso(0) { } + void * dso; + gl_hooks_t * hooks[2]; + EGLint major; + EGLint minor; + egl_t egl; +}; + +// ---------------------------------------------------------------------------- + +extern gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS]; +extern gl_hooks_t gHooksNoContext; +extern pthread_key_t gGLWrapperKey; +extern "C" void gl_unimplemented(); + +extern char const * const gl_names[]; +extern char const * const egl_names[]; + +extern egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS]; + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +#endif /* ANDROID_EGLDEFS_H */ diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp index dcf8735c..f89c865 100644 --- a/opengl/libs/EGL/getProcAddress.cpp +++ b/opengl/libs/EGL/getProcAddress.cpp @@ -20,6 +20,7 @@ #include <cutils/log.h> +#include "egldefs.h" #include "hooks.h" // ---------------------------------------------------------------------------- @@ -34,7 +35,7 @@ namespace android { #undef GL_EXTENSION_LIST #undef GET_TLS -#if defined(__arm__) +#if USE_FAST_TLS_KEY #ifdef HAVE_ARM_TLS_REGISTER #define GET_TLS(reg) \ @@ -77,7 +78,7 @@ namespace android { #define GL_EXTENSION(_n) - #warning "eglGetProcAddress() partially supported on this architecture" + #warning "eglGetProcAddress() partially supported" #endif diff --git a/opengl/libs/EGL/hooks.cpp b/opengl/libs/EGL/hooks.cpp deleted file mode 100644 index 72ad6b3..0000000 --- a/opengl/libs/EGL/hooks.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - ** Copyright 2009, 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. - */ - -#include <ctype.h> -#include <stdlib.h> -#include <errno.h> - -#include <cutils/log.h> - -#include "hooks.h" - -// ---------------------------------------------------------------------------- -namespace android { -// ---------------------------------------------------------------------------- - -void gl_unimplemented() { - LOGE("called unimplemented OpenGL ES API"); -} - - -// ---------------------------------------------------------------------------- -// GL / EGL hooks -// ---------------------------------------------------------------------------- - -#undef GL_ENTRY -#undef EGL_ENTRY -#define GL_ENTRY(_r, _api, ...) #_api, -#define EGL_ENTRY(_r, _api, ...) #_api, - -char const * const gl_names[] = { - #include "entries.in" - NULL -}; - -char const * const egl_names[] = { - #include "egl_entries.in" - NULL -}; - -#undef GL_ENTRY -#undef EGL_ENTRY - - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- - diff --git a/opengl/libs/EGL/trace.cpp b/opengl/libs/EGL/trace.cpp index f3e101b..0e934e2 100644 --- a/opengl/libs/EGL/trace.cpp +++ b/opengl/libs/EGL/trace.cpp @@ -26,6 +26,7 @@ #include <cutils/log.h> +#include "egl_tls.h" #include "hooks.h" // ---------------------------------------------------------------------------- diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h index d24b047..a809316 100644 --- a/opengl/libs/egl_impl.h +++ b/opengl/libs/egl_impl.h @@ -25,27 +25,12 @@ #include "hooks.h" -#define VERSION_MAJOR 1 -#define VERSION_MINOR 4 - // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- -struct egl_connection_t -{ - inline egl_connection_t() : dso(0) { } - void * dso; - gl_hooks_t * hooks[2]; - EGLint major; - EGLint minor; - egl_t egl; -}; - EGLAPI EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image); -extern egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS]; - // ---------------------------------------------------------------------------- }; // namespace android // ---------------------------------------------------------------------------- diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h index 812e26d..7ac88cd 100644 --- a/opengl/libs/hooks.h +++ b/opengl/libs/hooks.h @@ -54,22 +54,6 @@ namespace android { // ---------------------------------------------------------------------------- -// EGLDisplay are global, not attached to a given thread -const unsigned int NUM_DISPLAYS = 1; - -enum { - IMPL_HARDWARE = 0, - IMPL_SOFTWARE, - IMPL_NUM_IMPLEMENTATIONS -}; - -enum { - GLESv1_INDEX = 0, - GLESv2_INDEX = 1, -}; - -// ---------------------------------------------------------------------------- - // GL / EGL hooks #undef GL_ENTRY @@ -92,60 +76,8 @@ struct gl_hooks_t { #undef GL_ENTRY #undef EGL_ENTRY - -// ---------------------------------------------------------------------------- - -extern gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS]; -extern gl_hooks_t gHooksNoContext; -extern pthread_key_t gGLWrapperKey; -extern "C" void gl_unimplemented(); - -extern char const * const gl_names[]; -extern char const * const egl_names[]; - -// ---------------------------------------------------------------------------- - -#if USE_FAST_TLS_KEY - -// We have a dedicated TLS slot in bionic -static inline gl_hooks_t const * volatile * get_tls_hooks() { - volatile void *tls_base = __get_tls(); - gl_hooks_t const * volatile * tls_hooks = - reinterpret_cast<gl_hooks_t const * volatile *>(tls_base); - return tls_hooks; -} - -static inline void setGlThreadSpecific(gl_hooks_t const *value) { - gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); - tls_hooks[TLS_SLOT_OPENGL_API] = value; -} - -static gl_hooks_t const* getGlThreadSpecific() { - gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); - gl_hooks_t const* hooks = tls_hooks[TLS_SLOT_OPENGL_API]; - if (hooks) return hooks; - return &gHooksNoContext; -} - -#else - -static inline void setGlThreadSpecific(gl_hooks_t const *value) { - pthread_setspecific(gGLWrapperKey, value); -} - -static gl_hooks_t const* getGlThreadSpecific() { - gl_hooks_t const* hooks = static_cast<gl_hooks_t*>(pthread_getspecific(gGLWrapperKey)); - if (hooks) return hooks; - return &gHooksNoContext; -} - -#endif - -#if EGL_TRACE - -extern gl_hooks_t const* getGLTraceThreadSpecific(); - -#endif +EGLAPI void setGlThreadSpecific(gl_hooks_t const *value); +EGLAPI gl_hooks_t const* getGlThreadSpecific(); // ---------------------------------------------------------------------------- }; // namespace android diff --git a/packages/SystemUI/res/layout-large/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml index d9f3f23..d9f3f23 100644 --- a/packages/SystemUI/res/layout-large/status_bar.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_input_methods_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_item.xml index 3fef7e0..3fef7e0 100644 --- a/packages/SystemUI/res/layout-large/status_bar_input_methods_item.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_item.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_input_methods_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_panel.xml index f6ed804..f6ed804 100644 --- a/packages/SystemUI/res/layout-large/status_bar_input_methods_panel.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_panel.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_area.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml index f53b29e..f53b29e 100644 --- a/packages/SystemUI/res/layout-large/status_bar_notification_area.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml index ef57228..ef57228 100644 --- a/packages/SystemUI/res/layout-large/status_bar_notification_panel.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_panel_title.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel_title.xml index 543f4ed..543f4ed 100644 --- a/packages/SystemUI/res/layout-large/status_bar_notification_panel_title.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel_title.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_peek.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_peek.xml index 02f9a90..02f9a90 100644 --- a/packages/SystemUI/res/layout-large/status_bar_notification_peek.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_peek.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_row.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_row.xml index 8e456b2..8e456b2 100644 --- a/packages/SystemUI/res/layout-large/status_bar_notification_row.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_row.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_pocket_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_pocket_panel.xml index e4a6da4..e4a6da4 100644 --- a/packages/SystemUI/res/layout-large/status_bar_pocket_panel.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_pocket_panel.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml index cd42d7e..cd42d7e 100644 --- a/packages/SystemUI/res/layout-large/status_bar_recent_item.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml index 75fdc67..75fdc67 100644 --- a/packages/SystemUI/res/layout-large/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_recent_panel_footer.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel_footer.xml index 4d14d1f..4d14d1f 100644 --- a/packages/SystemUI/res/layout-large/status_bar_recent_panel_footer.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel_footer.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_settings_view.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_settings_view.xml index 677988d..677988d 100644 --- a/packages/SystemUI/res/layout-large/status_bar_settings_view.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_settings_view.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_ticker_compat.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_ticker_compat.xml index d963de1..d963de1 100644 --- a/packages/SystemUI/res/layout-large/status_bar_ticker_compat.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_ticker_compat.xml diff --git a/packages/SystemUI/res/layout-large/status_bar_ticker_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_ticker_panel.xml index 6cd8899..6cd8899 100644 --- a/packages/SystemUI/res/layout-large/status_bar_ticker_panel.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_ticker_panel.xml diff --git a/packages/SystemUI/res/values-xlarge/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml index 12c8950..12c8950 100644 --- a/packages/SystemUI/res/values-xlarge/styles.xml +++ b/packages/SystemUI/res/values-sw600dp/styles.xml diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index a6ba1a0..8e86eda 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -87,40 +87,20 @@ static const int kDumpLockSleep = 20000; static const nsecs_t kWarningThrottle = seconds(5); -#define AUDIOFLINGER_SECURITY_ENABLED 1 - // ---------------------------------------------------------------------------- static bool recordingAllowed() { -#ifndef HAVE_ANDROID_OS - return true; -#endif -#if AUDIOFLINGER_SECURITY_ENABLED if (getpid() == IPCThreadState::self()->getCallingPid()) return true; bool ok = checkCallingPermission(String16("android.permission.RECORD_AUDIO")); if (!ok) LOGE("Request requires android.permission.RECORD_AUDIO"); return ok; -#else - if (!checkCallingPermission(String16("android.permission.RECORD_AUDIO"))) - LOGW("WARNING: Need to add android.permission.RECORD_AUDIO to manifest"); - return true; -#endif } static bool settingsAllowed() { -#ifndef HAVE_ANDROID_OS - return true; -#endif -#if AUDIOFLINGER_SECURITY_ENABLED if (getpid() == IPCThreadState::self()->getCallingPid()) return true; bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")); if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS"); return ok; -#else - if (!checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"))) - LOGW("WARNING: Need to add android.permission.MODIFY_AUDIO_SETTINGS to manifest"); - return true; -#endif } // To collect the amplifier usage diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index ff4b11a..af30887 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -101,7 +101,7 @@ EventHub::Device::Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier) : next(NULL), fd(fd), id(id), path(path), identifier(identifier), - classes(0), keyBitmask(NULL), relBitmask(NULL), + classes(0), keyBitmask(NULL), relBitmask(NULL), propBitmask(NULL), configuration(NULL), virtualKeyMap(NULL) { } @@ -109,6 +109,7 @@ EventHub::Device::~Device() { close(); delete[] keyBitmask; delete[] relBitmask; + delete[] propBitmask; delete configuration; delete virtualKeyMap; } @@ -205,6 +206,18 @@ bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const { return false; } +bool EventHub::hasInputProperty(int32_t deviceId, int property) const { + if (property >= 0 && property <= INPUT_PROP_MAX) { + AutoMutex _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device && device->propBitmask) { + return test_bit(property, device->propBitmask); + } + } + return false; +} + int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { if (scanCode >= 0 && scanCode <= KEY_MAX) { AutoMutex _l(mLock); @@ -834,24 +847,24 @@ int EventHub::openDevice(const char *devicePath) { memset(sw_bitmask, 0, sizeof(sw_bitmask)); ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask); - device->keyBitmask = new uint8_t[sizeof(key_bitmask)]; - if (device->keyBitmask != NULL) { - memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask)); - } else { - delete device; - LOGE("out of memory allocating key bitmask"); - return -1; - } + uint8_t prop_bitmask[sizeof_bit_array(INPUT_PROP_MAX + 1)]; + memset(prop_bitmask, 0, sizeof(prop_bitmask)); + ioctl(fd, EVIOCGPROP(sizeof(prop_bitmask)), prop_bitmask); + device->keyBitmask = new uint8_t[sizeof(key_bitmask)]; device->relBitmask = new uint8_t[sizeof(rel_bitmask)]; - if (device->relBitmask != NULL) { - memcpy(device->relBitmask, rel_bitmask, sizeof(rel_bitmask)); - } else { + device->propBitmask = new uint8_t[sizeof(prop_bitmask)]; + + if (!device->keyBitmask || !device->relBitmask || !device->propBitmask) { delete device; - LOGE("out of memory allocating rel bitmask"); + LOGE("out of memory allocating bitmasks"); return -1; } + memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask)); + memcpy(device->relBitmask, rel_bitmask, sizeof(rel_bitmask)); + memcpy(device->propBitmask, prop_bitmask, sizeof(prop_bitmask)); + // See if this is a keyboard. Ignore everything in the button range except for // joystick and gamepad buttons which are handled like keyboards for the most part. bool haveKeyboardKeys = containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC)) diff --git a/services/input/EventHub.h b/services/input/EventHub.h index 4d26a95..ca33619 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -34,25 +34,38 @@ #include <linux/input.h> -/* These constants are not defined in linux/input.h but they are part of the multitouch - * input protocol. */ - -#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ -#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ -#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ -#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ -#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ -#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */ -#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */ -#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device (finger, pen, ...) */ -#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ -#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ -#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ - -#define MT_TOOL_FINGER 0 /* Identifies a finger */ -#define MT_TOOL_PEN 1 /* Identifies a pen */ +/* These constants are not defined in linux/input.h in the version of the kernel + * headers currently provided with Bionic. */ + +#define EVIOCGPROP(len) _IOC(_IOC_READ, 'E', 0x09, len) + +#define INPUT_PROP_POINTER 0x00 +#define INPUT_PROP_DIRECT 0x01 +#define INPUT_PROP_BUTTONPAD 0x02 +#define INPUT_PROP_SEMI_MT 0x03 +#define INPUT_PROP_MAX 0x1f +#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) + +#define ABS_MT_SLOT 0x2f +#define ABS_MT_TOUCH_MAJOR 0x30 +#define ABS_MT_TOUCH_MINOR 0x31 +#define ABS_MT_WIDTH_MAJOR 0x32 +#define ABS_MT_WIDTH_MINOR 0x33 +#define ABS_MT_ORIENTATION 0x34 +#define ABS_MT_POSITION_X 0x35 +#define ABS_MT_POSITION_Y 0x36 +#define ABS_MT_TOOL_TYPE 0x37 +#define ABS_MT_BLOB_ID 0x38 +#define ABS_MT_TRACKING_ID 0x39 +#define ABS_MT_PRESSURE 0x3a +#define ABS_MT_DISTANCE 0x3b + +#define MT_TOOL_FINGER 0 +#define MT_TOOL_PEN 1 #define SYN_MT_REPORT 2 +#define SYN_DROPPED 3 + /* Convenience constants. */ @@ -172,6 +185,8 @@ public: virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0; + virtual bool hasInputProperty(int32_t deviceId, int property) const = 0; + virtual status_t mapKey(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const = 0; @@ -236,6 +251,8 @@ public: virtual bool hasRelativeAxis(int32_t deviceId, int axis) const; + virtual bool hasInputProperty(int32_t deviceId, int property) const; + virtual status_t mapKey(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const; @@ -286,6 +303,7 @@ private: uint32_t classes; uint8_t* keyBitmask; uint8_t* relBitmask; + uint8_t* propBitmask; String8 configurationFile; PropertyMap* configuration; VirtualKeyMap* virtualKeyMap; diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 6003207..25a2c78 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -58,6 +58,9 @@ namespace android { // --- Constants --- +// Maximum number of slots supported when using the slot-based Multitouch Protocol B. +static const size_t MAX_SLOTS = 32; + // Quiet time between certain gesture transitions. // Time to allow for all fingers or buttons to settle into a stable state before // starting a new gesture. @@ -809,7 +812,8 @@ bool InputReaderThread::threadLoop() { // --- InputDevice --- InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) : - mContext(context), mId(id), mName(name), mSources(0), mIsExternal(false) { + mContext(context), mId(id), mName(name), mSources(0), + mIsExternal(false), mDropUntilNextSync(false) { } InputDevice::~InputDevice() { @@ -898,9 +902,26 @@ void InputDevice::process(const RawEvent* rawEvents, size_t count) { rawEvent->value, rawEvent->flags); #endif - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - mapper->process(rawEvent); + if (mDropUntilNextSync) { + if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) { + mDropUntilNextSync = false; +#if DEBUG_RAW_EVENTS + LOGD("Recovered from input event buffer overrun."); +#endif + } else { +#if DEBUG_RAW_EVENTS + LOGD("Dropped input event while waiting for next input sync."); +#endif + } + } else if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_DROPPED) { + LOGI("Detected input event buffer overrun for device %s.", mName.string()); + mDropUntilNextSync = true; + reset(); + } else { + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->process(rawEvent); + } } } } @@ -1812,6 +1833,10 @@ void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { info->addMotionRange(mLocked.orientedRanges.orientation); } + if (mLocked.orientedRanges.haveDistance) { + info->addMotionRange(mLocked.orientedRanges.distance); + } + if (mPointerController != NULL) { float minX, minY, maxX, maxY; if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { @@ -1849,6 +1874,7 @@ void TouchInputMapper::dump(String8& dump) { dump.appendFormat(INDENT4 "PressureScale: %0.3f\n", mLocked.pressureScale); dump.appendFormat(INDENT4 "SizeScale: %0.3f\n", mLocked.sizeScale); dump.appendFormat(INDENT4 "OrientationScale: %0.3f\n", mLocked.orientationScale); + dump.appendFormat(INDENT4 "DistanceScale: %0.3f\n", mLocked.distanceScale); dump.appendFormat(INDENT3 "Last Touch:\n"); dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount); @@ -1889,6 +1915,7 @@ void TouchInputMapper::initializeLocked() { mLocked.orientedRanges.haveTouchSize = false; mLocked.orientedRanges.haveToolSize = false; mLocked.orientedRanges.haveOrientation = false; + mLocked.orientedRanges.haveDistance = false; mPointerGesture.reset(); } @@ -1947,9 +1974,14 @@ void TouchInputMapper::configureParameters() { // The device is a cursor device with a touch pad attached. // By default don't use the touch pad to move the pointer. mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) { + // The device is a pointing device like a track pad. + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) { + // The device is a touch screen. + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; } else { - // The device is just a touch pad. - // By default use the touch pad to move the pointer and to perform related gestures. + // The device is a touch pad of unknown purpose. mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; } @@ -2016,6 +2048,9 @@ void TouchInputMapper::configureRawAxes() { mRawAxes.toolMajor.clear(); mRawAxes.toolMinor.clear(); mRawAxes.orientation.clear(); + mRawAxes.distance.clear(); + mRawAxes.trackingId.clear(); + mRawAxes.slot.clear(); } void TouchInputMapper::dumpRawAxes(String8& dump) { @@ -2028,6 +2063,9 @@ void TouchInputMapper::dumpRawAxes(String8& dump) { dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor"); dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor"); dumpRawAbsoluteAxisInfo(dump, mRawAxes.orientation, "Orientation"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.distance, "Distance"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.trackingId, "TrackingId"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.slot, "Slot"); } bool TouchInputMapper::configureSurfaceLocked() { @@ -2234,6 +2272,8 @@ bool TouchInputMapper::configureSurfaceLocked() { } } + mLocked.orientedRanges.haveOrientation = true; + mLocked.orientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; mLocked.orientedRanges.orientation.source = mTouchSource; mLocked.orientedRanges.orientation.min = - M_PI_2; @@ -2241,6 +2281,31 @@ bool TouchInputMapper::configureSurfaceLocked() { mLocked.orientedRanges.orientation.flat = 0; mLocked.orientedRanges.orientation.fuzz = 0; } + + // Distance + mLocked.distanceScale = 0; + if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) { + if (mCalibration.distanceCalibration + == Calibration::DISTANCE_CALIBRATION_SCALED) { + if (mCalibration.haveDistanceScale) { + mLocked.distanceScale = mCalibration.distanceScale; + } else { + mLocked.distanceScale = 1.0f; + } + } + + mLocked.orientedRanges.haveDistance = true; + + mLocked.orientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE; + mLocked.orientedRanges.distance.source = mTouchSource; + mLocked.orientedRanges.distance.min = + mRawAxes.distance.minValue * mLocked.distanceScale; + mLocked.orientedRanges.distance.max = + mRawAxes.distance.minValue * mLocked.distanceScale; + mLocked.orientedRanges.distance.flat = 0; + mLocked.orientedRanges.distance.fuzz = + mRawAxes.distance.fuzz * mLocked.distanceScale; + } } if (orientationChanged || sizeChanged) { @@ -2518,6 +2583,23 @@ void TouchInputMapper::parseCalibration() { orientationCalibrationString.string()); } } + + // Distance + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT; + String8 distanceCalibrationString; + if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) { + if (distanceCalibrationString == "none") { + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; + } else if (distanceCalibrationString == "scaled") { + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; + } else if (distanceCalibrationString != "default") { + LOGW("Invalid value for touch.distance.calibration: '%s'", + distanceCalibrationString.string()); + } + } + + out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), + out.distanceScale); } void TouchInputMapper::resolveCalibration() { @@ -2618,6 +2700,20 @@ void TouchInputMapper::resolveCalibration() { default: break; } + + // Distance + switch (mCalibration.distanceCalibration) { + case Calibration::DISTANCE_CALIBRATION_DEFAULT: + if (mRawAxes.distance.valid) { + mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; + } else { + mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; + } + break; + + default: + break; + } } void TouchInputMapper::dumpCalibration(String8& dump) { @@ -2740,6 +2836,23 @@ void TouchInputMapper::dumpCalibration(String8& dump) { default: LOG_ASSERT(false); } + + // Distance + switch (mCalibration.distanceCalibration) { + case Calibration::DISTANCE_CALIBRATION_NONE: + dump.append(INDENT4 "touch.distance.calibration: none\n"); + break; + case Calibration::DISTANCE_CALIBRATION_SCALED: + dump.append(INDENT4 "touch.distance.calibration: scaled\n"); + break; + default: + LOG_ASSERT(false); + } + + if (mCalibration.haveDistanceScale) { + dump.appendFormat(INDENT4 "touch.distance.scale: %0.3f\n", + mCalibration.distanceScale); + } } void TouchInputMapper::reset() { @@ -3247,6 +3360,16 @@ void TouchInputMapper::prepareTouches(int32_t* outEdgeFlags, orientation = 0; } + // Distance + float distance; + switch (mCalibration.distanceCalibration) { + case Calibration::DISTANCE_CALIBRATION_SCALED: + distance = in.distance * mLocked.distanceScale; + break; + default: + distance = 0; + } + // X and Y // Adjust coords for surface orientation. float x, y; @@ -3289,6 +3412,9 @@ void TouchInputMapper::prepareTouches(int32_t* outEdgeFlags, out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); + if (distance != 0) { + out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance); + } // Write output properties. PointerProperties& properties = mCurrentTouchProperties[i]; @@ -5020,13 +5146,13 @@ bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCode SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : TouchInputMapper(device) { - initialize(); + clearState(); } SingleTouchInputMapper::~SingleTouchInputMapper() { } -void SingleTouchInputMapper::initialize() { +void SingleTouchInputMapper::clearState() { mAccumulator.clear(); mDown = false; @@ -5040,7 +5166,7 @@ void SingleTouchInputMapper::initialize() { void SingleTouchInputMapper::reset() { TouchInputMapper::reset(); - initialize(); + clearState(); } void SingleTouchInputMapper::process(const RawEvent* rawEvent) { @@ -5144,6 +5270,7 @@ void SingleTouchInputMapper::sync(nsecs_t when) { mCurrentTouch.pointers[0].toolMajor = mToolWidth; mCurrentTouch.pointers[0].toolMinor = mToolWidth; mCurrentTouch.pointers[0].orientation = 0; + mCurrentTouch.pointers[0].distance = 0; mCurrentTouch.pointers[0].isStylus = false; // TODO: Set stylus mCurrentTouch.idToIndex[0] = 0; mCurrentTouch.idBits.markBit(0); @@ -5168,22 +5295,22 @@ void SingleTouchInputMapper::configureRawAxes() { // --- MultiTouchInputMapper --- MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : - TouchInputMapper(device) { - initialize(); + TouchInputMapper(device), mSlotCount(0), mUsingSlotsProtocol(false) { + clearState(); } MultiTouchInputMapper::~MultiTouchInputMapper() { } -void MultiTouchInputMapper::initialize() { - mAccumulator.clear(); +void MultiTouchInputMapper::clearState() { + mAccumulator.clear(mSlotCount); mButtonState = 0; } void MultiTouchInputMapper::reset() { TouchInputMapper::reset(); - initialize(); + clearState(); } void MultiTouchInputMapper::process(const RawEvent* rawEvent) { @@ -5203,45 +5330,69 @@ void MultiTouchInputMapper::process(const RawEvent* rawEvent) { } case EV_ABS: { - uint32_t pointerIndex = mAccumulator.pointerCount; - Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex]; + bool newSlot = false; + if (mUsingSlotsProtocol && rawEvent->scanCode == ABS_MT_SLOT) { + mAccumulator.currentSlot = rawEvent->value; + newSlot = true; + } + + if (mAccumulator.currentSlot < 0 || size_t(mAccumulator.currentSlot) >= mSlotCount) { + if (newSlot) { +#if DEBUG_POINTERS + LOGW("MultiTouch device %s emitted invalid slot index %d but it " + "should be between 0 and %d; ignoring this slot.", + getDeviceName().string(), mAccumulator.currentSlot, mSlotCount); +#endif + } + break; + } + + Accumulator::Slot* slot = &mAccumulator.slots[mAccumulator.currentSlot]; switch (rawEvent->scanCode) { case ABS_MT_POSITION_X: - pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_X; - pointer->absMTPositionX = rawEvent->value; + slot->fields |= Accumulator::FIELD_ABS_MT_POSITION_X; + slot->absMTPositionX = rawEvent->value; break; case ABS_MT_POSITION_Y: - pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y; - pointer->absMTPositionY = rawEvent->value; + slot->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y; + slot->absMTPositionY = rawEvent->value; break; case ABS_MT_TOUCH_MAJOR: - pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR; - pointer->absMTTouchMajor = rawEvent->value; + slot->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR; + slot->absMTTouchMajor = rawEvent->value; break; case ABS_MT_TOUCH_MINOR: - pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR; - pointer->absMTTouchMinor = rawEvent->value; + slot->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR; + slot->absMTTouchMinor = rawEvent->value; break; case ABS_MT_WIDTH_MAJOR: - pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; - pointer->absMTWidthMajor = rawEvent->value; + slot->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; + slot->absMTWidthMajor = rawEvent->value; break; case ABS_MT_WIDTH_MINOR: - pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR; - pointer->absMTWidthMinor = rawEvent->value; + slot->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR; + slot->absMTWidthMinor = rawEvent->value; break; case ABS_MT_ORIENTATION: - pointer->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION; - pointer->absMTOrientation = rawEvent->value; + slot->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION; + slot->absMTOrientation = rawEvent->value; break; case ABS_MT_TRACKING_ID: - pointer->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID; - pointer->absMTTrackingId = rawEvent->value; + if (mUsingSlotsProtocol && rawEvent->value < 0) { + slot->clear(); + } else { + slot->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID; + slot->absMTTrackingId = rawEvent->value; + } break; case ABS_MT_PRESSURE: - pointer->fields |= Accumulator::FIELD_ABS_MT_PRESSURE; - pointer->absMTPressure = rawEvent->value; + slot->fields |= Accumulator::FIELD_ABS_MT_PRESSURE; + slot->absMTPressure = rawEvent->value; + break; + case ABS_MT_TOOL_TYPE: + slot->fields |= Accumulator::FIELD_ABS_MT_TOOL_TYPE; + slot->absMTToolType = rawEvent->value; break; } break; @@ -5251,19 +5402,7 @@ void MultiTouchInputMapper::process(const RawEvent* rawEvent) { switch (rawEvent->scanCode) { case SYN_MT_REPORT: { // MultiTouch Sync: The driver has returned all data for *one* of the pointers. - uint32_t pointerIndex = mAccumulator.pointerCount; - - if (mAccumulator.pointers[pointerIndex].fields) { - if (pointerIndex == MAX_POINTERS) { - LOGW("MultiTouch device driver returned more than maximum of %d pointers.", - MAX_POINTERS); - } else { - pointerIndex += 1; - mAccumulator.pointerCount = pointerIndex; - } - } - - mAccumulator.pointers[pointerIndex].clear(); + mAccumulator.currentSlot += 1; break; } @@ -5279,99 +5418,120 @@ void MultiTouchInputMapper::sync(nsecs_t when) { static const uint32_t REQUIRED_FIELDS = Accumulator::FIELD_ABS_MT_POSITION_X | Accumulator::FIELD_ABS_MT_POSITION_Y; - uint32_t inCount = mAccumulator.pointerCount; - uint32_t outCount = 0; + size_t inCount = mSlotCount; + size_t outCount = 0; bool havePointerIds = true; mCurrentTouch.clear(); - for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) { - const Accumulator::Pointer& inPointer = mAccumulator.pointers[inIndex]; - uint32_t fields = inPointer.fields; + for (size_t inIndex = 0; inIndex < inCount; inIndex++) { + const Accumulator::Slot& inSlot = mAccumulator.slots[inIndex]; + uint32_t fields = inSlot.fields; if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) { // Some drivers send empty MT sync packets without X / Y to indicate a pointer up. + // This may also indicate an unused slot. // Drop this finger. continue; } + if (outCount >= MAX_POINTERS) { +#if DEBUG_POINTERS + LOGD("MultiTouch device %s emitted more than maximum of %d pointers; " + "ignoring the rest.", + getDeviceName().string(), MAX_POINTERS); +#endif + break; // too many fingers! + } + PointerData& outPointer = mCurrentTouch.pointers[outCount]; - outPointer.x = inPointer.absMTPositionX; - outPointer.y = inPointer.absMTPositionY; + outPointer.x = inSlot.absMTPositionX; + outPointer.y = inSlot.absMTPositionY; if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) { - if (inPointer.absMTPressure <= 0) { - // Some devices send sync packets with X / Y but with a 0 pressure to indicate - // a pointer going up. Drop this finger. - continue; - } - outPointer.pressure = inPointer.absMTPressure; + outPointer.pressure = inSlot.absMTPressure; } else { // Default pressure to 0 if absent. outPointer.pressure = 0; } if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) { - if (inPointer.absMTTouchMajor <= 0) { + if (inSlot.absMTTouchMajor <= 0) { // Some devices send sync packets with X / Y but with a 0 touch major to indicate // a pointer going up. Drop this finger. continue; } - outPointer.touchMajor = inPointer.absMTTouchMajor; + outPointer.touchMajor = inSlot.absMTTouchMajor; } else { // Default touch area to 0 if absent. outPointer.touchMajor = 0; } if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) { - outPointer.touchMinor = inPointer.absMTTouchMinor; + outPointer.touchMinor = inSlot.absMTTouchMinor; } else { // Assume touch area is circular. outPointer.touchMinor = outPointer.touchMajor; } if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) { - outPointer.toolMajor = inPointer.absMTWidthMajor; + outPointer.toolMajor = inSlot.absMTWidthMajor; } else { // Default tool area to 0 if absent. outPointer.toolMajor = 0; } if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) { - outPointer.toolMinor = inPointer.absMTWidthMinor; + outPointer.toolMinor = inSlot.absMTWidthMinor; } else { // Assume tool area is circular. outPointer.toolMinor = outPointer.toolMajor; } if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) { - outPointer.orientation = inPointer.absMTOrientation; + outPointer.orientation = inSlot.absMTOrientation; } else { // Default orientation to vertical if absent. outPointer.orientation = 0; } - outPointer.isStylus = false; // TODO: Handle stylus + if (fields & Accumulator::FIELD_ABS_MT_DISTANCE) { + outPointer.distance = inSlot.absMTDistance; + } else { + // Default distance is 0 (direct contact). + outPointer.distance = 0; + } + + if (fields & Accumulator::FIELD_ABS_MT_TOOL_TYPE) { + outPointer.isStylus = (inSlot.absMTToolType == MT_TOOL_PEN); + } else { + // Assume this is not a stylus. + outPointer.isStylus = false; + } // Assign pointer id using tracking id if available. if (havePointerIds) { - if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) { - uint32_t id = uint32_t(inPointer.absMTTrackingId); + int32_t id; + if (mUsingSlotsProtocol) { + id = inIndex; + } else if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) { + id = inSlot.absMTTrackingId; + } else { + id = -1; + } - if (id > MAX_POINTER_ID) { + if (id >= 0 && id <= MAX_POINTER_ID) { + outPointer.id = id; + mCurrentTouch.idToIndex[id] = outCount; + mCurrentTouch.idBits.markBit(id); + } else { + if (id >= 0) { #if DEBUG_POINTERS - LOGD("Pointers: Ignoring driver provided pointer id %d because " - "it is larger than max supported id %d", + LOGD("Pointers: Ignoring driver provided slot index or tracking id %d because " + "it is larger than the maximum supported pointer id %d", id, MAX_POINTER_ID); #endif - havePointerIds = false; - } - else { - outPointer.id = id; - mCurrentTouch.idToIndex[id] = outCount; - mCurrentTouch.idBits.markBit(id); } - } else { havePointerIds = false; } } @@ -5386,20 +5546,40 @@ void MultiTouchInputMapper::sync(nsecs_t when) { syncTouch(when, havePointerIds); - mAccumulator.clear(); + mAccumulator.clear(mUsingSlotsProtocol ? 0 : mSlotCount); } void MultiTouchInputMapper::configureRawAxes() { TouchInputMapper::configureRawAxes(); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mRawAxes.x); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mRawAxes.y); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mRawAxes.touchMajor); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mRawAxes.touchMinor); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mRawAxes.toolMajor); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mRawAxes.toolMinor); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mRawAxes.orientation); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mRawAxes.pressure); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, &mRawAxes.x); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, &mRawAxes.y); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, &mRawAxes.touchMajor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, &mRawAxes.touchMinor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, &mRawAxes.toolMajor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, &mRawAxes.toolMinor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, &mRawAxes.orientation); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, &mRawAxes.pressure); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_DISTANCE, &mRawAxes.distance); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TRACKING_ID, &mRawAxes.trackingId); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_SLOT, &mRawAxes.slot); + + if (mRawAxes.trackingId.valid + && mRawAxes.slot.valid && mRawAxes.slot.minValue == 0 && mRawAxes.slot.maxValue > 0) { + mSlotCount = mRawAxes.slot.maxValue + 1; + if (mSlotCount > MAX_SLOTS) { + LOGW("MultiTouch Device %s reported %d slots but the framework " + "only supports a maximum of %d slots at this time.", + getDeviceName().string(), mSlotCount, MAX_SLOTS); + mSlotCount = MAX_SLOTS; + } + mUsingSlotsProtocol = true; + } else { + mSlotCount = MAX_POINTERS; + mUsingSlotsProtocol = false; + } + + mAccumulator.allocateSlots(mSlotCount); } diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 62ac4b2..85338b6 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -331,6 +331,7 @@ private: String8 mName; uint32_t mSources; bool mIsExternal; + bool mDropUntilNextSync; typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); @@ -602,6 +603,7 @@ protected: int32_t toolMajor; int32_t toolMinor; int32_t orientation; + int32_t distance; bool isStylus; inline bool operator== (const PointerData& other) const { @@ -613,7 +615,8 @@ protected: && touchMinor == other.touchMinor && toolMajor == other.toolMajor && toolMinor == other.toolMinor - && orientation == other.orientation; + && orientation == other.orientation + && distance == other.distance; } inline bool operator!= (const PointerData& other) const { return !(*this == other); @@ -759,6 +762,17 @@ protected: }; OrientationCalibration orientationCalibration; + + // Distance + enum DistanceCalibration { + DISTANCE_CALIBRATION_DEFAULT, + DISTANCE_CALIBRATION_NONE, + DISTANCE_CALIBRATION_SCALED, + }; + + DistanceCalibration distanceCalibration; + bool haveDistanceScale; + float distanceScale; } mCalibration; // Raw axis information from the driver. @@ -771,6 +785,9 @@ protected: RawAbsoluteAxisInfo toolMajor; RawAbsoluteAxisInfo toolMinor; RawAbsoluteAxisInfo orientation; + RawAbsoluteAxisInfo distance; + RawAbsoluteAxisInfo trackingId; + RawAbsoluteAxisInfo slot; } mRawAxes; // Current and previous touch sample data. @@ -819,6 +836,8 @@ protected: float orientationScale; + float distanceScale; + // Oriented motion ranges for input device info. struct OrientedRanges { InputDeviceInfo::MotionRange x; @@ -840,6 +859,9 @@ protected: bool haveOrientation; InputDeviceInfo::MotionRange orientation; + + bool haveDistance; + InputDeviceInfo::MotionRange distance; } orientedRanges; // Oriented dimensions and precision. @@ -1146,7 +1168,7 @@ private: int32_t mToolWidth; int32_t mButtonState; - void initialize(); + void clearState(); void sync(nsecs_t when); }; @@ -1166,20 +1188,21 @@ protected: private: struct Accumulator { enum { - FIELD_ABS_MT_POSITION_X = 1, - FIELD_ABS_MT_POSITION_Y = 2, - FIELD_ABS_MT_TOUCH_MAJOR = 4, - FIELD_ABS_MT_TOUCH_MINOR = 8, - FIELD_ABS_MT_WIDTH_MAJOR = 16, - FIELD_ABS_MT_WIDTH_MINOR = 32, - FIELD_ABS_MT_ORIENTATION = 64, - FIELD_ABS_MT_TRACKING_ID = 128, - FIELD_ABS_MT_PRESSURE = 256, + FIELD_ABS_MT_POSITION_X = 1 << 0, + FIELD_ABS_MT_POSITION_Y = 1 << 1, + FIELD_ABS_MT_TOUCH_MAJOR = 1 << 2, + FIELD_ABS_MT_TOUCH_MINOR = 1 << 3, + FIELD_ABS_MT_WIDTH_MAJOR = 1 << 4, + FIELD_ABS_MT_WIDTH_MINOR = 1 << 5, + FIELD_ABS_MT_ORIENTATION = 1 << 6, + FIELD_ABS_MT_TRACKING_ID = 1 << 7, + FIELD_ABS_MT_PRESSURE = 1 << 8, + FIELD_ABS_MT_TOOL_TYPE = 1 << 9, + FIELD_ABS_MT_DISTANCE = 1 << 10, }; - uint32_t pointerCount; - struct Pointer { - uint32_t fields; + struct Slot { + uint32_t fields; // 0 if slot is unused int32_t absMTPositionX; int32_t absMTPositionY; @@ -1190,27 +1213,56 @@ private: int32_t absMTOrientation; int32_t absMTTrackingId; int32_t absMTPressure; + int32_t absMTToolType; + int32_t absMTDistance; + + inline Slot() { + clear(); + } inline void clear() { fields = 0; } - } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks + }; + + // Current slot index. + int32_t currentSlot; + + // Array of slots. + Slot* slots; // Bitfield of buttons that went down or up. uint32_t buttonDown; uint32_t buttonUp; - inline void clear() { - pointerCount = 0; - pointers[0].clear(); + Accumulator() : slots(NULL) { + clear(false); + } + + ~Accumulator() { + delete[] slots; + } + + void allocateSlots(size_t slotCount) { + slots = new Slot[slotCount]; + } + + void clear(size_t slotCount) { + for (size_t i = 0; i < slotCount; i++) { + slots[i].clear(); + } + currentSlot = 0; buttonDown = 0; buttonUp = 0; } } mAccumulator; + size_t mSlotCount; + bool mUsingSlotsProtocol; + int32_t mButtonState; - void initialize(); + void clearState(); void sync(nsecs_t when); }; diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index f5d7ae8..1ab2a3e 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -613,6 +613,10 @@ private: return false; } + virtual bool hasInputProperty(int32_t deviceId, int property) const { + return false; + } + virtual status_t mapKey(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const { Device* device = getDevice(deviceId); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index ca2adb4..d2fd04b 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -543,11 +543,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { * active */ public NetworkInfo getActiveNetworkInfo() { - enforceAccessPermission(); - if (mActiveDefaultNetwork != -1) { - return mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(); - } - return null; + return getNetworkInfo(mActiveDefaultNetwork); } public NetworkInfo getNetworkInfo(int networkType) { @@ -579,18 +575,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { * none is active */ public LinkProperties getActiveLinkProperties() { - enforceAccessPermission(); - for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) { - if (mNetConfigs[type] == null || !mNetConfigs[type].isDefault()) { - continue; - } - NetworkStateTracker t = mNetTrackers[type]; - NetworkInfo info = t.getNetworkInfo(); - if (info.isConnected()) { - return t.getLinkProperties(); - } - } - return null; + return getLinkProperties(mActiveDefaultNetwork); } public LinkProperties getLinkProperties(int networkType) { diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index ae04b7f..f78dca9 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -163,6 +163,7 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC private final ArrayList<StorageVolume> mVolumes = new ArrayList<StorageVolume>(); private StorageVolume mPrimaryVolume; private final HashMap<String, String> mVolumeStates = new HashMap<String, String>(); + private final HashMap<String, StorageVolume> mVolumeMap = new HashMap<String, StorageVolume>(); private String mExternalStoragePath; private PackageManagerService mPms; private boolean mUmsEnabling; @@ -672,8 +673,6 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC * Callback from NativeDaemonConnector */ public boolean onEvent(int code, String raw, String[] cooked) { - Intent in = null; - if (DEBUG_EVENTS) { StringBuilder builder = new StringBuilder(); builder.append("onEvent::"); @@ -708,6 +707,7 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>) // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>) // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>) + String action = null; final String label = cooked[2]; final String path = cooked[3]; int major = -1; @@ -746,32 +746,31 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC /* Send the media unmounted event first */ if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first"); updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); - in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); - mContext.sendBroadcast(in); + sendStorageIntent(Environment.MEDIA_UNMOUNTED, path); if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed"); updatePublicVolumeState(path, Environment.MEDIA_REMOVED); - in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path)); + action = Intent.ACTION_MEDIA_REMOVED; } else if (code == VoldResponseCode.VolumeBadRemoval) { if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first"); /* Send the media unmounted event first */ updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); - in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); - mContext.sendBroadcast(in); + action = Intent.ACTION_MEDIA_UNMOUNTED; if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal"); updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL); - in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path)); + action = Intent.ACTION_MEDIA_BAD_REMOVAL; } else { Slog.e(TAG, String.format("Unknown code {%d}", code)); } + + if (action != null) { + sendStorageIntent(action, path); + } } else { return false; } - if (in != null) { - mContext.sendBroadcast(in); - } return true; } @@ -779,12 +778,11 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC String vs = getVolumeState(path); if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs); - Intent in = null; + String action = null; if (oldState == VolumeState.Shared && newState != oldState) { if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent"); - mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED, - Uri.parse("file://" + path))); + sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, path); } if (newState == VolumeState.Init) { @@ -801,31 +799,29 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) { if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable"); updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); - in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); + action = Intent.ACTION_MEDIA_UNMOUNTED; } } else if (newState == VolumeState.Pending) { } else if (newState == VolumeState.Checking) { if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking"); updatePublicVolumeState(path, Environment.MEDIA_CHECKING); - in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path)); + action = Intent.ACTION_MEDIA_CHECKING; } else if (newState == VolumeState.Mounted) { if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted"); updatePublicVolumeState(path, Environment.MEDIA_MOUNTED); - in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path)); - in.putExtra("read-only", false); + action = Intent.ACTION_MEDIA_MOUNTED; } else if (newState == VolumeState.Unmounting) { - in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path)); + action = Intent.ACTION_MEDIA_EJECT; } else if (newState == VolumeState.Formatting) { } else if (newState == VolumeState.Shared) { if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted"); /* Send the media unmounted event first */ updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); - in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); - mContext.sendBroadcast(in); + sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, path); if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared"); updatePublicVolumeState(path, Environment.MEDIA_SHARED); - in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path)); + action = Intent.ACTION_MEDIA_SHARED; if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent"); } else if (newState == VolumeState.SharedMnt) { Slog.e(TAG, "Live shared mounts not supported yet!"); @@ -834,8 +830,8 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC Slog.e(TAG, "Unhandled VolumeState {" + newState + "}"); } - if (in != null) { - mContext.sendBroadcast(in); + if (action != null) { + sendStorageIntent(action, path); } } @@ -885,7 +881,7 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC /* * Mount failed for some reason */ - Intent in = null; + String action = null; int code = e.getCode(); if (code == VoldResponseCode.OpFailedNoMedia) { /* @@ -898,7 +894,7 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC * Media is blank or does not contain a supported filesystem */ updatePublicVolumeState(path, Environment.MEDIA_NOFS); - in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path)); + action = Intent.ACTION_MEDIA_NOFS; rc = StorageResultCode.OperationFailedMediaBlank; } else if (code == VoldResponseCode.OpFailedMediaCorrupt) { if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt"); @@ -906,7 +902,7 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC * Volume consistency check failed */ updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE); - in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path)); + action = Intent.ACTION_MEDIA_UNMOUNTABLE; rc = StorageResultCode.OperationFailedMediaCorrupt; } else { rc = StorageResultCode.OperationFailedInternalError; @@ -915,8 +911,8 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC /* * Send broadcast intent (if required for the failure) */ - if (in != null) { - mContext.sendBroadcast(in); + if (action != null) { + sendStorageIntent(action, path); } } @@ -1073,6 +1069,14 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC } } + private void sendStorageIntent(String action, String path) { + Intent intent = new Intent(action, Uri.parse("file://" + path)); + // add StorageVolume extra + intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolumeMap.get(path)); + Slog.d(TAG, "sendStorageIntent " + intent); + mContext.sendBroadcast(intent); + } + private void sendUmsIntent(boolean c) { mContext.sendBroadcast( new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED))); @@ -1124,7 +1128,8 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC if (path == null || description == null) { Slog.e(TAG, "path or description is null in readStorageList"); } else { - StorageVolume volume = new StorageVolume(path.toString(), + String pathString = path.toString(); + StorageVolume volume = new StorageVolume(pathString, description.toString(), removable, emulated, mtpReserve); if (primary) { if (mPrimaryVolume == null) { @@ -1139,6 +1144,7 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC } else { mVolumes.add(volume); } + mVolumeMap.put(pathString, volume); } a.recycle(); } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 7c613c1..8f179f5 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -16,51 +16,34 @@ package com.android.server; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Resources; import android.content.pm.PackageManager; -import android.net.NetworkStats; -import android.net.Uri; -import android.net.InterfaceConfiguration; import android.net.INetworkManagementEventObserver; +import android.net.InterfaceConfiguration; import android.net.LinkAddress; +import android.net.NetworkStats; import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; +import android.os.Binder; import android.os.INetworkManagementService; -import android.os.Handler; -import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; -import android.text.TextUtils; import android.util.Log; import android.util.Slog; -import java.util.ArrayList; -import java.util.NoSuchElementException; -import java.util.StringTokenizer; -import android.provider.Settings; -import android.content.ContentResolver; -import android.database.ContentObserver; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileReader; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; -import java.io.RandomAccessFile; -import java.io.Reader; -import java.lang.IllegalStateException; -import java.net.InetAddress; import java.net.Inet4Address; -import java.net.UnknownHostException; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; import java.util.concurrent.CountDownLatch; import libcore.io.IoUtils; @@ -69,14 +52,16 @@ import libcore.io.IoUtils; * @hide */ class NetworkManagementService extends INetworkManagementService.Stub { - - private static final String TAG = "NetworkManagmentService"; + private static final String TAG = "NetworkManagementService"; private static final boolean DBG = false; private static final String NETD_TAG = "NetdConnector"; private static final int ADD = 1; private static final int REMOVE = 2; + /** Base path to UID-granularity network statistics. */ + private static final File PATH_PROC_UID_STAT = new File("/proc/uid_stat"); + class NetdResponseCode { public static final int InterfaceListResult = 110; public static final int TetherInterfaceListResult = 111; @@ -891,7 +876,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { return -1; } - /** {@inheritDoc} */ + @Override public NetworkStats getNetworkStatsSummary() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); @@ -909,31 +894,46 @@ class NetworkManagementService extends INetworkManagementService.Stub { return stats.build(); } - /** {@inheritDoc} */ + @Override public NetworkStats getNetworkStatsDetail() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - final File procPath = new File("/proc/uid_stat"); - final String[] knownUids = procPath.list(); + final String[] knownUids = PATH_PROC_UID_STAT.list(); final NetworkStats.Builder stats = new NetworkStats.Builder( SystemClock.elapsedRealtime(), knownUids.length); - // TODO: kernel module will provide interface-level stats in future - // TODO: migrate these stats to come across netd in bulk, instead of all - // these individual file reads. for (String uid : knownUids) { - final File uidPath = new File(procPath, uid); - final int rx = readSingleIntFromFile(new File(uidPath, "tcp_rcv")); - final int tx = readSingleIntFromFile(new File(uidPath, "tcp_snd")); - final int uidInt = Integer.parseInt(uid); - stats.addEntry(NetworkStats.IFACE_ALL, uidInt, rx, tx); + collectNetworkStatsDetail(stats, uidInt); } return stats.build(); } + @Override + public NetworkStats getNetworkStatsUidDetail(int uid) { + if (Binder.getCallingUid() != uid) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + } + + final NetworkStats.Builder stats = new NetworkStats.Builder( + SystemClock.elapsedRealtime(), 1); + collectNetworkStatsDetail(stats, uid); + return stats.build(); + } + + private void collectNetworkStatsDetail(NetworkStats.Builder stats, int uid) { + // TODO: kernel module will provide interface-level stats in future + // TODO: migrate these stats to come across netd in bulk, instead of all + // these individual file reads. + final File uidPath = new File(PATH_PROC_UID_STAT, Integer.toString(uid)); + final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); + final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd")); + stats.addEntry(NetworkStats.IFACE_ALL, uid, rx, tx); + } + public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); @@ -994,22 +994,17 @@ class NetworkManagementService extends INetworkManagementService.Stub { } /** - * Utility method to read a single plain-text {@link Integer} from the given + * Utility method to read a single plain-text {@link Long} from the given * {@link File}, usually from a {@code /proc/} filesystem. */ - private static int readSingleIntFromFile(File file) { - RandomAccessFile f = null; + private static long readSingleLongFromFile(File file) { try { - f = new RandomAccessFile(file, "r"); - byte[] buffer = new byte[(int) f.length()]; - f.readFully(buffer); - return Integer.parseInt(new String(buffer).trim()); + final byte[] buffer = IoUtils.readFileAsByteArray(file.toString()); + return Long.parseLong(new String(buffer).trim()); } catch (NumberFormatException e) { return -1; } catch (IOException e) { return -1; - } finally { - IoUtils.closeQuietly(f); } } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index cd8915d..5355d44 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -18,6 +18,7 @@ package com.android.server; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.am.ActivityManagerService; +import com.android.server.net.NetworkPolicyManagerService; import com.android.server.pm.PackageManagerService; import com.android.server.usb.UsbService; import com.android.server.wm.WindowManagerService; @@ -119,6 +120,7 @@ class ServerThread extends Thread { LightsService lights = null; PowerManagerService power = null; BatteryService battery = null; + NetworkPolicyManagerService networkPolicy = null; ConnectivityService connectivity = null; IPackageManager pm = null; Context context = null; @@ -282,6 +284,15 @@ class ServerThread extends Thread { } try { + Slog.i(TAG, "NetworkPolicy Service"); + networkPolicy = new NetworkPolicyManagerService( + context, ActivityManagerService.self(), power); + ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting Connectivity Service", e); + } + + try { Slog.i(TAG, "NetworkManagement Service"); ServiceManager.addService( Context.NETWORKMANAGEMENT_SERVICE, @@ -528,6 +539,7 @@ class ServerThread extends Thread { // These are needed to propagate to the runnable below. final Context contextF = context; final BatteryService batteryF = battery; + final NetworkPolicyManagerService networkPolicyF = networkPolicy; final ConnectivityService connectivityF = connectivity; final DockObserver dockF = dock; final UsbService usbF = usb; @@ -553,6 +565,7 @@ class ServerThread extends Thread { startSystemUi(contextF); if (batteryF != null) batteryF.systemReady(); + if (networkPolicyF != null) networkPolicyF.systemReady(); if (connectivityF != null) connectivityF.systemReady(); if (dockF != null) dockF.systemReady(); if (usbF != null) usbF.systemReady(); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 5aae539..cf5592c 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -43,6 +43,7 @@ import android.app.IActivityWatcher; import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; import android.app.INotificationManager; +import android.app.IProcessObserver; import android.app.IServiceConnection; import android.app.IThumbnailReceiver; import android.app.IThumbnailRetriever; @@ -752,8 +753,6 @@ public final class ActivityManagerService extends ActivityManagerNative */ final UsageStatsService mUsageStatsService; - final NetworkPolicyManagerService mNetworkPolicyService; - /** * Current configuration information. HistoryRecord objects are given * a reference to this object to indicate which configuration they are @@ -854,14 +853,6 @@ public final class ActivityManagerService extends ActivityManagerNative * Current sequence id for process LRU updating. */ int mLruSeq = 0; - - /** - * Set to true if the ANDROID_SIMPLE_PROCESS_MANAGEMENT envvar - * is set, indicating the user wants processes started in such a way - * that they can use ANDROID_PROCESS_WRAPPER and know what will be - * running in each process (thus no pre-initialized process, etc). - */ - boolean mSimpleProcessManagement = false; /** * System monitoring: number of processes that died since the last @@ -885,7 +876,10 @@ public final class ActivityManagerService extends ActivityManagerNative final RemoteCallbackList<IActivityWatcher> mWatchers = new RemoteCallbackList<IActivityWatcher>(); - + + final RemoteCallbackList<IProcessObserver> mProcessObservers + = new RemoteCallbackList<IProcessObserver>(); + /** * Callback of last caller to {@link #requestPss}. */ @@ -1277,16 +1271,15 @@ public final class ActivityManagerService extends ActivityManagerNative } } break; case DISPATCH_FOREGROUND_ACTIVITIES_CHANGED: { - // Flag might have changed during dispatch, but it's always - // consistent since we dispatch for every change. final ProcessRecord app = (ProcessRecord) msg.obj; - mNetworkPolicyService.onForegroundActivitiesChanged( - app.info.uid, app.pid, app.foregroundActivities); + final boolean foregroundActivities = msg.arg1 != 0; + dispatchForegroundActivitiesChanged( + app.pid, app.info.uid, foregroundActivities); break; } case DISPATCH_PROCESS_DIED: { final ProcessRecord app = (ProcessRecord) msg.obj; - mNetworkPolicyService.onProcessDied(app.info.uid, app.pid); + dispatchProcessDied(app.pid, app.info.uid); break; } } @@ -1358,7 +1351,6 @@ public final class ActivityManagerService extends ActivityManagerNative m.mBatteryStatsService.publish(context); m.mUsageStatsService.publish(context); - m.mNetworkPolicyService.publish(context); synchronized (thr) { thr.mReady = true; @@ -1455,15 +1447,6 @@ public final class ActivityManagerService extends ActivityManagerNative } private ActivityManagerService() { - String v = System.getenv("ANDROID_SIMPLE_PROCESS_MANAGEMENT"); - if (v != null && Integer.getInteger(v) != 0) { - mSimpleProcessManagement = true; - } - v = System.getenv("ANDROID_DEBUG_APP"); - if (v != null) { - mSimpleProcessManagement = true; - } - Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass()); File dataDir = Environment.getDataDirectory(); @@ -1480,8 +1463,6 @@ public final class ActivityManagerService extends ActivityManagerNative mUsageStatsService = new UsageStatsService(new File( systemDir, "usagestats").toString()); - mNetworkPolicyService = new NetworkPolicyManagerService(); - GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", ConfigurationInfo.GL_ES_VERSION_UNDEFINED); @@ -1937,8 +1918,7 @@ public final class ActivityManagerService extends ActivityManagerNative debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; } int pid = Process.start("android.app.ActivityThread", - mSimpleProcessManagement ? app.processName : null, uid, uid, - gids, debugFlags, null); + app.processName, uid, uid, gids, debugFlags, null); BatteryStatsImpl bs = app.batteryStats.getBatteryStats(); synchronized (bs) { if (bs.isOnBattery()) { @@ -2152,6 +2132,36 @@ public final class ActivityManagerService extends ActivityManagerNative mWatchers.finishBroadcast(); } + private void dispatchForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) { + int i = mProcessObservers.beginBroadcast(); + while (i > 0) { + i--; + final IProcessObserver observer = mProcessObservers.getBroadcastItem(i); + if (observer != null) { + try { + observer.onForegroundActivitiesChanged(pid, uid, foregroundActivities); + } catch (RemoteException e) { + } + } + } + mProcessObservers.finishBroadcast(); + } + + private void dispatchProcessDied(int pid, int uid) { + int i = mProcessObservers.beginBroadcast(); + while (i > 0) { + i--; + final IProcessObserver observer = mProcessObservers.getBroadcastItem(i); + if (observer != null) { + try { + observer.onProcessDied(pid, uid); + } catch (RemoteException e) { + } + } + } + mProcessObservers.finishBroadcast(); + } + final void doPendingActivityLaunchesLocked(boolean doResume) { final int N = mPendingActivityLaunches.size(); if (N <= 0) { @@ -6084,7 +6094,6 @@ public final class ActivityManagerService extends ActivityManagerNative mUsageStatsService.shutdown(); mBatteryStatsService.shutdown(); - mNetworkPolicyService.shutdown(); return timedout; } @@ -6241,6 +6250,14 @@ public final class ActivityManagerService extends ActivityManagerNative } } + public void registerProcessObserver(IProcessObserver observer) { + mProcessObservers.register(observer); + } + + public void unregisterProcessObserver(IProcessObserver observer) { + mProcessObservers.unregister(observer); + } + public void setImmersive(IBinder token, boolean immersive) { synchronized(this) { int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1; @@ -12755,7 +12772,8 @@ public final class ActivityManagerService extends ActivityManagerNative app.curSchedGroup = schedGroup; if (hadForegroundActivities != app.foregroundActivities) { - mHandler.obtainMessage(DISPATCH_FOREGROUND_ACTIVITIES_CHANGED, app).sendToTarget(); + mHandler.obtainMessage(DISPATCH_FOREGROUND_ACTIVITIES_CHANGED, + app.foregroundActivities ? 1 : 0, 0, app).sendToTarget(); } return adj; diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index b1ab05b..67e73f5 100755 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -277,8 +277,8 @@ public class GpsLocationProvider implements LocationProviderInterface { private final SparseIntArray mClientUids = new SparseIntArray(); // how often to request NTP time, in milliseconds - // current setting 4 hours - private static final long NTP_INTERVAL = 4*60*60*1000; + // current setting 24 hours + private static final long NTP_INTERVAL = 24*60*60*1000; // how long to wait if we have a network error in NTP or XTRA downloading // current setting - 5 minutes private static final long RETRY_INTERVAL = 5*60*1000; diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index a7a4f07..d083d01 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -19,13 +19,20 @@ package com.android.server.net; import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.UPDATE_DEVICE_STATS; import static android.net.NetworkPolicyManager.POLICY_NONE; -import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID; import static android.net.NetworkPolicyManager.POLICY_REJECT_BACKGROUND; +import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID; +import android.app.IActivityManager; +import android.app.IProcessObserver; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.net.INetworkPolicyManager; -import android.os.ServiceManager; +import android.os.IPowerManager; +import android.os.RemoteException; import android.util.Log; +import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; @@ -38,92 +45,139 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final String TAG = "NetworkPolicy"; private static final boolean LOGD = true; - private static final String SERVICE_NAME = "netpolicy"; - private Context mContext; + private IActivityManager mActivityManager; + private IPowerManager mPowerManager; + + private Object mRulesLock = new Object(); + + private boolean mScreenOn = false; /** Current network policy for each UID. */ - private SparseIntArray mUidPolicy; + private SparseIntArray mUidPolicy = new SparseIntArray(); /** Foreground at both UID and PID granularity. */ - private SparseBooleanArray mUidForeground; - private SparseArray<SparseBooleanArray> mUidPidForeground; + private SparseBooleanArray mUidForeground = new SparseBooleanArray(); + private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray< + SparseBooleanArray>(); // TODO: periodically poll network stats and write to disk // TODO: save/restore policy information from disk - // TODO: watch screen on/off broadcasts to track foreground + public NetworkPolicyManagerService( + Context context, IActivityManager activityManager, IPowerManager powerManager) { + mContext = checkNotNull(context, "missing context"); + mActivityManager = checkNotNull(activityManager, "missing activityManager"); + mPowerManager = checkNotNull(powerManager, "missing powerManager"); + } - public void publish(Context context) { - mContext = context; - ServiceManager.addService(SERVICE_NAME, asBinder()); + public void systemReady() { + // TODO: read current policy+stats from disk and generate NMS rules - mUidPolicy = new SparseIntArray(); - mUidForeground = new SparseBooleanArray(); - mUidPidForeground = new SparseArray<SparseBooleanArray>(); + updateScreenOn(); - // TODO: register for NetworkManagementService callbacks - // TODO: read current policy+stats from disk and generate NMS rules - } + try { + mActivityManager.registerProcessObserver(mProcessObserver); + } catch (RemoteException e) { + // ouch, no foregroundActivities updates means some processes may + // never get network access. + Slog.e(TAG, "unable to register IProcessObserver", e); + } - public void shutdown() { - // TODO: persist any pending stats during clean shutdown + // TODO: traverse existing processes to know foreground state, or have + // activitymanager dispatch current state when new observer attached. - mUidPolicy = null; - mUidForeground = null; - mUidPidForeground = null; - } + final IntentFilter screenFilter = new IntentFilter(); + screenFilter.addAction(Intent.ACTION_SCREEN_ON); + screenFilter.addAction(Intent.ACTION_SCREEN_OFF); + mContext.registerReceiver(mScreenReceiver, screenFilter); - @Override - public void onForegroundActivitiesChanged(int uid, int pid, boolean foreground) { - // only someone like AMS should only be calling us - mContext.enforceCallingOrSelfPermission( - MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission"); + final IntentFilter shutdownFilter = new IntentFilter(); + shutdownFilter.addAction(Intent.ACTION_SHUTDOWN); + mContext.registerReceiver(mShutdownReceiver, shutdownFilter); - // because a uid can have multiple pids running inside, we need to - // remember all pid states and summarize foreground at uid level. + } - // record foreground for this specific pid - SparseBooleanArray pidForeground = mUidPidForeground.get(uid); - if (pidForeground == null) { - pidForeground = new SparseBooleanArray(2); - mUidPidForeground.put(uid, pidForeground); + private IProcessObserver mProcessObserver = new IProcessObserver.Stub() { + @Override + public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) { + // only someone like AMS should only be calling us + mContext.enforceCallingOrSelfPermission( + MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission"); + + synchronized (mRulesLock) { + // because a uid can have multiple pids running inside, we need to + // remember all pid states and summarize foreground at uid level. + + // record foreground for this specific pid + SparseBooleanArray pidForeground = mUidPidForeground.get(uid); + if (pidForeground == null) { + pidForeground = new SparseBooleanArray(2); + mUidPidForeground.put(uid, pidForeground); + } + pidForeground.put(pid, foregroundActivities); + computeUidForegroundL(uid); + } } - pidForeground.put(pid, foreground); - computeUidForeground(uid); - } - @Override - public void onProcessDied(int uid, int pid) { - // only someone like AMS should only be calling us - mContext.enforceCallingOrSelfPermission( - MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission"); + @Override + public void onProcessDied(int pid, int uid) { + // only someone like AMS should only be calling us + mContext.enforceCallingOrSelfPermission( + MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission"); + + synchronized (mRulesLock) { + // clear records and recompute, when they exist + final SparseBooleanArray pidForeground = mUidPidForeground.get(uid); + if (pidForeground != null) { + pidForeground.delete(pid); + computeUidForegroundL(uid); + } + } + } + }; + + private BroadcastReceiver mScreenReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mRulesLock) { + // screen-related broadcasts are protected by system, no need + // for permissions check. + updateScreenOn(); + } + } + }; - // clear records and recompute, when they exist - final SparseBooleanArray pidForeground = mUidPidForeground.get(uid); - if (pidForeground != null) { - pidForeground.delete(pid); - computeUidForeground(uid); + private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // TODO: persist any pending stats during clean shutdown + Log.d(TAG, "persisting stats"); } - } + }; @Override public void setUidPolicy(int uid, int policy) { mContext.enforceCallingOrSelfPermission( UPDATE_DEVICE_STATS, "requires UPDATE_DEVICE_STATS permission"); - mUidPolicy.put(uid, policy); + + synchronized (mRulesLock) { + mUidPolicy.put(uid, policy); + } } @Override public int getUidPolicy(int uid) { - return mUidPolicy.get(uid, POLICY_NONE); + synchronized (mRulesLock) { + return mUidPolicy.get(uid, POLICY_NONE); + } } /** * Foreground for PID changed; recompute foreground at UID level. If - * changed, will trigger {@link #updateRulesForUid(int)}. + * changed, will trigger {@link #updateRulesForUidL(int)}. */ - private void computeUidForeground(int uid) { + private void computeUidForegroundL(int uid) { final SparseBooleanArray pidForeground = mUidPidForeground.get(uid); // current pid is dropping foreground; examine other pids @@ -140,12 +194,37 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (oldUidForeground != uidForeground) { // foreground changed, push updated rules mUidForeground.put(uid, uidForeground); - updateRulesForUid(uid); + updateRulesForUidL(uid); + } + } + + private void updateScreenOn() { + synchronized (mRulesLock) { + try { + mScreenOn = mPowerManager.isScreenOn(); + } catch (RemoteException e) { + } + updateRulesForScreenL(); + } + } + + /** + * Update rules that might be changed by {@link #mScreenOn} value. + */ + private void updateRulesForScreenL() { + // only update rules for anyone with foreground activities + final int size = mUidForeground.size(); + for (int i = 0; i < size; i++) { + if (mUidForeground.valueAt(i)) { + final int uid = mUidForeground.keyAt(i); + updateRulesForUidL(uid); + } } } - private void updateRulesForUid(int uid) { - final boolean uidForeground = mUidForeground.get(uid, false); + private void updateRulesForUidL(int uid) { + // only really in foreground when screen on + final boolean uidForeground = mUidForeground.get(uid, false) && mScreenOn; final int uidPolicy = getUidPolicy(uid); if (LOGD) { @@ -162,4 +241,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private static <T> T checkNotNull(T value, String message) { + if (value == null) { + throw new NullPointerException(message); + } + return value; + } } diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java index f20d5e5..ca33d32 100644 --- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java @@ -42,6 +42,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.provider.Settings; import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.Suppress; import android.text.format.DateUtils; import android.util.Log; @@ -54,6 +55,7 @@ import java.util.concurrent.Future; /** * Tests for {@link ThrottleService}. */ +@LargeTest public class ThrottleServiceTest extends AndroidTestCase { private static final String TAG = "ThrottleServiceTest"; diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java index 9fc4667..0c4581b 100644 --- a/telephony/java/com/android/internal/telephony/BaseCommands.java +++ b/telephony/java/com/android/internal/telephony/BaseCommands.java @@ -25,6 +25,11 @@ import android.os.AsyncResult; import android.os.SystemProperties; import android.util.Log; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * {@hide} */ @@ -794,6 +799,34 @@ public abstract class BaseCommands implements CommandsInterface { } /** + * The contents of the /proc/cmdline file + */ + private static String getProcCmdLine() + { + String cmdline = ""; + FileInputStream is = null; + try { + is = new FileInputStream("/proc/cmdline"); + byte [] buffer = new byte[2048]; + int count = is.read(buffer); + if (count > 0) { + cmdline = new String(buffer, 0, count); + } + } catch (IOException e) { + Log.d(LOG_TAG, "No /proc/cmdline exception=" + e); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + } + } + } + Log.d(LOG_TAG, "/proc/cmdline=" + cmdline); + return cmdline; + } + + /** * {@inheritDoc} */ @Override @@ -801,6 +834,17 @@ public abstract class BaseCommands implements CommandsInterface { return getLteOnCdmaModeStatic(); } + /** Kernel command line */ + private static final String sKernelCmdLine = getProcCmdLine(); + + /** Pattern for selecting the product type from the kernel command line */ + private static final Pattern sProductTypePattern = + Pattern.compile("\\sproduct_type\\s*=\\s*(\\w+)"); + + /** The ProductType used for LTE on CDMA devices */ + private static final String sLteOnCdmaProductType = + SystemProperties.get(TelephonyProperties.PROPERTY_LTE_ON_CDMA_PRODUCT_TYPE, ""); + /** * Return if the current radio is LTE on CDMA. This * is a tri-state return value as for a period of time @@ -810,9 +854,24 @@ public abstract class BaseCommands implements CommandsInterface { * or {@link Phone#LTE_ON_CDMA_TRUE} */ public static int getLteOnCdmaModeStatic() { - int retVal = SystemProperties.getInt(TelephonyProperties.PROPERTY_NETWORK_LTE_ON_CDMA, - Phone.LTE_ON_CDMA_FALSE); - Log.d(LOG_TAG, "getLteOnCdmaMode=" + retVal); + int retVal; + String productType; + + Matcher matcher = sProductTypePattern.matcher(sKernelCmdLine); + if (matcher.find()) { + productType = matcher.group(1); + if (sLteOnCdmaProductType.equals(productType)) { + retVal = Phone.LTE_ON_CDMA_TRUE; + } else { + retVal = Phone.LTE_ON_CDMA_FALSE; + } + } else { + retVal = Phone.LTE_ON_CDMA_FALSE; + productType = ""; + } + + Log.d(LOG_TAG, "getLteOnCdmaMode=" + retVal + " product_type='" + productType + + "' lteOnCdmaProductType='" + sLteOnCdmaProductType + "'"); return retVal; } } diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java index 2e781b2..3636baa 100644 --- a/telephony/java/com/android/internal/telephony/DataConnection.java +++ b/telephony/java/com/android/internal/telephony/DataConnection.java @@ -22,8 +22,10 @@ import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; +import android.net.LinkAddress; import android.net.LinkCapabilities; import android.net.LinkProperties; +import android.net.NetworkUtils; import android.net.ProxyProperties; import android.os.AsyncResult; import android.os.Bundle; @@ -33,6 +35,7 @@ import android.os.Parcelable; import android.os.SystemProperties; import android.text.TextUtils; +import java.util.Collection; import java.util.HashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -202,6 +205,7 @@ public abstract class DataConnection extends StateMachine { protected long lastFailTime; protected FailCause lastFailCause; protected static final String NULL_IP = "0.0.0.0"; + private int mRefCount; Object userData; //***** Abstract methods @@ -249,6 +253,8 @@ public abstract class DataConnection extends StateMachine { Message m = dp.onCompletedMsg; if (TextUtils.equals(dp.reason, Phone.REASON_RADIO_TURNED_OFF)) { discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF; + } else if (TextUtils.equals(dp.reason, Phone.REASON_PDP_RESET)) { + discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET; } } if (phone.mCM.getRadioState().isOn()) { @@ -408,49 +414,6 @@ public abstract class DataConnection extends StateMachine { return mRetryMgr.configure(configStr); } - private AtomicInteger mRefCount = new AtomicInteger(0); - - /** - * Set refCount. - * - * @param val is new refCount - */ - public void setRefCount(int val) { - mRefCount.set(val); - } - - /** - * Get refCount - * - * @return refCount - */ - public int getRefCount() { - return mRefCount.get(); - } - - /** - * @return decrement and return refCount - * - * TODO: Consider using the refCount for defining the - * life time of a connection. When this goes zero the - * DataConnection could tear itself down. - */ - public int decAndGetRefCount() { - int v = mRefCount.decrementAndGet(); - if (v < 0) { - log("BUG: decAndGetRefCount caused refCount to be < 0"); - mRefCount.set(0); - } - return v; - } - - /** - * @return increment and return refCount - */ - public int incAndGetRefCount() { - return mRefCount.incrementAndGet(); - } - /* * ************************************************************************** * End members owned by DataConnectionTracker @@ -466,6 +429,7 @@ public abstract class DataConnection extends StateMachine { createTime = -1; lastFailTime = -1; lastFailCause = FailCause.NONE; + mRefCount = 0; mLinkProperties = new LinkProperties(); mApn = null; @@ -531,8 +495,10 @@ public abstract class DataConnection extends StateMachine { return response.setLinkProperties(lp, okToUseSystemPropertyDns); } - private boolean updateLinkProperty(DataCallState newState) { - boolean changed = false; + private DataConnectionAc.LinkPropertyChangeAction updateLinkProperty( + DataCallState newState) { + DataConnectionAc.LinkPropertyChangeAction changed = + DataConnectionAc.LinkPropertyChangeAction.NONE; if (newState == null) return changed; @@ -551,9 +517,23 @@ public abstract class DataConnection extends StateMachine { if (DBG) log("old LP=" + mLinkProperties); if (DBG) log("new LP=" + newLp); + // Check consistency of link address. Currently we expect + // only one "global" address is assigned per each IP type. + Collection<LinkAddress> oLinks = mLinkProperties.getLinkAddresses(); + Collection<LinkAddress> nLinks = newLp.getLinkAddresses(); + for (LinkAddress oldLink : oLinks) { + for (LinkAddress newLink : nLinks) { + if ((NetworkUtils.addressTypeMatches(oldLink.getAddress(), + newLink.getAddress())) && + (oldLink.equals(newLink) == false)) { + return DataConnectionAc.LinkPropertyChangeAction.RESET; + } + } + } + if (mLinkProperties == null || !mLinkProperties.equals(newLp)) { mLinkProperties = newLp; - changed = true; + changed = DataConnectionAc.LinkPropertyChangeAction.CHANGED; } return changed; @@ -631,15 +611,14 @@ public abstract class DataConnection extends StateMachine { } case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: { DataCallState newState = (DataCallState) msg.obj; - int updated = updateLinkProperty(newState) ? 1 : 0; + DataConnectionAc.LinkPropertyChangeAction action = updateLinkProperty(newState); if (DBG) { - log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE updated=" - + (updated == 1) - + " newState=" + newState); + log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE action=" + + action + " newState=" + newState); } mAc.replyToMessage(msg, DataConnectionAc.RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, - updated); + action.ordinal()); break; } case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: { @@ -654,6 +633,11 @@ public abstract class DataConnection extends StateMachine { mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET); transitionTo(mInactiveState); break; + case DataConnectionAc.REQ_GET_REFCOUNT: { + log("REQ_GET_REFCOUNT refCount=" + mRefCount); + mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_REFCOUNT, mRefCount); + break; + } case EVENT_CONNECT: if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected"); @@ -754,9 +738,13 @@ public abstract class DataConnection extends StateMachine { break; case EVENT_CONNECT: - if (DBG) log("DcInactiveState msg.what=EVENT_CONNECT"); ConnectionParams cp = (ConnectionParams) msg.obj; cp.tag = mTag; + if (DBG) { + log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = " + + mRefCount); + } + mRefCount = 1; onConnect(cp); transitionTo(mActivatingState); retVal = HANDLED; @@ -784,7 +772,15 @@ public abstract class DataConnection extends StateMachine { switch (msg.what) { case EVENT_DISCONNECT: - if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT"); + if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT" + + mRefCount); + deferMessage(msg); + retVal = HANDLED; + break; + + case EVENT_CONNECT: + if (DBG) log("DcActivatingState deferring msg.what=EVENT_CONNECT refCount = " + + mRefCount); deferMessage(msg); retVal = HANDLED; break; @@ -908,12 +904,28 @@ public abstract class DataConnection extends StateMachine { boolean retVal; switch (msg.what) { + case EVENT_CONNECT: + mRefCount++; + if (DBG) log("DcActiveState msg.what=EVENT_CONNECT RefCount=" + mRefCount); + if (msg.obj != null) { + notifyConnectCompleted((ConnectionParams) msg.obj, FailCause.NONE); + } + retVal = HANDLED; + break; case EVENT_DISCONNECT: - if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT"); - DisconnectParams dp = (DisconnectParams) msg.obj; - dp.tag = mTag; - tearDownData(dp); - transitionTo(mDisconnectingState); + mRefCount--; + if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" + mRefCount); + if (mRefCount == 0) + { + DisconnectParams dp = (DisconnectParams) msg.obj; + dp.tag = mTag; + tearDownData(dp); + transitionTo(mDisconnectingState); + } else { + if (msg.obj != null) { + notifyDisconnectCompleted((DisconnectParams) msg.obj); + } + } retVal = HANDLED; break; @@ -936,6 +948,13 @@ public abstract class DataConnection extends StateMachine { boolean retVal; switch (msg.what) { + case EVENT_CONNECT: + if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = " + + mRefCount); + deferMessage(msg); + retVal = HANDLED; + break; + case EVENT_DEACTIVATE_DONE: if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE"); AsyncResult ar = (AsyncResult) msg.obj; diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java index 2ab6184..a0d9b0f 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java @@ -59,6 +59,29 @@ public class DataConnectionAc extends AsyncChannel { public static final int REQ_RESET = BASE + 14; public static final int RSP_RESET = BASE + 15; + public static final int REQ_GET_REFCOUNT = BASE + 16; + public static final int RSP_GET_REFCOUNT = BASE + 17; + + /** + * enum used to notify action taken or necessary to be + * taken after the link property is changed. + */ + public enum LinkPropertyChangeAction { + NONE, CHANGED, RESET; + + public static LinkPropertyChangeAction fromInt(int value) { + if (value == NONE.ordinal()) { + return NONE; + } else if (value == CHANGED.ordinal()) { + return CHANGED; + } else if (value == RESET.ordinal()) { + return RESET; + } else { + throw new RuntimeException("LinkPropertyChangeAction.fromInt: bad value=" + value); + } + } + } + public DataConnectionAc(DataConnection dc, String logTag) { dataConnection = dc; mLogTag = logTag; @@ -132,6 +155,40 @@ public class DataConnectionAc extends AsyncChannel { } /** + * Request the Reference Count. + * Response {@link #rspRefCount} + */ + public void reqRefCount() { + sendMessage(REQ_GET_REFCOUNT); + if (DBG) log("reqRefCount"); + } + + /** + * Evaluate a RSP_GET_REFCOUNT message and return the refCount. + * + * @param response Message + * @return ref count or -1 if an error + */ + public int rspRefCount(Message response) { + int retVal = response.arg1; + if (DBG) log("rspRefCount=" + retVal); + return retVal; + } + + /** + * @return connection id or -1 if an error + */ + public int getRefCountSync() { + Message response = sendMessageSynchronously(REQ_GET_REFCOUNT); + if ((response != null) && (response.what == RSP_GET_REFCOUNT)) { + return rspRefCount(response); + } else { + log("rspRefCount error response=" + response); + return -1; + } + } + + /** * Request the connections ApnSetting. * Response {@link #rspApnSetting} */ @@ -234,8 +291,8 @@ public class DataConnectionAc extends AsyncChannel { if (DBG) log("reqUpdateLinkPropertiesDataCallState"); } - public boolean rspUpdateLinkPropertiesDataCallState(Message response) { - boolean retVal = response.arg1 == 1; + public LinkPropertyChangeAction rspUpdateLinkPropertiesDataCallState(Message response) { + LinkPropertyChangeAction retVal = LinkPropertyChangeAction.fromInt(response.arg1); if (DBG) log("rspUpdateLinkPropertiesState=" + retVal); return retVal; } @@ -245,7 +302,7 @@ public class DataConnectionAc extends AsyncChannel { * * @return true if link property has been updated. false otherwise. */ - public boolean updateLinkPropertiesDataCallStateSync(DataCallState newState) { + public LinkPropertyChangeAction updateLinkPropertiesDataCallStateSync(DataCallState newState) { Message response = sendMessageSynchronously(REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, newState); if ((response != null) && @@ -253,7 +310,7 @@ public class DataConnectionAc extends AsyncChannel { return rspUpdateLinkPropertiesDataCallState(response); } else { log("getLinkProperties error response=" + response); - return false; + return LinkPropertyChangeAction.NONE; } } diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java index b7ac879..5ddfcd1 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java @@ -987,7 +987,7 @@ public abstract class DataConnectionTracker extends Handler { resetAllRetryCounts(); onTrySetupData(Phone.REASON_DATA_ENABLED); } else { - onCleanUpConnection(true, APN_DEFAULT_ID, Phone.REASON_DATA_DISABLED); + onCleanUpAllConnections(Phone.REASON_DATA_DISABLED); } } } diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java index 1b49d2d..a516b49 100644 --- a/telephony/java/com/android/internal/telephony/IccCard.java +++ b/telephony/java/com/android/internal/telephony/IccCard.java @@ -18,13 +18,20 @@ package com.android.internal.telephony; import static android.Manifest.permission.READ_PHONE_STATE; import android.app.ActivityManagerNative; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; +import android.content.res.Resources; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; +import android.os.Power; +import android.os.PowerManager; import android.os.Registrant; import android.os.RegistrantList; import android.util.Log; +import android.view.WindowManager; import com.android.internal.telephony.PhoneBase; import com.android.internal.telephony.CommandsInterface.RadioState; @@ -32,6 +39,8 @@ import com.android.internal.telephony.gsm.SIMRecords; import android.os.SystemProperties; +import com.android.internal.R; + /** * {@hide} */ @@ -88,6 +97,8 @@ public abstract class IccCard { private static final int EVENT_QUERY_FACILITY_FDN_DONE = 10; private static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11; private static final int EVENT_ICC_STATUS_CHANGED = 12; + private static final int EVENT_CARD_REMOVED = 13; + private static final int EVENT_CARD_ADDED = 14; /* UNKNOWN is a transient state, for example, after uesr inputs ICC pin under @@ -106,6 +117,11 @@ public abstract class IccCard { public boolean isPinLocked() { return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)); } + + public boolean iccCardExist() { + return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED) + || (this == NETWORK_LOCKED) || (this == READY)); + } } public State getState() { @@ -400,6 +416,8 @@ public abstract class IccCard { boolean transitionedIntoPinLocked; boolean transitionedIntoAbsent; boolean transitionedIntoNetworkLocked; + boolean isIccCardRemoved; + boolean isIccCardAdded; State oldState, newState; @@ -416,24 +434,36 @@ public abstract class IccCard { transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT); transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED && newState == State.NETWORK_LOCKED); + isIccCardRemoved = (oldState != null && + oldState.iccCardExist() && newState == State.ABSENT); + isIccCardAdded = (oldState == State.ABSENT && + newState != null && newState.iccCardExist()); if (transitionedIntoPinLocked) { - if(mDbg) log("Notify SIM pin or puk locked."); + if (mDbg) log("Notify SIM pin or puk locked."); mPinLockedRegistrants.notifyRegistrants(); broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED, (newState == State.PIN_REQUIRED) ? INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK); } else if (transitionedIntoAbsent) { - if(mDbg) log("Notify SIM missing."); + if (mDbg) log("Notify SIM missing."); mAbsentRegistrants.notifyRegistrants(); broadcastIccStateChangedIntent(INTENT_VALUE_ICC_ABSENT, null); } else if (transitionedIntoNetworkLocked) { - if(mDbg) log("Notify SIM network locked."); + if (mDbg) log("Notify SIM network locked."); mNetworkLockedRegistrants.notifyRegistrants(); broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED, INTENT_VALUE_LOCKED_NETWORK); } + if (isIccCardRemoved) { + mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null)); + } else if (isIccCardAdded) { + mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null)); + } + + + /* * TODO: We need to try to remove this, maybe if the RIL sends up a RIL_UNSOL_SIM_REFRESH? */ @@ -443,6 +473,48 @@ public abstract class IccCard { ((SIMRecords)mPhone.mIccRecords).onSimReady(); } } + + } + + private void onIccSwap(boolean isAdded) { + // TODO: Here we assume the device can't handle SIM hot-swap + // and has to reboot. We may want to add a property, + // e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support + // hot-swap. + DialogInterface.OnClickListener listener = null; + + + // TODO: SimRecords is not reset while SIM ABSENT (only reset while + // Radio_off_or_not_available). Have to reset in both both + // added or removed situation. + listener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + if (mDbg) log("Reboot due to SIM swap"); + PowerManager pm = (PowerManager) mPhone.getContext() + .getSystemService(Context.POWER_SERVICE); + pm.reboot("SIM is added."); + } + } + + }; + + Resources r = Resources.getSystem(); + + String title = (isAdded) ? r.getString(R.string.sim_added_title) : + r.getString(R.string.sim_removed_title); + String message = (isAdded) ? r.getString(R.string.sim_added_message) : + r.getString(R.string.sim_removed_message); + String buttonTxt = r.getString(R.string.sim_restart_button); + + AlertDialog dialog = new AlertDialog.Builder(mPhone.getContext()) + .setTitle(title) + .setMessage(message) + .setPositiveButton(buttonTxt, listener) + .create(); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + dialog.show(); } /** @@ -609,6 +681,12 @@ public abstract class IccCard { Log.d(mLogTag, "Received Event EVENT_ICC_STATUS_CHANGED"); mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE)); break; + case EVENT_CARD_REMOVED: + onIccSwap(false); + break; + case EVENT_CARD_ADDED: + onIccSwap(true); + break; default: Log.e(mLogTag, "[IccCard] Unknown Event " + msg.what); } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index f5d6c51..93fc9ce 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -113,6 +113,7 @@ public interface RILConstants { /* Deactivate data call reasons */ int DEACTIVATE_REASON_NONE = 0; int DEACTIVATE_REASON_RADIO_OFF = 1; + int DEACTIVATE_REASON_PDP_RESET = 2; /* cat include/telephony/ril.h | \ diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java index 4927006..4309309 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java +++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java @@ -72,10 +72,12 @@ public interface TelephonyProperties */ static final String PROPERTY_OPERATOR_ISO_COUNTRY = "gsm.operator.iso-country"; - /** 'true' if device supports both LTE and CDMA mode of operation. - * Availability: Set only on devices supporting LTE and CDMA. + /** + * The contents of this property is the value of the kernel command line + * product_type variable that corresponds to a product that supports LTE on CDMA. + * {@see BaseCommands#getLteOnCdmaMode()} */ - static final String PROPERTY_NETWORK_LTE_ON_CDMA = "telephony.lte_on_cdma"; + static final String PROPERTY_LTE_ON_CDMA_PRODUCT_TYPE = "telephony.lteOnCdmaProductType"; static final String CURRENT_ACTIVE_PHONE = "gsm.current.phone-type"; diff --git a/telephony/java/com/android/internal/telephony/cat/CatService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java index 33cc97e..fb53686 100644 --- a/telephony/java/com/android/internal/telephony/cat/CatService.java +++ b/telephony/java/com/android/internal/telephony/cat/CatService.java @@ -32,6 +32,7 @@ import com.android.internal.telephony.IccRecords; import java.io.ByteArrayOutputStream; +import java.util.Locale; /** * Enumeration for representing the tag value of COMPREHENSION-TLV objects. If @@ -273,8 +274,20 @@ public class CatService extends Handler implements AppInterface { sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); break; case PROVIDE_LOCAL_INFORMATION: - sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); - return; + ResponseData resp; + switch (cmdParams.cmdDet.commandQualifier) { + case CommandParamsFactory.DTTZ_SETTING: + resp = new DTTZResponseData(null); + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp); + break; + case CommandParamsFactory.LANGUAGE_SETTING: + resp = new LanguageResponseData(Locale.getDefault().getLanguage()); + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp); + break; + default: + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); + return; + } case LAUNCH_BROWSER: case SELECT_ITEM: case GET_INPUT: diff --git a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java index 12204a0..686fe46 100644 --- a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java +++ b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java @@ -53,6 +53,7 @@ class CommandParamsFactory extends Handler { static final int REFRESH_UICC_RESET = 0x04; // Command Qualifier values for PLI command + static final int DTTZ_SETTING = 0x03; static final int LANGUAGE_SETTING = 0x04; static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller, @@ -883,6 +884,10 @@ class CommandParamsFactory extends Handler { throws ResultException { CatLog.d(this, "process ProvideLocalInfo"); switch (cmdDet.commandQualifier) { + case DTTZ_SETTING: + CatLog.d(this, "PLI [DTTZ_SETTING]"); + mCmdParams = new CommandParams(cmdDet); + break; case LANGUAGE_SETTING: CatLog.d(this, "PLI [LANGUAGE_SETTING]"); mCmdParams = new CommandParams(cmdDet); diff --git a/telephony/java/com/android/internal/telephony/cat/ResponseData.java b/telephony/java/com/android/internal/telephony/cat/ResponseData.java index 95f0399..55a2b63 100644 --- a/telephony/java/com/android/internal/telephony/cat/ResponseData.java +++ b/telephony/java/com/android/internal/telephony/cat/ResponseData.java @@ -18,6 +18,8 @@ package com.android.internal.telephony.cat; import com.android.internal.telephony.EncodeException; import com.android.internal.telephony.GsmAlphabet; +import java.util.Calendar; +import com.android.internal.telephony.cat.AppInterface.CommandType; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; @@ -147,4 +149,109 @@ class GetInkeyInputResponseData extends ResponseData { } } +// For "PROVIDE LOCAL INFORMATION" command. +// See TS 31.111 section 6.4.15/ETSI TS 102 223 +// TS 31.124 section 27.22.4.15 for test spec +class LanguageResponseData extends ResponseData { + private String lang; + + public LanguageResponseData(String lang) { + super(); + this.lang = lang; + } + + @Override + public void format(ByteArrayOutputStream buf) { + if (buf == null) { + return; + } + + // Text string object + int tag = 0x80 | ComprehensionTlvTag.LANGUAGE.value(); + buf.write(tag); // tag + + byte[] data; + + if (lang != null && lang.length() > 0) { + data = GsmAlphabet.stringToGsm8BitPacked(lang); + } + else { + data = new byte[0]; + } + + buf.write(data.length); + + for (byte b : data) { + buf.write(b); + } + } +} + +// For "PROVIDE LOCAL INFORMATION" command. +// See TS 31.111 section 6.4.15/ETSI TS 102 223 +// TS 31.124 section 27.22.4.15 for test spec +class DTTZResponseData extends ResponseData { + private Calendar calendar; + + public DTTZResponseData(Calendar cal) { + super(); + calendar = cal; + } + + @Override + public void format(ByteArrayOutputStream buf) { + if (buf == null) { + return; + } + + // DTTZ object + int tag = 0x80 | CommandType.PROVIDE_LOCAL_INFORMATION.value(); + buf.write(tag); // tag + + byte[] data = new byte[8]; + byte btmp; // temp variable + + data[0] = 0x07; // Write length of DTTZ data + + if (calendar == null) { + calendar = Calendar.getInstance(); + } + // Fill year byte + btmp = (byte) (calendar.get(java.util.Calendar.YEAR) % 100); + data[1] = (byte) (btmp / 10); + data[1] += (byte) ((btmp % 10) << 4); + + // Fill month byte + btmp = (byte) (calendar.get(java.util.Calendar.MONTH) + 1); + data[2] = (byte) (btmp / 10); + data[2] += (byte) ((btmp % 10) << 4); + + // Fill day byte + btmp = (byte) (calendar.get(java.util.Calendar.DATE)); + data[3] = (byte) (btmp / 10); + data[3] += (byte) ((btmp % 10) << 4); + + // Fill hour byte + btmp = (byte) (calendar.get(java.util.Calendar.HOUR_OF_DAY)); + data[4] = (byte) (btmp / 10); + data[4] += (byte) ((btmp % 10) << 4); + + // Fill minute byte + btmp = (byte) (calendar.get(java.util.Calendar.MINUTE)); + data[5] = (byte) (btmp / 10); + data[5] += (byte) ((btmp % 10) << 4); + + // Fill second byte + btmp = (byte) (calendar.get(java.util.Calendar.SECOND)); + data[6] = (byte) (btmp / 10); + data[6] += (byte) ((btmp % 10) << 4); + + // No time zone info + data[7] = (byte) 0xFF; + + for (byte b : data) { + buf.write(b); + } + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java index 375d0d1..e3e3d78 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java @@ -521,19 +521,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { * TODO: Make this configurable? */ int nextReconnectDelay = mDataConnections.get(0).getRetryTimer(); - log("Data Connection activate failed. Scheduling next attempt for " - + (nextReconnectDelay / 1000) + "s"); - - AlarmManager am = - (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); - Intent intent = new Intent(INTENT_RECONNECT_ALARM); - intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason); - mReconnectIntent = PendingIntent.getBroadcast( - mPhone.getContext(), 0, intent, 0); - am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + nextReconnectDelay, - mReconnectIntent); - + startAlarmForReconnect(nextReconnectDelay, reason); mDataConnections.get(0).increaseRetryCount(); if (!shouldPostNotification(lastFailCauseCode)) { @@ -545,6 +533,22 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { } } + private void startAlarmForReconnect(int delay, String reason) { + + log("Data Connection activate failed. Scheduling next attempt for " + + (delay / 1000) + "s"); + + AlarmManager am = + (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); + Intent intent = new Intent(INTENT_RECONNECT_ALARM); + intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason); + mReconnectIntent = PendingIntent.getBroadcast( + mPhone.getContext(), 0, intent, 0); + am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + delay, mReconnectIntent); + + } + private void notifyNoData(FailCause lastFailCauseCode) { setState(State.FAILED); notifyDataAvailability(null); @@ -702,7 +706,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { mActiveApn = null; if (retryAfterDisconnected(reason)) { // Wait a bit before trying, so we're not tying up RIL command channel. - sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), APN_DELAY_MILLIS); + startAlarmForReconnect(APN_DELAY_MILLIS, reason); } } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 6b4054a..e1a6fef 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -127,10 +127,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (apnContext != null) { apnContext.setReason(reason); if (apnContext.getState() == State.FAILED) { - Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); - msg.arg1 = 0; // tearDown is false - msg.obj = (ApnContext)apnContext; - sendMessage(msg); + apnContext.setState(State.IDLE); } sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext)); } @@ -615,28 +612,26 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { * * @param apnContext * @param tearDown - * @return refCount + * @return none */ - private int releaseApnContext(ApnContext apnContext, boolean tearDown) { + private void releaseApnContext(ApnContext apnContext, boolean tearDown) { if (apnContext == null) { if (DBG) loge("releaseApnContext: apnContext null should not happen, ignore"); - return -1; + return; } DataConnection dc = apnContext.getDataConnection(); if (dc == null) { if (DBG) loge("releaseApnContext: apnContext dc == null should not happen, ignore"); - return -1; + return; } - int refCount = dc.decAndGetRefCount(); - if (DBG) log("releaseApnContext: dec refCount=" + refCount + " tearDown=" + tearDown); - if (tearDown && (refCount == 0)) { + if (tearDown) { if (DBG) log("releaseApnContext: tearing down"); Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext); apnContext.getDataConnection().tearDown(apnContext.getReason(), msg); } apnContext.setDataConnection(null); apnContext.setDataConnectionAc(null); - return refCount; + return; } private void setupDataOnReadyApns(String reason) { @@ -644,7 +639,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { for (ApnContext apnContext : mApnContexts.values()) { if (apnContext.isReady()) { if (apnContext.getState() == State.FAILED) { - cleanUpConnection(false, apnContext); + cleanApnContextBeforeRestart(apnContext); if (apnContext.getDataConnection() != null) { apnContext.getDataConnection().resetRetryCount(); } @@ -800,6 +795,34 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (DBG) { log("cleanUpConnection: tearDown=" + tearDown + " reason=" + apnContext.getReason()); } + if (tearDown && cleanApnContextBeforeRestart(apnContext)) { + // if the request is tearDown and ApnContext does not hold an active connection, + // we're ok to return here. + return; + } + + DataConnectionAc dcac = apnContext.getDataConnectionAc(); + if (dcac != null) { + if (tearDown) { + apnContext.setState(State.DISCONNECTING); + releaseApnContext(apnContext, tearDown); + } else { + dcac.resetSync(); + apnContext.setState(State.IDLE); + mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); + apnContext.setDataConnection(null); + apnContext.setDataConnectionAc(null); + } + } + } + + /** + * @param APNContext to clean + * @return true if ApnContext is not connected anymore. + * false if ApnContext still holds a connection. + */ + private boolean cleanApnContextBeforeRestart(ApnContext apnContext) { + if (apnContext == null) return true; // Clear the reconnect alarm, if set. if (apnContext.getReconnectIntent() != null) { @@ -811,32 +834,16 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (apnContext.getState() == State.IDLE || apnContext.getState() == State.DISCONNECTING) { if (DBG) log("cleanUpConnection: state= " + apnContext.getState()); - return; + return true; } if (apnContext.getState() == State.FAILED) { - if (DBG) log("cleanUpConnection: state is in FAILED"); apnContext.setState(State.IDLE); - return; - } - - DataConnection conn = apnContext.getDataConnection(); - if (conn != null) { - DataConnectionAc dcac = mDataConnectionAsyncChannels.get(conn.getDataConnectionId()); - apnContext.setState(State.DISCONNECTING); - if (tearDown) { - releaseApnContext(apnContext, tearDown); - } else { - if (dcac != null) { - dcac.resetSync(); - } - apnContext.setState(State.IDLE); - mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); - } + return true; } + return false; } - /** * @param types comma delimited list of APN types * @return array of APN types @@ -927,37 +934,48 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return false; } - dc = findReadyDataConnection(apn); + // First, check to see if ApnContext already has DC. + // This could happen if the retries are currently engaged. + dc = (GsmDataConnection)apnContext.getDataConnection(); if (dc == null) { - if (DBG) log("setupData: No ready GsmDataConnection found!"); - // TODO: When allocating you are mapping type to id. If more than 1 free, - // then could findFreeDataConnection get the wrong one?? - dc = findFreeDataConnection(); - } - if (dc == null) { - dc = createDataConnection(); - } + dc = (GsmDataConnection) checkForConnectionForApnContext(apnContext); - if (dc == null) { - if (DBG) log("setupData: No free GsmDataConnection found!"); - return false; - } + if (dc == null) { + dc = findReadyDataConnection(apn); + } + + if (dc == null) { + if (DBG) log("setupData: No ready GsmDataConnection found!"); + // TODO: When allocating you are mapping type to id. If more than 1 free, + // then could findFreeDataConnection get the wrong one?? + dc = findFreeDataConnection(); + } + + if (dc == null) { + dc = createDataConnection(); + } + + if (dc == null) { + if (DBG) log("setupData: No free GsmDataConnection found!"); + return false; + } - dc.setProfileId( profileId ); - dc.setActiveApnType(apnContext.getApnType()); - int refCount = dc.incAndGetRefCount(); - if (DBG) log("setupData: init dc and apnContext refCount=" + refCount); + DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId()); + dc.setProfileId( profileId ); + dc.setActiveApnType(apnContext.getApnType()); + int refCount = dcac.getRefCountSync(); + if (DBG) log("setupData: init dc and apnContext refCount=" + refCount); - // configure retry count if no other Apn is using the same connection. - if (refCount == 1) { - configureRetry(dc, apnContext.getApnType()); + // configure retry count if no other Apn is using the same connection. + if (refCount == 0) { + configureRetry(dc, apnContext.getApnType()); + } + apnContext.setDataConnectionAc(mDataConnectionAsyncChannels.get(dc.getDataConnectionId())); + apnContext.setApnSetting(apn); + apnContext.setDataConnection(dc); } - DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId()); - apnContext.setDataConnectionAc(mDataConnectionAsyncChannels.get(dc.getDataConnectionId())); - apnContext.setApnSetting(apn); - apnContext.setDataConnection(dc); Message msg = obtainMessage(); msg.what = EVENT_DATA_SETUP_COMPLETE; @@ -1053,25 +1071,37 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (DBG) log("onDataStateChanged(ar): Found ConnId=" + connectionId + " newState=" + newState.toString()); if (newState.active != 0) { - boolean changed - = dcac.updateLinkPropertiesDataCallStateSync(newState); - if (changed) { + boolean resetConnection; + switch (dcac.updateLinkPropertiesDataCallStateSync(newState)) { + case NONE: + if (DBG) log("onDataStateChanged(ar): Found but no change, skip"); + resetConnection = false; + break; + case CHANGED: if (DBG) log("onDataStateChanged(ar): Found and changed, notify"); mPhone.notifyDataConnection(Phone.REASON_LINK_PROPERTIES_CHANGED, - apnContext.getApnType()); - // Temporary hack, if false we'll reset connections and at this - // time a transition from CDMA -> Global fails. The DEACTIVATE - // fails with a GENERIC_FAILURE and the VZWINTERNET connection is - // never setup. @see bug/ + apnContext.getApnType()); + // Temporary hack, at this time a transition from CDMA -> Global + // fails so we'll hope for the best and not reset the connection. + // @see bug/4455071 if (SystemProperties.getBoolean("telephony.ignore-state-changes", - true)) { + true)) { log("onDataStateChanged(ar): STOPSHIP don't reset, continue"); - continue; + resetConnection = false; + } else { + // Things changed so reset connection, when hack is removed + // this is the normal path. + log("onDataStateChanged(ar): changed so resetting connection"); + resetConnection = true; } - } else { - if (DBG) log("onDataStateChanged(ar): Found but no change, skip"); - continue; + break; + case RESET: + default: + if (DBG) log("onDataStateChanged(ar): an error, reset connection"); + resetConnection = true; + break; } + if (resetConnection == false) continue; } } @@ -1214,10 +1244,30 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { preTxPkts = mTxPkts; preRxPkts = mRxPkts; - mTxPkts = TrafficStats.getMobileTxPackets(); - mRxPkts = TrafficStats.getMobileRxPackets(); + long txSum = 0, rxSum = 0; + for (ApnContext apnContext : mApnContexts.values()) { + if (apnContext.getState() == State.CONNECTED) { + DataConnectionAc dcac = apnContext.getDataConnectionAc(); + if (dcac == null) continue; - //log("rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts)); + LinkProperties linkProp = dcac.getLinkPropertiesSync(); + if (linkProp == null) continue; + + String iface = linkProp.getInterfaceName(); + + if (iface != null) { + long stats = TrafficStats.getTxPackets(iface); + if (stats > 0) txSum += stats; + stats = TrafficStats.getRxPackets(iface); + if (stats > 0) rxSum += stats; + } + } + } + + mTxPkts = txSum; + mRxPkts = rxSum; + + // log("tx " + mTxPkts + " rx " + mRxPkts); if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) { sent = mTxPkts - preTxPkts; @@ -1333,7 +1383,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { loge("reconnectAfterFail: apnContext == null, impossible"); return; } - if (apnContext.getState() == State.FAILED) { + if ((apnContext.getState() == State.FAILED) && + (apnContext.getDataConnection() != null)) { if (!apnContext.getDataConnection().isRetryNeeded()) { if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) { mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType()); @@ -1353,23 +1404,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } int nextReconnectDelay = apnContext.getDataConnection().getRetryTimer(); - if (DBG) { - log("reconnectAfterFail: activate failed. Scheduling next attempt for " - + (nextReconnectDelay / 1000) + "s"); - } - - AlarmManager am = - (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); - Intent intent = new Intent(INTENT_RECONNECT_ALARM); - intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason()); - // Should put an extra of apn type? - intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnContext.getApnType()); - apnContext.setReconnectIntent(PendingIntent.getBroadcast ( - mPhone.getContext(), 0, intent, 0)); - am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + nextReconnectDelay, - apnContext.getReconnectIntent()); - + startAlarmForReconnect(nextReconnectDelay, apnContext); apnContext.getDataConnection().increaseRetryCount(); if (!shouldPostNotification(lastFailCauseCode)) { @@ -1383,6 +1418,25 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } } + private void startAlarmForReconnect(int delay, ApnContext apnContext) { + + if (DBG) { + log("Schedule alarm for reconnect: activate failed. Scheduling next attempt for " + + (delay / 1000) + "s"); + } + + AlarmManager am = + (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); + Intent intent = new Intent(INTENT_RECONNECT_ALARM); + intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason()); + intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnContext.getApnType()); + apnContext.setReconnectIntent(PendingIntent.getBroadcast ( + mPhone.getContext(), 0, intent, 0)); + am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + delay, apnContext.getReconnectIntent()); + + } + private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode, ApnContext apnContext) { if (DBG) log( "notifyNoData: type=" + apnContext.getApnType()); @@ -1437,23 +1491,10 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } else { apnContext.setReason(Phone.REASON_DATA_ENABLED); } - DataConnection conn = checkForConnectionForApnContext(apnContext); - if (conn == null) { - if (apnContext.getState() == State.FAILED) { - apnContext.setState(State.IDLE); - } - trySetup = true; - } else { - int refCount = conn.incAndGetRefCount(); - apnContext.setDataConnection(conn); - apnContext.setDataConnectionAc( - mDataConnectionAsyncChannels.get(conn.getDataConnectionId())); - if (DBG) { - log("applyNewState: Found existing connection for " + - apnContext.getApnType() + " inc refCount=" + refCount + - " conn=" + conn); - } + if (apnContext.getState() == State.FAILED) { + apnContext.setState(State.IDLE); } + trySetup = true; } } apnContext.setEnabled(enabled); @@ -1588,7 +1629,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (DBG) { log(String.format("onDataSetupComplete: success apn=%s", - apnContext.getWaitingApns().get(0).apn) + " refCount=" + dc.getRefCount()); + apnContext.getWaitingApns().get(0).apn)); } ApnSetting apn = apnContext.getApnSetting(); if (apn.proxy != null && apn.proxy.length() != 0) { @@ -1655,6 +1696,11 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } apnContext.setState(State.FAILED); mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType()); + + releaseApnContext(apnContext, false); + if (DBG) { + log("onDataSetupComplete: permanent error apn=%s" + apnString ); + } } else { if (DBG) log("onDataSetupComplete: Not all permanent failures, retry"); startDelayedRetry(cause, apnContext); @@ -1664,13 +1710,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { apnContext.setState(State.SCANNING); // Wait a bit before trying the next APN, so that // we're not tying up the RIL command channel - sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext), - APN_DELAY_MILLIS); - } - - int refCount = releaseApnContext(apnContext, false); - if (DBG) { - log("onDataSetupComplete: error apn=%s" + apnString + " refCount=" + refCount); + startAlarmForReconnect(APN_DELAY_MILLIS, apnContext); } } } @@ -1710,7 +1750,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // Wait a bit before trying the next APN, so that // we're not tying up the RIL command channel. // This also helps in any external dependency to turn off the context. - sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext),APN_DELAY_MILLIS); + startAlarmForReconnect(APN_DELAY_MILLIS, apnContext); } } @@ -1753,7 +1793,10 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) { if (DBG) log("onCleanUpConnection"); ApnContext apnContext = mApnContexts.get(apnIdToType(apnId)); - cleanUpConnection(tearDown, apnContext); + if (apnContext != null) { + apnContext.setReason(reason); + cleanUpConnection(tearDown, apnContext); + } } protected boolean isConnected() { diff --git a/telephony/java/com/android/internal/telephony/gsm/SimCard.java b/telephony/java/com/android/internal/telephony/gsm/SimCard.java index b7b0af3..643f709 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SimCard.java +++ b/telephony/java/com/android/internal/telephony/gsm/SimCard.java @@ -50,6 +50,7 @@ public final class SimCard extends IccCard { if(mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) { mPhone.mCM.registerForNVReady(mHandler, EVENT_ICC_READY, null); + mPhone.mCM.registerForIccStatusChanged(mHandler, EVENT_ICC_LOCKED_OR_ABSENT, null); } } @@ -60,6 +61,11 @@ public final class SimCard extends IccCard { mPhone.mCM.unregisterForSIMLockedOrAbsent(mHandler); mPhone.mCM.unregisterForOffOrNotAvailable(mHandler); mPhone.mCM.unregisterForSIMReady(mHandler); + + if(mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) { + mPhone.mCM.unregisterForNVReady(mHandler); + mPhone.mCM.unregisterForIccStatusChanged(mHandler); + } } @Override diff --git a/tests/BiDiTests/AndroidManifest.xml b/tests/BiDiTests/AndroidManifest.xml index 727f980..8a77519 100644 --- a/tests/BiDiTests/AndroidManifest.xml +++ b/tests/BiDiTests/AndroidManifest.xml @@ -14,24 +14,49 @@ limitations under the License. --> -<!-- Declare the contents of this Android application. The namespace - attribute brings in the Android platform namespace, and the package - supplies a unique name for the application. When writing your - own application, the package name must be changed from "com.example.*" - to come from a domain that you own or have control over. --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.bidi" android:versionCode="1" android:versionName="1.0"> <application android:label="BiDiTests"> - <activity android:name="BiDiTestActivity" - android:windowSoftInputMode="stateAlwaysHidden"> + + <activity android:name=".BiDiTestActivity" + android:windowSoftInputMode="stateAlwaysHidden"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity android:name=".BiDiTestBasicActivity" + android:windowSoftInputMode="stateAlwaysHidden"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + + <activity android:name=".BiDiTestCanvasActivity" + android:windowSoftInputMode="stateAlwaysHidden"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + + <activity android:name=".BiDiTestLinearLayoutLtrActivity" + android:windowSoftInputMode="stateAlwaysHidden"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + + <activity android:name=".BiDiTestLinearLayoutRtlActivity" + android:windowSoftInputMode="stateAlwaysHidden"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + </application> + </manifest>
\ No newline at end of file diff --git a/tests/BiDiTests/res/layout/biditest_main.xml b/tests/BiDiTests/res/layout/basic.xml index 044a355..c4807ff 100644 --- a/tests/BiDiTests/res/layout/biditest_main.xml +++ b/tests/BiDiTests/res/layout/basic.xml @@ -20,7 +20,7 @@ android:layout_height="match_parent"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="horizontal" + android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> @@ -39,7 +39,7 @@ android:text="@string/textview_text" /> - <EditText android:id="@+id/textview" + <EditText android:id="@+id/edittext" android:layout_height="wrap_content" android:layout_width="match_parent" android:textSize="32dip" @@ -47,16 +47,4 @@ </LinearLayout> - <SeekBar android:id="@+id/seekbar" - android:layout_height="wrap_content" - android:layout_width="match_parent" - /> - - <view class="com.android.bidi.BiDiTestView" - android:id="@+id/main" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="#FF0000" - /> - </LinearLayout>
\ No newline at end of file diff --git a/tests/BiDiTests/res/layout/canvas.xml b/tests/BiDiTests/res/layout/canvas.xml new file mode 100644 index 0000000..371cc23 --- /dev/null +++ b/tests/BiDiTests/res/layout/canvas.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <SeekBar android:id="@+id/seekbar" + android:layout_height="wrap_content" + android:layout_width="match_parent" + /> + + <view class="com.android.bidi.BiDiTestView" + android:id="@+id/testview" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="#FF0000" + /> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/BiDiTests/res/layout/linear_layout_ltr.xml b/tests/BiDiTests/res/layout/linear_layout_ltr.xml new file mode 100644 index 0000000..a95fb0e --- /dev/null +++ b/tests/BiDiTests/res/layout/linear_layout_ltr.xml @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/layouttest" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:horizontalDirection="ltr"> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalDirection="inherit"> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button1_text" + android:textSize="32dip" + /> + + <TextView android:id="@+id/textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="32dip" + android:text="@string/textview_text" + /> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button2_text" + android:textSize="32dip" + /> + + </LinearLayout> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalDirection="ltr"> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button1_text" + android:textSize="32dip" + /> + + <TextView android:id="@+id/textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="32dip" + android:text="@string/textview_text" + /> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button2_text" + android:textSize="32dip" + /> + + </LinearLayout> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalDirection="rtl"> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button1_text" + android:textSize="32dip" + /> + + <TextView android:id="@+id/textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="32dip" + android:text="@string/textview_text" + /> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button2_text" + android:textSize="32dip" + /> + + </LinearLayout> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalDirection="inherit"> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button1_text" + android:textSize="32dip" + /> + + <TextView android:id="@+id/textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="32dip" + android:text="@string/textview_text" + /> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button2_text" + android:textSize="32dip" + /> + + </LinearLayout> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalDirection="ltr"> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button1_text" + android:textSize="32dip" + /> + + <TextView android:id="@+id/textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="32dip" + android:text="@string/textview_text" + /> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button2_text" + android:textSize="32dip" + /> + + </LinearLayout> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalDirection="rtl"> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button1_text" + android:textSize="32dip" + /> + + <TextView android:id="@+id/textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="32dip" + android:text="@string/textview_text" + /> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button2_text" + android:textSize="32dip" + /> + + </LinearLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/BiDiTests/res/layout/linear_layout_rtl.xml b/tests/BiDiTests/res/layout/linear_layout_rtl.xml new file mode 100644 index 0000000..0d60950 --- /dev/null +++ b/tests/BiDiTests/res/layout/linear_layout_rtl.xml @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/layouttest" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:horizontalDirection="rtl"> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalDirection="inherit"> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button1_text" + android:textSize="32dip" + /> + + <TextView android:id="@+id/textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="32dip" + android:text="@string/textview_text" + /> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button2_text" + android:textSize="32dip" + /> + + </LinearLayout> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalDirection="ltr"> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button1_text" + android:textSize="32dip" + /> + + <TextView android:id="@+id/textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="32dip" + android:text="@string/textview_text" + /> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button2_text" + android:textSize="32dip" + /> + + </LinearLayout> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalDirection="rtl"> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button1_text" + android:textSize="32dip" + /> + + <TextView android:id="@+id/textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="32dip" + android:text="@string/textview_text" + /> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button2_text" + android:textSize="32dip" + /> + + </LinearLayout> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalDirection="inherit"> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button1_text" + android:textSize="32dip" + /> + + <TextView android:id="@+id/textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="32dip" + android:text="@string/textview_text" + /> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button2_text" + android:textSize="32dip" + /> + + </LinearLayout> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalDirection="ltr"> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button1_text" + android:textSize="32dip" + /> + + <TextView android:id="@+id/textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="32dip" + android:text="@string/textview_text" + /> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button2_text" + android:textSize="32dip" + /> + + </LinearLayout> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalDirection="rtl"> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button1_text" + android:textSize="32dip" + /> + + <TextView android:id="@+id/textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:textSize="32dip" + android:text="@string/textview_text" + /> + + <Button android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/button2_text" + android:textSize="32dip" + /> + + </LinearLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/BiDiTests/res/layout/main.xml b/tests/BiDiTests/res/layout/main.xml new file mode 100644 index 0000000..e39d1d6 --- /dev/null +++ b/tests/BiDiTests/res/layout/main.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<TabHost xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/tabhost" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:padding="5dp"> + + <TabWidget + android:id="@android:id/tabs" + android:layout_width="fill_parent" + android:layout_height="wrap_content" /> + + <FrameLayout + android:id="@android:id/tabcontent" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:padding="5dp" /> + + </LinearLayout> + +</TabHost> diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml index c272df1..3795d4b 100644 --- a/tests/BiDiTests/res/values/strings.xml +++ b/tests/BiDiTests/res/values/strings.xml @@ -14,6 +14,9 @@ --> <resources> <string name="button_text">Button</string> + <string name="button1_text">Button1</string> + <string name="button2_text">Button2</string> + <string name="button_requestlayout_text">Request Layout</string> <string name="textview_text">This is a text for a TextView</string> <string name="edittext_text">mmmmmmmmmmmmmmmmmmmmmmmm</string> <string name="normal_text">Normal String</string> diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java index 6c71574..0d9b4f7 100644 --- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java @@ -16,56 +16,43 @@ package com.android.bidi; -import android.app.Activity; +import android.app.TabActivity; +import android.content.Intent; import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.widget.SeekBar; +import android.widget.TabHost; -import static com.android.bidi.BiDiTestConstants.FONT_MIN_SIZE; -import static com.android.bidi.BiDiTestConstants.FONT_MAX_SIZE; - -public class BiDiTestActivity extends Activity { - - static final String TAG = "BiDiTestActivity"; - - static final int INIT_TEXT_SIZE = (FONT_MAX_SIZE - FONT_MIN_SIZE) / 2; - - private BiDiTestView textView; - private SeekBar textSizeSeekBar; +public class BiDiTestActivity extends TabActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.biditest_main); + setContentView(R.layout.main); - textView = (BiDiTestView) findViewById(R.id.main); - textView.setCurrentTextSize(INIT_TEXT_SIZE); + TabHost tabHost = getTabHost(); + TabHost.TabSpec spec; + Intent intent; - textSizeSeekBar = (SeekBar) findViewById(R.id.seekbar); - textSizeSeekBar.setProgress(INIT_TEXT_SIZE); - textSizeSeekBar.setMax(FONT_MAX_SIZE - FONT_MIN_SIZE); + // Create an Intent to launch an Activity for the tab (to be reused) + intent = new Intent().setClass(this, BiDiTestBasicActivity.class); - textSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - textView.setCurrentTextSize(FONT_MIN_SIZE + progress); - } + // Initialize a TabSpec for each tab and add it to the TabHost + spec = tabHost.newTabSpec("basic").setIndicator("Basic").setContent(intent); + tabHost.addTab(spec); - public void onStartTrackingTouch(SeekBar seekBar) { - } + // Do the same for the other tabs + intent = new Intent().setClass(this, BiDiTestCanvasActivity.class); + spec = tabHost.newTabSpec("canvas").setIndicator("Canvas").setContent(intent); + tabHost.addTab(spec); - public void onStopTrackingTouch(SeekBar seekBar) { - } - }); - } + intent = new Intent().setClass(this, BiDiTestLinearLayoutLtrActivity.class); + spec = tabHost.newTabSpec("layout-ltr").setIndicator("LinearLayout LTR").setContent(intent); + tabHost.addTab(spec); - @Override - protected void onResume() { - super.onResume(); - } + intent = new Intent().setClass(this, BiDiTestLinearLayoutRtlActivity.class); + spec = tabHost.newTabSpec("layout-rtl").setIndicator("LinearLayout RTL").setContent(intent); + tabHost.addTab(spec); - public void onButtonClick(View v) { - Log.v(TAG, "onButtonClick"); + tabHost.setCurrentTab(0); } }
\ No newline at end of file diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestBasicActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestBasicActivity.java new file mode 100644 index 0000000..2a8de04 --- /dev/null +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestBasicActivity.java @@ -0,0 +1,30 @@ +/* + * 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 com.android.bidi; + +import android.app.Activity; +import android.os.Bundle; + +public class BiDiTestBasicActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.basic); + } +} diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvasActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvasActivity.java new file mode 100644 index 0000000..3ab75d5 --- /dev/null +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvasActivity.java @@ -0,0 +1,58 @@ +/* + * 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 com.android.bidi; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.SeekBar; + +import static com.android.bidi.BiDiTestConstants.FONT_MAX_SIZE; +import static com.android.bidi.BiDiTestConstants.FONT_MIN_SIZE; + +public class BiDiTestCanvasActivity extends Activity { + + static final int INIT_TEXT_SIZE = (FONT_MAX_SIZE - FONT_MIN_SIZE) / 2; + + private BiDiTestView testView; + private SeekBar textSizeSeekBar; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.canvas); + + testView = (BiDiTestView) findViewById(R.id.testview); + testView.setCurrentTextSize(INIT_TEXT_SIZE); + + textSizeSeekBar = (SeekBar) findViewById(R.id.seekbar); + textSizeSeekBar.setProgress(INIT_TEXT_SIZE); + textSizeSeekBar.setMax(FONT_MAX_SIZE - FONT_MIN_SIZE); + + textSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + testView.setCurrentTextSize(FONT_MIN_SIZE + progress); + } + + public void onStartTrackingTouch(SeekBar seekBar) { + } + + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); + } +} diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutLtrActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutLtrActivity.java new file mode 100644 index 0000000..6d8f11d --- /dev/null +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutLtrActivity.java @@ -0,0 +1,35 @@ +/* + * 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 com.android.bidi; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.LinearLayout; + +public class BiDiTestLinearLayoutLtrActivity extends Activity { + + private LinearLayout layout; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.linear_layout_ltr); + + layout = (LinearLayout) findViewById(R.id.layouttest); + } +} diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutRtlActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutRtlActivity.java new file mode 100644 index 0000000..0130793 --- /dev/null +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutRtlActivity.java @@ -0,0 +1,35 @@ +/* + * 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 com.android.bidi; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.LinearLayout; + +public class BiDiTestLinearLayoutRtlActivity extends Activity { + + private LinearLayout layout; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.linear_layout_rtl); + + layout = (LinearLayout) findViewById(R.id.layouttest); + } +} diff --git a/tests/DumpRenderTree2/assets/run_apache2.py b/tests/DumpRenderTree2/assets/run_apache2.py index b4a8685..3806599 100755 --- a/tests/DumpRenderTree2/assets/run_apache2.py +++ b/tests/DumpRenderTree2/assets/run_apache2.py @@ -79,8 +79,8 @@ def main(run_cmd, options): # complete set of tests and the required scripts. directives += " -c \"DocumentRoot " + os.path.join(layout_tests_path, "http", "tests/") + "\"" directives += " -c \"Alias /LayoutTests " + layout_tests_path + "\"" - directives += " -c \"Alias /WebKitTools/DumpRenderTree/android " + \ - os.path.join(webkit_path, "WebKitTools", "DumpRenderTree", "android") + "\"" + directives += " -c \"Alias /Tools/DumpRenderTree/android " + \ + os.path.join(webkit_path, "Tools", "DumpRenderTree", "android") + "\"" directives += " -c \"Alias /ThirdPartyProject.prop " + \ os.path.join(webkit_path, "ThirdPartyProject.prop") + "\"" diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py index 3b8c09a..161416a 100755 --- a/tests/DumpRenderTree2/assets/run_layout_tests.py +++ b/tests/DumpRenderTree2/assets/run_layout_tests.py @@ -44,9 +44,15 @@ def main(path, options): logging.info("Running the tests...") logging.debug("Command = %s" % cmd) (stdoutdata, stderrdata) = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + if stderrdata != "": + logging.info("Failed to start tests:\n%s", stderrdata) + return if re.search("^INSTRUMENTATION_STATUS_CODE: -1", stdoutdata, re.MULTILINE) != None: logging.info("Failed to run the tests. Is DumpRenderTree2 installed on the device?") return + if re.search("^OK \([0-9]+ tests?\)", stdoutdata, re.MULTILINE) == None: + logging.info("DumpRenderTree2 failed to run correctly:\n%s", stdoutdata) + return logging.info("Downloading the summaries...") diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java index d1aba43..54cbfda 100644 --- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java @@ -63,7 +63,7 @@ public class FsUtils { public static final String LOG_TAG = "FsUtils"; private static final String SCRIPT_URL = ForwarderManager.getHostSchemePort(false) + - "WebKitTools/DumpRenderTree/android/get_layout_tests_dir_contents.php"; + "Tools/DumpRenderTree/android/get_layout_tests_dir_contents.php"; private static final int HTTP_TIMEOUT_MS = 5000; diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java index d9f5dd4..f59da37 100644 --- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java @@ -398,6 +398,11 @@ public class LayoutTestsExecutor extends Activity { } private void startTests() { + // This is called when the tests are started and after each crash. + // We only send the reset message in the former case. + if (mCurrentTestIndex <= 0) { + sendResetMessage(); + } if (mCurrentTestIndex == 0) { sendFirstTestMessage(); } @@ -405,6 +410,15 @@ public class LayoutTestsExecutor extends Activity { runNextTest(); } + private void sendResetMessage() { + try { + Message serviceMsg = Message.obtain(null, ManagerService.MSG_RESET); + mManagerServiceMessenger.send(serviceMsg); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Error sending message to manager service:", e); + } + } + private void sendFirstTestMessage() { try { Message serviceMsg = Message.obtain(null, ManagerService.MSG_FIRST_TEST); diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java index e4df62d..4783cc7 100644 --- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java @@ -66,6 +66,7 @@ public class ManagerService extends Service { static final int MSG_ALL_TESTS_FINISHED = 1; static final int MSG_FIRST_TEST = 2; static final int MSG_CURRENT_TEST_CRASHED = 3; + static final int MSG_RESET = 4; /** * This handler is purely for IPC. It is used to create mMessenger @@ -75,8 +76,11 @@ public class ManagerService extends Service { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_FIRST_TEST: + case MSG_RESET: mSummarizer.reset(); + break; + + case MSG_FIRST_TEST: Bundle bundle = msg.getData(); ensureNextTestSetup(bundle.getString("firstTest"), bundle.getInt("index")); break; diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java index 65c6964..bae8e6b 100644 --- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java @@ -568,7 +568,7 @@ public class Summarizer { URL url = null; try { url = new URL("http", "localhost", ForwarderManager.HTTP_PORT, - "/WebKitTools/DumpRenderTree/android/view_source.php?src=" + + "/Tools/DumpRenderTree/android/view_source.php?src=" + relativePath); } catch (MalformedURLException e) { assert false : "relativePath=" + relativePath; diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index 75535f8..4894196 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -913,6 +913,11 @@ bool AaptGroupEntry::getUiModeTypeName(const char* name, (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) | ResTable_config::UI_MODE_TYPE_CAR; return true; + } else if (strcmp(name, "television") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_TELEVISION; + return true; } return false; diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 134f29f..bd6da64 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -491,7 +491,7 @@ public class WifiStateMachine extends StateMachine { mNetworkInfo.setIsAvailable(false); mLinkProperties.clear(); mLastBssid = null; - mLastNetworkId = -1; + mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID; mLastSignalLevel = -1; mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); @@ -837,11 +837,12 @@ public class WifiStateMachine extends StateMachine { } public void connectNetwork(WifiConfiguration wifiConfig) { - /* arg1 is used to indicate netId, force a netId value of -1 when - * we are passing a configuration since the default value of - * 0 is a valid netId + /* arg1 is used to indicate netId, force a netId value of + * WifiConfiguration.INVALID_NETWORK_ID when we are passing + * a configuration since the default value of 0 is a valid netId */ - sendMessage(obtainMessage(CMD_CONNECT_NETWORK, -1, 0, wifiConfig)); + sendMessage(obtainMessage(CMD_CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID, + 0, wifiConfig)); } public void saveNetwork(WifiConfiguration wifiConfig) { @@ -1445,7 +1446,7 @@ public class WifiStateMachine extends StateMachine { mWifiInfo.setInetAddress(null); mWifiInfo.setBSSID(null); mWifiInfo.setSSID(null); - mWifiInfo.setNetworkId(-1); + mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID); mWifiInfo.setRssi(MIN_RSSI); mWifiInfo.setLinkSpeed(-1); @@ -1457,7 +1458,7 @@ public class WifiStateMachine extends StateMachine { mLinkProperties.clear(); mLastBssid= null; - mLastNetworkId = -1; + mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID; } @@ -2050,7 +2051,7 @@ public class WifiStateMachine extends StateMachine { mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE); /* Initialize data structures */ mLastBssid = null; - mLastNetworkId = -1; + mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID; mLastSignalLevel = -1; mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand()); @@ -2545,7 +2546,10 @@ public class WifiStateMachine extends StateMachine { // Network id is only valid when we start connecting if (SupplicantState.isConnecting(state)) { mWifiInfo.setNetworkId(stateChangeResult.networkId); + } else { + mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID); } + if (state == SupplicantState.ASSOCIATING) { /* BSSID is valid only in ASSOCIATING state */ mWifiInfo.setBSSID(stateChangeResult.BSSID); |
