Merge "Make sure all references to the decoder plugin are accounted for"
diff --git a/Android.mk b/Android.mk
index 4a994ed..c05bafe 100644
--- a/Android.mk
+++ b/Android.mk
@@ -114,6 +114,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 i = runtime.addVmArguments(argc, argv);
- // Next arg is parent directory
- if (i < argc) {
- runtime.mParentDir = argv[i++];
+ // 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 {
+ className = arg;
+ break;
+ }
}
- // 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);
- } else {
- set_process_name(argv0);
+ if (niceName && *niceName) {
+ setArgv0(argv0, niceName);
+ set_process_name(niceName);
+ }
- runtime.mClassName = arg;
+ runtime.mParentDir = parentDir;
- // Remainder of args get passed to startup class main()
- runtime.mArgC = argc-i;
- runtime.mArgV = argv+i;
-
- LOGV("App process is starting with pid=%d, class=%s.\n",
- getpid(), runtime.getClassName());
- runtime.start();
- }
+ 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 @@
#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 @@
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 @@
* 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 @@
*/
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 @@
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 @@
// 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);
- }
- // 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);
+ 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;
+ }
+
+ }
+ }
+
// 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 @@
}
/**
+ * 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 @@
* 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 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 @@
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.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 @@
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 @@
* 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 @@
*/
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 @@
}
/**
+ * 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 @@
}
/**
+ * 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 @@
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 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 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
* <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 @@
}
/**
+ * 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.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 @@
long mResizeBitmapStartTime;
int mResizeBitmapDuration;
static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator();
+ private ArrayList<LayoutTransition> mPendingTransitions;
final ViewConfiguration mViewConfiguration;
@@ -672,6 +672,7 @@
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 @@
}
}
+ /**
+ * 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 @@
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 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 @@
// 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 @@
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 @@
}
}
- 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 @@
if (isDown) {
ensureTouchMode(true);
}
- if(false) {
- captureMotionLog("captureDispatchPointer", event);
- }
// Offset the scroll position.
if (mCurScrollY != 0) {
@@ -2534,6 +2561,7 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 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 @@
}
/**
+ * 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 @@
// immediately.
private boolean mHasInstantTimer;
- // Reference count the pause/resume of timers
- private int mPauseTimerRefCount;
-
private boolean mTimerPaused;
private boolean mHasDeferredTimers;
@@ -136,7 +133,7 @@
* Pause all timers.
*/
public void pause() {
- if (--mPauseTimerRefCount == 0) {
+ if (!mTimerPaused) {
mTimerPaused = true;
mHasDeferredTimers = false;
}
@@ -146,7 +143,7 @@
* 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 @@
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 @@
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 @@
}
/**
- * 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 @@
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 @@
}
// 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 @@
// 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 @@
// 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 @@
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.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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
// 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 @@
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 @@
}
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 @@
}
void showSuggestions() {
+ if (!mSuggestionsEnabled || !isTextEditable()) return;
+
if (mSuggestionsPopupWindow == null) {
mSuggestionsPopupWindow = new SuggestionsPopupWindow();
}
@@ -8615,6 +8612,31 @@
}
/**
+ * 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 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 @@
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 @@
}
@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 @@
}
@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 @@
}
@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 @@
// Double tap detection
private long mPreviousTapUpTime = 0;
- private int mPreviousTapPositionX, mPreviousTapPositionY;
+ private float mPreviousTapPositionX, mPreviousTapPositionY;
SelectionModifierCursorController() {
resetTouchOffsets();
@@ -9593,19 +9615,19 @@
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 @@
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 @@
}
/**
- * 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 @@
/** 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 @@
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 @@
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 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 @@
*/
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 @@
}
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 @@
*/
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 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 @@
*/
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 @@
*
* 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 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];
+ applicationInit(argv);
+ }
- 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);
- }
- }
+ /**
+ * 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");
- if (curArg == argv.length) {
- Slog.e(TAG, "Missing classname argument to RuntimeInit!");
+ 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 @@
// 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.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 @@
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 @@
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 @@
}
/**
- * 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 @@
* 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 @@
/** from --runtime-init */
boolean runtimeInit;
+ /** from --nice-name */
+ String niceName;
+
/** from --capabilities */
boolean capabilitiesSpecified;
long permittedCapabilities;
@@ -315,6 +350,9 @@
/** 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 @@
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 @@
/**
- * 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 @@
}
/**
+ * 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 @@
* 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 @@
descriptors[1], descriptors[2]);
for (FileDescriptor fd: descriptors) {
- ZygoteInit.closeDescriptor(fd);
+ IoUtils.closeQuietly(fd);
}
newStderr = System.err;
} catch (IOException ex) {
@@ -712,37 +812,48 @@
}
}
+ if (parsedArgs.niceName != null) {
+ Process.setArgV0(parsedArgs.niceName);
+ }
+
if (parsedArgs.runtimeInit) {
- RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
- } else {
- ClassLoader cloader;
-
- if (parsedArgs.classpath != null) {
- cloader
- = new PathClassLoader(parsedArgs.classpath,
- ClassLoader.getSystemClassLoader());
+ 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 @@
* 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) {
- // 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");
+ if (pid > 0) {
+ setChildPgid(pid);
+ }
+
+ if (descriptors != null) {
+ for (FileDescriptor fd: descriptors) {
+ IoUtils.closeQuietly(fd);
}
}
- try {
- if (descriptors != null) {
- for (FileDescriptor fd: descriptors) {
- ZygoteInit.closeDescriptor(fd);
+ if (pipeFd != null && pid > 0) {
+ DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
+ int innerPid = -1;
+ try {
+ innerPid = is.readInt();
+ } catch (IOException ex) {
+ Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
+ } finally {
+ try {
+ is.close();
+ } catch (IOException ex) {
}
}
- } catch (IOException ex) {
- Log.e(TAG, "Error closing passed descriptors in "
- + "parent process", ex);
+
+ // 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);
+ }
+ }
}
try {
@@ -808,6 +937,20 @@
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.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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
.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 @@
* 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 @@
* '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 @@
/*
* 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 @@
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 @@
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 @@
} 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 @@
"(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 @@
AndroidRuntime();
virtual ~AndroidRuntime();
+ enum StartMode {
+ Zygote,
+ SystemServer,
+ Application,
+ Tool,
+ };
+
/**
* Register a set of methods in the specified class.
*/
@@ -59,8 +66,7 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
}
int64_t WVMExtractor::getCachedDurationUs(status_t *finalStatus) {
- // TODO: Fill this with life.
+ if (mImpl == NULL) {
+ return 0;
+ }
- *finalStatus = OK;
-
- 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 @@
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 @@
// 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 @@
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 @@
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 @@
close();
delete[] keyBitmask;
delete[] relBitmask;
+ delete[] propBitmask;
delete configuration;
delete virtualKeyMap;
}
@@ -205,6 +206,18 @@
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,23 +847,23 @@
memset(sw_bitmask, 0, sizeof(sw_bitmask));
ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask);
+ 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)];
- if (device->keyBitmask != NULL) {
- memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
- } else {
+ device->relBitmask = new uint8_t[sizeof(rel_bitmask)];
+ device->propBitmask = new uint8_t[sizeof(prop_bitmask)];
+
+ if (!device->keyBitmask || !device->relBitmask || !device->propBitmask) {
delete device;
- LOGE("out of memory allocating key bitmask");
+ LOGE("out of memory allocating bitmasks");
return -1;
}
- device->relBitmask = new uint8_t[sizeof(rel_bitmask)];
- if (device->relBitmask != NULL) {
- memcpy(device->relBitmask, rel_bitmask, sizeof(rel_bitmask));
- } else {
- delete device;
- LOGE("out of memory allocating rel bitmask");
- 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.
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. */
+/* These constants are not defined in linux/input.h in the version of the kernel
+ * headers currently provided with Bionic. */
-#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 EVIOCGPROP(len) _IOC(_IOC_READ, 'E', 0x09, len)
-#define MT_TOOL_FINGER 0 /* Identifies a finger */
-#define MT_TOOL_PEN 1 /* Identifies a pen */
+#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 @@
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 @@
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 @@
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 @@
// --- 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 @@
// --- 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 @@
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 @@
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 @@
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 @@
mLocked.orientedRanges.haveTouchSize = false;
mLocked.orientedRanges.haveToolSize = false;
mLocked.orientedRanges.haveOrientation = false;
+ mLocked.orientedRanges.haveDistance = false;
mPointerGesture.reset();
}
@@ -1947,9 +1974,14 @@
// 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 @@
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 @@
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 @@
}
}
+ 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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::reset() {
TouchInputMapper::reset();
- initialize();
+ clearState();
}
void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
@@ -5144,6 +5270,7 @@
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 @@
// --- 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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
&& 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 @@
};
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 @@
RawAbsoluteAxisInfo toolMajor;
RawAbsoluteAxisInfo toolMinor;
RawAbsoluteAxisInfo orientation;
+ RawAbsoluteAxisInfo distance;
+ RawAbsoluteAxisInfo trackingId;
+ RawAbsoluteAxisInfo slot;
} mRawAxes;
// Current and previous touch sample data.
@@ -819,6 +836,8 @@
float orientationScale;
+ float distanceScale;
+
// Oriented motion ranges for input device info.
struct OrientedRanges {
InputDeviceInfo::MotionRange x;
@@ -840,6 +859,9 @@
bool haveOrientation;
InputDeviceInfo::MotionRange orientation;
+
+ bool haveDistance;
+ InputDeviceInfo::MotionRange distance;
} orientedRanges;
// Oriented dimensions and precision.
@@ -1146,7 +1168,7 @@
int32_t mToolWidth;
int32_t mButtonState;
- void initialize();
+ void clearState();
void sync(nsecs_t when);
};
@@ -1166,20 +1188,21 @@
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 @@
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 @@
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 @@
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 @@
* 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 @@
// 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 @@
/* 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 @@
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 @@
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 @@
Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
}
- if (in != null) {
- mContext.sendBroadcast(in);
+ if (action != null) {
+ sendStorageIntent(action, path);
}
}
@@ -885,7 +881,7 @@
/*
* Mount failed for some reason
*/
- Intent in = null;
+ String action = null;
int code = e.getCode();
if (code == VoldResponseCode.OpFailedNoMedia) {
/*
@@ -898,7 +894,7 @@
* 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 @@
* 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 @@
/*
* Send broadcast intent (if required for the failure)
*/
- if (in != null) {
- mContext.sendBroadcast(in);
+ if (action != null) {
+ sendStorageIntent(action, path);
}
}
@@ -1073,6 +1069,14 @@
}
}
+ 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 @@
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 @@
} 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 @@
* 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 @@
}
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 @@
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..67e73f5f 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -277,8 +277,8 @@
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 @@
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 @@
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 @@
createTime = -1;
lastFailTime = -1;
lastFailCause = FailCause.NONE;
+ mRefCount = 0;
mLinkProperties = new LinkProperties();
mApn = null;
@@ -674,6 +633,11 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
}
/**
+ * 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 @@
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 @@
* 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 @@
}
}
+ 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 @@
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 @@
*
* @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 @@
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 @@
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 @@
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 @@
}
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 @@
}
}
+ 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 @@
} 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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
# 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 @@
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 d1aba437..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 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 @@
}
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 @@
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 @@
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 @@
@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 @@
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 @@
(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;