diff options
58 files changed, 1695 insertions, 875 deletions
@@ -114,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 \ diff --git a/api/current.txt b/api/current.txt index 3339497..a51b649 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; @@ -11641,6 +11644,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(); @@ -11652,6 +11656,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(); } @@ -21433,6 +21442,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); @@ -25207,6 +25218,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(); @@ -25227,6 +25239,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(); @@ -25298,6 +25311,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/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/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/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/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/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/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/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..ccba894 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; @@ -45,9 +46,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; @@ -234,6 +233,7 @@ public final class ViewAncestor extends Handler implements ViewParent, long mResizeBitmapStartTime; int mResizeBitmapDuration; static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator(); + private ArrayList<LayoutTransition> mPendingTransitions; final ViewConfiguration mViewConfiguration; @@ -672,6 +672,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 " @@ -701,6 +702,28 @@ public final class ViewAncestor extends Handler implements ViewParent, } } + /** + * 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); + } + } + private void performTraversals() { // cache mView since it is used so much below... final View host = mView; @@ -1396,6 +1419,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; @@ -1909,20 +1938,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 +2017,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 +2236,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 +2270,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 +2484,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 +2561,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 +2887,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 +2975,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 +3370,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 +3407,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 +3418,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 +3425,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/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/WebSettings.java b/core/java/android/webkit/WebSettings.java index 8ffbda2..b0b16bb 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 @@ -383,13 +382,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 +1644,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/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index e8083eb..5902745 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; @@ -2439,8 +2416,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 +2425,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/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/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/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/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/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/values/attrs.xml b/core/res/res/values/attrs.xml index 6c18089..ebb70e3 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 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/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/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/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/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/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/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/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 568183b..cf5592c 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -853,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 @@ -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(); @@ -1935,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()) { 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/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java index dc07ef2..3636baa 100644 --- a/telephony/java/com/android/internal/telephony/DataConnection.java +++ b/telephony/java/com/android/internal/telephony/DataConnection.java @@ -205,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 @@ -413,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 @@ -471,6 +429,7 @@ public abstract class DataConnection extends StateMachine { createTime = -1; lastFailTime = -1; lastFailCause = FailCause.NONE; + mRefCount = 0; mLinkProperties = new LinkProperties(); mApn = null; @@ -674,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"); @@ -774,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; @@ -804,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; @@ -928,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; @@ -956,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 939bab2..a0d9b0f 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java @@ -59,6 +59,9 @@ 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. @@ -152,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} */ 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/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 a48202f..e1a6fef 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -612,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) { @@ -809,12 +807,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { apnContext.setState(State.DISCONNECTING); releaseApnContext(apnContext, tearDown); } else { - // STOPSHIP: Reference counting logic in GDCT still have issue. - // Need to be cleaned up in later patch dcac.resetSync(); - if (apnContext.getDataConnection() != null) { - apnContext.getDataConnection().setRefCount(0); - } apnContext.setState(State.IDLE); mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); apnContext.setDataConnection(null); @@ -946,7 +939,12 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { dc = (GsmDataConnection)apnContext.getDataConnection(); if (dc == null) { - dc = findReadyDataConnection(apn); + + dc = (GsmDataConnection) checkForConnectionForApnContext(apnContext); + + if (dc == null) { + dc = findReadyDataConnection(apn); + } if (dc == null) { if (DBG) log("setupData: No ready GsmDataConnection found!"); @@ -964,16 +962,16 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return false; } + DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId()); dc.setProfileId( profileId ); dc.setActiveApnType(apnContext.getApnType()); - int refCount = dc.incAndGetRefCount(); + 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) { + if (refCount == 0) { configureRetry(dc, apnContext.getApnType()); } - DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId()); apnContext.setDataConnectionAc(mDataConnectionAsyncChannels.get(dc.getDataConnectionId())); apnContext.setApnSetting(apn); apnContext.setDataConnection(dc); @@ -1406,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)) { @@ -1436,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()); @@ -1490,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); @@ -1641,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) { @@ -1709,10 +1697,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { apnContext.setState(State.FAILED); mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType()); - int refCount = releaseApnContext(apnContext, false); + releaseApnContext(apnContext, false); if (DBG) { - log("onDataSetupComplete: permanent error apn=%s" + apnString + - " refCount=" + refCount); + log("onDataSetupComplete: permanent error apn=%s" + apnString ); } } else { if (DBG) log("onDataSetupComplete: Not all permanent failures, retry"); @@ -1723,8 +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); + startAlarmForReconnect(APN_DELAY_MILLIS, apnContext); } } } @@ -1764,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); } } @@ -1807,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/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; |
