Merge "Convert line breaks to Unix style"
diff --git a/Android.mk b/Android.mk
index b8a48a4..e3406ea 100644
--- a/Android.mk
+++ b/Android.mk
@@ -71,6 +71,7 @@
core/java/android/app/IBackupAgent.aidl \
core/java/android/app/IInstrumentationWatcher.aidl \
core/java/android/app/INotificationManager.aidl \
+ core/java/android/app/IProcessObserver.aidl \
core/java/android/app/ISearchManager.aidl \
core/java/android/app/ISearchManagerCallback.aidl \
core/java/android/app/IServiceConnection.aidl \
@@ -113,6 +114,7 @@
core/java/android/nfc/ILlcpConnectionlessSocket.aidl \
core/java/android/nfc/ILlcpServiceSocket.aidl \
core/java/android/nfc/ILlcpSocket.aidl \
+ core/java/android/nfc/INdefPushCallback.aidl \
core/java/android/nfc/INfcAdapter.aidl \
core/java/android/nfc/INfcAdapterExtras.aidl \
core/java/android/nfc/INfcTag.aidl \
@@ -160,6 +162,7 @@
core/java/com/android/internal/view/IInputMethodSession.aidl \
core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
+ keystore/java/android/security/IKeyChainAliasResponse.aidl \
keystore/java/android/security/IKeyChainService.aidl \
location/java/android/location/ICountryDetector.aidl \
location/java/android/location/ICountryListener.aidl \
@@ -361,7 +364,7 @@
-werror -hide 113 \
-overview $(LOCAL_PATH)/core/java/overview.html
-framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= $(call intermediates-dir-for,JAVA_LIBRARIES,framework)
+framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= $(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \
frameworks/base/docs/knowntags.txt
@@ -584,7 +587,7 @@
LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(call intermediates-dir-for,JAVA_LIBRARIES,framework)
+LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
LOCAL_MODULE := hidden
diff --git a/api/current.txt b/api/current.txt
index 7d87425..6854965 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -866,6 +866,7 @@
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;
@@ -9684,7 +9687,8 @@
method public void unloadSoundEffects();
method public void unregisterMediaButtonEventReceiver(android.content.ComponentName);
field public static final java.lang.String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY";
- field public static final java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
+ field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
+ field public static final java.lang.String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
field public static final int ADJUST_LOWER = -1; // 0xffffffff
field public static final int ADJUST_RAISE = 1; // 0x1
field public static final int ADJUST_SAME = 0; // 0x0
@@ -9697,6 +9701,7 @@
field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0
field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1
field public static final java.lang.String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE";
+ field public static final java.lang.String EXTRA_SCO_AUDIO_PREVIOUS_STATE = "android.media.extra.SCO_AUDIO_PREVIOUS_STATE";
field public static final java.lang.String EXTRA_SCO_AUDIO_STATE = "android.media.extra.SCO_AUDIO_STATE";
field public static final java.lang.String EXTRA_VIBRATE_SETTING = "android.media.EXTRA_VIBRATE_SETTING";
field public static final java.lang.String EXTRA_VIBRATE_TYPE = "android.media.EXTRA_VIBRATE_TYPE";
@@ -9733,6 +9738,7 @@
field public static final deprecated int ROUTE_HEADSET = 8; // 0x8
field public static final deprecated int ROUTE_SPEAKER = 2; // 0x2
field public static final int SCO_AUDIO_STATE_CONNECTED = 1; // 0x1
+ field public static final int SCO_AUDIO_STATE_CONNECTING = 2; // 0x2
field public static final int SCO_AUDIO_STATE_DISCONNECTED = 0; // 0x0
field public static final int SCO_AUDIO_STATE_ERROR = -1; // 0xffffffff
field public static final int STREAM_ALARM = 4; // 0x4
@@ -10058,6 +10064,7 @@
method public void setOnSeekCompleteListener(android.media.MediaPlayer.OnSeekCompleteListener);
method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
method public void setScreenOnWhilePlaying(boolean);
+ method public void setTexture(android.graphics.SurfaceTexture);
method public void setVolume(float, float);
method public void setWakeMode(android.content.Context, int);
method public void start() throws java.lang.IllegalStateException;
@@ -11640,6 +11647,7 @@
method public void disableForegroundNdefPush(android.app.Activity);
method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], java.lang.String[][]);
method public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
+ method public void enableForegroundNdefPush(android.app.Activity, android.nfc.NfcAdapter.NdefPushCallback);
method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
method public static deprecated android.nfc.NfcAdapter getDefaultAdapter();
method public boolean isEnabled();
@@ -11651,6 +11659,11 @@
field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
}
+ public static abstract interface NfcAdapter.NdefPushCallback {
+ method public abstract android.nfc.NdefMessage createMessage();
+ method public abstract void onMessagePushed();
+ }
+
public final class NfcManager {
method public android.nfc.NfcAdapter getDefaultAdapter();
}
@@ -13923,12 +13936,14 @@
public class ParcelFileDescriptor implements android.os.Parcelable {
ctor public ParcelFileDescriptor(android.os.ParcelFileDescriptor);
+ method public static android.os.ParcelFileDescriptor adoptFd(int);
method public void close() throws java.io.IOException;
method public static android.os.ParcelFileDescriptor[] createPipe() throws java.io.IOException;
method public int describeContents();
method public int detachFd();
method public static android.os.ParcelFileDescriptor dup(java.io.FileDescriptor) throws java.io.IOException;
method public static android.os.ParcelFileDescriptor fromDatagramSocket(java.net.DatagramSocket);
+ method public static android.os.ParcelFileDescriptor fromFd(int) throws java.io.IOException;
method public static android.os.ParcelFileDescriptor fromSocket(java.net.Socket);
method public int getFd();
method public java.io.FileDescriptor getFileDescriptor();
@@ -21430,6 +21445,8 @@
method public void setScaleY(float);
method public void setScrollBarStyle(int);
method public void setScrollContainer(boolean);
+ method public void setScrollX(int);
+ method public void setScrollY(int);
method public void setScrollbarFadingEnabled(boolean);
method public void setSelected(boolean);
method public void setSoundEffectsEnabled(boolean);
@@ -21847,6 +21864,9 @@
public class ViewPropertyAnimator {
method public android.view.ViewPropertyAnimator alpha(float);
method public android.view.ViewPropertyAnimator alphaBy(float);
+ method public void cancel();
+ method public long getDuration();
+ method public long getStartDelay();
method public android.view.ViewPropertyAnimator rotation(float);
method public android.view.ViewPropertyAnimator rotationBy(float);
method public android.view.ViewPropertyAnimator rotationX(float);
@@ -21860,6 +21880,8 @@
method public android.view.ViewPropertyAnimator setDuration(long);
method public android.view.ViewPropertyAnimator setInterpolator(android.animation.TimeInterpolator);
method public android.view.ViewPropertyAnimator setListener(android.animation.Animator.AnimatorListener);
+ method public android.view.ViewPropertyAnimator setStartDelay(long);
+ method public void start();
method public android.view.ViewPropertyAnimator translationX(float);
method public android.view.ViewPropertyAnimator translationXBy(float);
method public android.view.ViewPropertyAnimator translationY(float);
@@ -25204,6 +25226,7 @@
method public final android.content.res.ColorStateList getLinkTextColors();
method public final boolean getLinksClickable();
method public final android.text.method.MovementMethod getMovementMethod();
+ method public int getOffsetForPosition(float, float);
method public android.text.TextPaint getPaint();
method public int getPaintFlags();
method public java.lang.String getPrivateImeOptions();
@@ -25224,6 +25247,7 @@
method public android.text.style.URLSpan[] getUrls();
method public boolean hasSelection();
method public boolean isInputMethodTarget();
+ method public boolean isSuggestionsEnabled();
method public boolean isTextSelectable();
method public int length();
method public boolean moveCursorToVisibleOffset();
@@ -25295,6 +25319,7 @@
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/Activity.java b/core/java/android/app/Activity.java
index 87369ab..3877bd0 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1398,6 +1398,10 @@
public void onConfigurationChanged(Configuration newConfig) {
mCalled = true;
+ if (mActionBar != null) {
+ mActionBar.onConfigurationChanged(newConfig);
+ }
+
mFragments.dispatchConfigurationChanged(newConfig);
if (mWindow != null) {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f2c97964..2a0d798 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1467,6 +1467,22 @@
return true;
}
+ case REGISTER_PROCESS_OBSERVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IProcessObserver observer = IProcessObserver.Stub.asInterface(
+ data.readStrongBinder());
+ registerProcessObserver(observer);
+ return true;
+ }
+
+ case UNREGISTER_PROCESS_OBSERVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IProcessObserver observer = IProcessObserver.Stub.asInterface(
+ data.readStrongBinder());
+ unregisterProcessObserver(observer);
+ return true;
+ }
+
}
return super.onTransact(code, data, reply, flags);
@@ -3300,5 +3316,27 @@
return result;
}
+ public void registerProcessObserver(IProcessObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ mRemote.transact(REGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ mRemote.transact(UNREGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 54c3422..1f53c0e 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -355,6 +355,9 @@
public boolean removeTask(int taskId, int flags) throws RemoteException;
+ public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
+ public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -577,4 +580,6 @@
int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+127;
int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+128;
int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+129;
+ int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+130;
+ int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131;
}
diff --git a/core/java/android/app/IProcessObserver.aidl b/core/java/android/app/IProcessObserver.aidl
new file mode 100644
index 0000000..2094294
--- /dev/null
+++ b/core/java/android/app/IProcessObserver.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+/** {@hide} */
+oneway interface IProcessObserver {
+
+ void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities);
+ void onProcessDied(int pid, int uid);
+
+}
diff --git a/core/java/android/app/IThumbnailRetriever.aidl b/core/java/android/app/IThumbnailRetriever.aidl
index 2a6737d..410cc20 100644
--- a/core/java/android/app/IThumbnailRetriever.aidl
+++ b/core/java/android/app/IThumbnailRetriever.aidl
@@ -18,6 +18,7 @@
/**
* System private API for retrieving thumbnails
+ * {@hide}
*/
interface IThumbnailRetriever {
Bitmap getThumbnail(int index);
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 95451d6..71f6445 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -167,7 +167,8 @@
* Return the current running mode type. May be one of
* {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL},
* {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, or
- * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR},
+ * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, or
+ * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_TV}.
*/
public int getCurrentModeType() {
if (mService != null) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4c7d87f..a660bd7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1544,6 +1544,9 @@
*/
public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
+ /** {@hide} */
+ public static final String NETWORK_POLICY_SERVICE = "netpolicy";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.net.wifi.WifiManager} for handling management of
diff --git a/core/java/android/content/IOnPrimaryClipChangedListener.aidl b/core/java/android/content/IOnPrimaryClipChangedListener.aidl
index fb42a45..46d7f7c 100644
--- a/core/java/android/content/IOnPrimaryClipChangedListener.aidl
+++ b/core/java/android/content/IOnPrimaryClipChangedListener.aidl
@@ -16,6 +16,9 @@
package android.content;
+/**
+ * {@hide}
+ */
oneway interface IOnPrimaryClipChangedListener {
void dispatchPrimaryClipChanged();
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 12ec258..51a7115 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -226,6 +226,7 @@
public 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/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index fa6eae5..d9351ee 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -23,9 +23,6 @@
*/
interface INetworkPolicyManager {
- void onForegroundActivitiesChanged(int uid, int pid, boolean foregroundActivities);
- void onProcessDied(int uid, int pid);
-
void setUidPolicy(int uid, int policy);
int getUidPolicy(int uid);
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 2312bd9..1913aa7 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -16,6 +16,7 @@
package android.net;
+import android.content.Context;
import android.os.RemoteException;
/**
@@ -43,6 +44,10 @@
mService = service;
}
+ public static NetworkPolicyManager getSystemService(Context context) {
+ return (NetworkPolicyManager) context.getSystemService(Context.NETWORK_POLICY_SERVICE);
+ }
+
/**
* Set policy flags for specific UID.
*
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 4430e00..0f207bc 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -36,6 +36,9 @@
/** {@link #uid} value when entry is summarized over all UIDs. */
public static final int UID_ALL = 0;
+ // NOTE: data should only be accounted for once in this structure; if data
+ // is broken out, the summarized version should not be included.
+
/**
* {@link SystemClock#elapsedRealtime()} timestamp when this data was
* generated.
@@ -81,12 +84,13 @@
mTx = new long[size];
}
- public void addEntry(String iface, int uid, long rx, long tx) {
+ public Builder addEntry(String iface, int uid, long rx, long tx) {
mIface[mIndex] = iface;
mUid[mIndex] = uid;
mRx[mIndex] = rx;
mTx[mIndex] = tx;
mIndex++;
+ return this;
}
public NetworkStats build() {
@@ -97,11 +101,17 @@
}
}
+ public int length() {
+ // length is identical for all fields
+ return iface.length;
+ }
+
/**
* Find first stats index that matches the requested parameters.
*/
public int findIndex(String iface, int uid) {
- for (int i = 0; i < this.iface.length; i++) {
+ final int length = length();
+ for (int i = 0; i < length; i++) {
if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
return i;
}
@@ -109,13 +119,38 @@
return -1;
}
- private static boolean equal(Object a, Object b) {
- return a == b || (a != null && a.equals(b));
+ /**
+ * Subtract the given {@link NetworkStats}, effectively leaving the delta
+ * between two snapshots in time. Assumes that statistics rows collect over
+ * time, and that none of them have disappeared.
+ */
+ public NetworkStats subtract(NetworkStats value) {
+ // result will have our rows, but no meaningful timestamp
+ final int length = length();
+ final NetworkStats.Builder result = new NetworkStats.Builder(-1, length);
+
+ for (int i = 0; i < length; i++) {
+ final String iface = this.iface[i];
+ final int uid = this.uid[i];
+
+ // find remote row that matches, and subtract
+ final int j = value.findIndex(iface, uid);
+ if (j == -1) {
+ // newly appearing row, return entire value
+ result.addEntry(iface, uid, this.rx[i], this.tx[i]);
+ } else {
+ // existing row, subtract remote value
+ final long rx = this.rx[i] - value.rx[j];
+ final long tx = this.tx[i] - value.tx[j];
+ result.addEntry(iface, uid, rx, tx);
+ }
+ }
+
+ return result.build();
}
- /** {@inheritDoc} */
- public int describeContents() {
- return 0;
+ private static boolean equal(Object a, Object b) {
+ return a == b || (a != null && a.equals(b));
}
public void dump(String prefix, PrintWriter pw) {
@@ -138,6 +173,11 @@
}
/** {@inheritDoc} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(elapsedRealtime);
dest.writeStringArray(iface);
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 7ee7a81..c0ff734 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -16,6 +16,12 @@
package android.net;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
import dalvik.system.BlockGuard;
import java.net.Socket;
@@ -36,6 +42,17 @@
public final static int UNSUPPORTED = -1;
/**
+ * Snapshot of {@link NetworkStats} when the currently active profiling
+ * session started, or {@code null} if no session active.
+ *
+ * @see #startDataProfiling(Context)
+ * @see #stopDataProfiling(Context)
+ */
+ private static NetworkStats sActiveProfilingStart;
+
+ private static Object sProfilingLock = new Object();
+
+ /**
* Set active tag to use when accounting {@link Socket} traffic originating
* from the current thread. Only one active tag per thread is supported.
* <p>
@@ -93,6 +110,44 @@
}
/**
+ * Start profiling data usage for current UID. Only one profiling session
+ * can be active at a time.
+ *
+ * @hide
+ */
+ public static void startDataProfiling(Context context) {
+ synchronized (sProfilingLock) {
+ if (sActiveProfilingStart != null) {
+ throw new IllegalStateException("already profiling data");
+ }
+
+ // take snapshot in time; we calculate delta later
+ sActiveProfilingStart = getNetworkStatsForUid(context);
+ }
+ }
+
+ /**
+ * Stop profiling data usage for current UID.
+ *
+ * @return Detailed {@link NetworkStats} of data that occurred since last
+ * {@link #startDataProfiling(Context)} call.
+ * @hide
+ */
+ public static NetworkStats stopDataProfiling(Context context) {
+ synchronized (sProfilingLock) {
+ if (sActiveProfilingStart == null) {
+ throw new IllegalStateException("not profiling data");
+ }
+
+ // subtract starting values and return delta
+ final NetworkStats profilingStop = getNetworkStatsForUid(context);
+ final NetworkStats profilingDelta = profilingStop.subtract(sActiveProfilingStart);
+ sActiveProfilingStart = null;
+ return profilingDelta;
+ }
+ }
+
+ /**
* Get the total number of packets transmitted through the mobile interface.
*
* @return number of packets. If the statistics are not supported by this device,
@@ -350,4 +405,21 @@
* {@link #UNSUPPORTED} will be returned.
*/
public static native long getUidUdpRxPackets(int uid);
+
+ /**
+ * Return detailed {@link NetworkStats} for the current UID. Requires no
+ * special permission.
+ */
+ private static NetworkStats getNetworkStatsForUid(Context context) {
+ final IBinder binder = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ final INetworkManagementService service = INetworkManagementService.Stub.asInterface(
+ binder);
+
+ final int uid = android.os.Process.myUid();
+ try {
+ return service.getNetworkStatsUidDetail(uid);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/INdefPushCallback.aidl
new file mode 100644
index 0000000..80ba2ed
--- /dev/null
+++ b/core/java/android/nfc/INdefPushCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.nfc.NdefMessage;
+
+/**
+ * @hide
+ */
+interface INdefPushCallback
+{
+ NdefMessage onConnect();
+ void onMessagePushed();
+}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 870127c..d11fea0 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -25,6 +25,7 @@
import android.nfc.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/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index ecc111b..f17a6f2 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -214,6 +214,12 @@
NetworkStats getNetworkStatsDetail();
/**
+ * Return detailed network statistics for the requested UID,
+ * including interface and tag details.
+ */
+ NetworkStats getNetworkStatsUidDetail(int uid);
+
+ /**
* Configures bandwidth throttling on an interface.
*/
void setInterfaceThrottle(String iface, int rxKbps, int txKbps);
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 727fcca..3ea3f56 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -129,7 +129,46 @@
}
/**
- * Create a new ParcelFileDescriptor from the specified Socket.
+ * Create a new ParcelFileDescriptor from a raw native fd. The new
+ * ParcelFileDescriptor holds a dup of the original fd passed in here,
+ * so you must still close that fd as well as the new ParcelFileDescriptor.
+ *
+ * @param fd The native fd that the ParcelFileDescriptor should dup.
+ *
+ * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
+ * for a dup of the given fd.
+ */
+ public static ParcelFileDescriptor fromFd(int fd) throws IOException {
+ FileDescriptor fdesc = getFileDescriptorFromFd(fd);
+ return new ParcelFileDescriptor(fdesc);
+ }
+
+ // Extracts the file descriptor from the specified socket and returns it untouched
+ private static native FileDescriptor getFileDescriptorFromFd(int fd) throws IOException;
+
+ /**
+ * Take ownership of a raw native fd in to a new ParcelFileDescriptor.
+ * The returned ParcelFileDescriptor now owns the given fd, and will be
+ * responsible for closing it. You must not close the fd yourself.
+ *
+ * @param fd The native fd that the ParcelFileDescriptor should adopt.
+ *
+ * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
+ * for the given fd.
+ */
+ public static ParcelFileDescriptor adoptFd(int fd) {
+ FileDescriptor fdesc = getFileDescriptorFromFdNoDup(fd);
+ return new ParcelFileDescriptor(fdesc);
+ }
+
+ // Extracts the file descriptor from the specified socket and returns it untouched
+ private static native FileDescriptor getFileDescriptorFromFdNoDup(int fd);
+
+ /**
+ * Create a new ParcelFileDescriptor from the specified Socket. The new
+ * ParcelFileDescriptor holds a dup of the original FileDescriptor in
+ * the Socket, so you must still close the Socket as well as the new
+ * ParcelFileDescriptor.
*
* @param socket The Socket whose FileDescriptor is used to create
* a new ParcelFileDescriptor.
@@ -163,17 +202,14 @@
*/
public static ParcelFileDescriptor[] createPipe() throws IOException {
FileDescriptor[] fds = new FileDescriptor[2];
- int res = createPipeNative(fds);
- if (res == 0) {
- ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2];
- pfds[0] = new ParcelFileDescriptor(fds[0]);
- pfds[1] = new ParcelFileDescriptor(fds[1]);
- return pfds;
- }
- throw new IOException("Unable to create pipe: errno=" + -res);
+ createPipeNative(fds);
+ ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2];
+ pfds[0] = new ParcelFileDescriptor(fds[0]);
+ pfds[1] = new ParcelFileDescriptor(fds[1]);
+ return pfds;
}
- private static native int createPipeNative(FileDescriptor[] outFds);
+ private static native void createPipeNative(FileDescriptor[] outFds) throws IOException;
/**
* @hide Please use createPipe() or ContentProvider.openPipeHelper().
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index f85df6c..d475f36 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -634,6 +634,20 @@
}
/**
+ * Returns the parent process id for a currently running process.
+ * @param pid the process id
+ * @return the parent process id of the process, or -1 if the process is not running.
+ * @hide
+ */
+ public static final int getParentPid(int pid) {
+ String[] procStatusLabels = { "PPid:" };
+ long[] procStatusValues = new long[1];
+ procStatusValues[0] = -1;
+ Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues);
+ return (int) procStatusValues[0];
+ }
+
+ /**
* Set the priority of a thread, based on Linux priorities.
*
* @param tid The identifier of the thread/process to change.
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 1375a29..01c640a 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -1481,6 +1481,13 @@
onVmPolicyViolation(message, originStack);
}
+ /**
+ * @hide
+ */
+ public static void onWebViewMethodCalledOnWrongThread(Throwable originStack) {
+ onVmPolicyViolation(null, originStack);
+ }
+
// Map from VM violation fingerprint to uptime millis.
private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>();
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index d68e6fb..bc6e993 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -36,6 +36,11 @@
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/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
old mode 100644
new mode 100755
index 2c79385..60bee9a
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1104,7 +1104,7 @@
}
/*package*/ synchronized boolean setBondState(String address, int state, int reason) {
- mBondState.setBondState(address.toUpperCase(), state);
+ mBondState.setBondState(address.toUpperCase(), state, reason);
return true;
}
diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
index 6f4c15b..d698b54 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
@@ -18,6 +18,7 @@
import android.media.AudioFormat;
import android.media.AudioTrack;
import android.os.Bundle;
+import android.os.Handler;
import android.util.Log;
/**
@@ -49,16 +50,20 @@
private final float mPan;
private final Object mStateLock = new Object();
- private AudioTrack mAudioTrack = null;
+ private final Handler mAudioTrackHandler;
+ private volatile AudioTrack mAudioTrack = null;
private boolean mStopped = false;
private boolean mDone = false;
+ private volatile boolean mWriteErrorOccured;
PlaybackSynthesisRequest(String text, Bundle params,
- int streamType, float volume, float pan) {
+ int streamType, float volume, float pan, Handler audioTrackHandler) {
super(text, params);
mStreamType = streamType;
mVolume = volume;
mPan = pan;
+ mAudioTrackHandler = audioTrackHandler;
+ mWriteErrorOccured = false;
}
@Override
@@ -70,14 +75,28 @@
}
}
+ // Always guarded by mStateLock.
private void cleanUp() {
if (DBG) Log.d(TAG, "cleanUp()");
- if (mAudioTrack != null) {
- mAudioTrack.flush();
- mAudioTrack.stop();
- mAudioTrack.release();
- mAudioTrack = null;
+ if (mAudioTrack == null) {
+ return;
}
+
+ final AudioTrack audioTrack = mAudioTrack;
+ mAudioTrack = null;
+
+ // Clean up on the audiotrack handler thread.
+ //
+ // NOTE: It isn't very clear whether AudioTrack is thread safe.
+ // If it is we can clean up on the current (synthesis) thread.
+ mAudioTrackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ audioTrack.flush();
+ audioTrack.stop();
+ audioTrack.release();
+ }
+ });
}
@Override
@@ -146,10 +165,15 @@
Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
+ offset + "," + length + ")");
}
- if (length > getMaxBufferSize()) {
- throw new IllegalArgumentException("buffer is too large (" + length + " bytes)");
+ if (length > getMaxBufferSize() || length <= 0) {
+ throw new IllegalArgumentException("buffer is too large or of zero length (" +
+ + length + " bytes)");
}
synchronized (mStateLock) {
+ if (mWriteErrorOccured) {
+ if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
+ return TextToSpeech.ERROR;
+ }
if (mStopped) {
if (DBG) Log.d(TAG, "Request has been aborted.");
return TextToSpeech.ERROR;
@@ -158,22 +182,33 @@
Log.e(TAG, "audioAvailable(): Not started");
return TextToSpeech.ERROR;
}
- int playState = mAudioTrack.getPlayState();
- if (playState == AudioTrack.PLAYSTATE_STOPPED) {
- if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
- mAudioTrack.play();
- }
- // TODO: loop until all data is written?
- if (DBG) Log.d(TAG, "AudioTrack.write()");
- int count = mAudioTrack.write(buffer, offset, length);
- if (DBG) Log.d(TAG, "AudioTrack.write() returned " + count);
- if (count < 0) {
- Log.e(TAG, "Writing to AudioTrack failed: " + count);
- cleanUp();
- return TextToSpeech.ERROR;
- } else {
- return TextToSpeech.SUCCESS;
- }
+ final AudioTrack audioTrack = mAudioTrack;
+ // Sigh, another copy.
+ final byte[] bufferCopy = new byte[length];
+ System.arraycopy(buffer, offset, bufferCopy, 0, length);
+
+ mAudioTrackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ int playState = audioTrack.getPlayState();
+ if (playState == AudioTrack.PLAYSTATE_STOPPED) {
+ if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
+ audioTrack.play();
+ }
+ // TODO: loop until all data is written?
+ if (DBG) Log.d(TAG, "AudioTrack.write()");
+ int count = audioTrack.write(bufferCopy, 0, bufferCopy.length);
+ // The semantics of this change very slightly. Earlier, we would
+ // report an error immediately, Now we will return an error on
+ // the next API call, usually done( ) or another audioAvailable( )
+ // call.
+ if (count < 0) {
+ mWriteErrorOccured = true;
+ }
+ }
+ });
+
+ return TextToSpeech.SUCCESS;
}
}
@@ -181,6 +216,10 @@
public int done() {
if (DBG) Log.d(TAG, "done()");
synchronized (mStateLock) {
+ if (mWriteErrorOccured) {
+ if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
+ return TextToSpeech.ERROR;
+ }
if (mStopped) {
if (DBG) Log.d(TAG, "Request has been aborted.");
return TextToSpeech.ERROR;
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index f32474f..717dde8 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -51,6 +51,7 @@
private static final String SYNTH_THREAD_NAME = "SynthThread";
private SynthHandler mSynthHandler;
+ private Handler mAudioTrackHandler;
private CallbackMap mCallbacks;
@@ -63,6 +64,10 @@
synthThread.start();
mSynthHandler = new SynthHandler(synthThread.getLooper());
+ HandlerThread audioTrackThread = new HandlerThread("TTS.audioTrackThread");
+ audioTrackThread.start();
+ mAudioTrackHandler = new Handler(audioTrackThread.getLooper());
+
mCallbacks = new CallbackMap();
// Load default language
@@ -75,6 +80,7 @@
// Tell the synthesizer to stop
mSynthHandler.quit();
+ mAudioTrackHandler.getLooper().quit();
// Unregister all callbacks.
mCallbacks.kill();
@@ -444,7 +450,7 @@
protected SynthesisRequest createSynthesisRequest() {
return new PlaybackSynthesisRequest(mText, mParams,
- getStreamType(), getVolume(), getPan());
+ getStreamType(), getVolume(), getPan(), mAudioTrackHandler);
}
private void setRequestParams(SynthesisRequest request) {
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index d432dee..fe96565 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -234,7 +234,7 @@
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/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 845fbc3..61a24a0 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -17,7 +17,6 @@
package android.view;
-import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
@@ -137,14 +136,14 @@
*
* @param canvas The Canvas used to render the view.
*/
- void onHardwarePreDraw(Canvas canvas);
+ void onHardwarePreDraw(HardwareCanvas canvas);
/**
* Invoked after a view is drawn by a hardware renderer.
*
* @param canvas The Canvas used to render the view.
*/
- void onHardwarePostDraw(Canvas canvas);
+ void onHardwarePostDraw(HardwareCanvas canvas);
}
/**
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 82fd581..3436cd1 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -883,8 +883,8 @@
* <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..bf04502 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -17,6 +17,7 @@
package android.view;
import android.Manifest;
+import android.animation.LayoutTransition;
import android.app.ActivityManagerNative;
import android.content.ClipDescription;
import android.content.ComponentCallbacks;
@@ -25,7 +26,6 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PixelFormat;
@@ -45,9 +45,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -230,10 +228,11 @@
int mScrollY;
int mCurScrollY;
Scroller mScroller;
- Bitmap mResizeBitmap;
- long mResizeBitmapStartTime;
- int mResizeBitmapDuration;
+ HardwareLayer mResizeBuffer;
+ long mResizeBufferStartTime;
+ int mResizeBufferDuration;
static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator();
+ private ArrayList<LayoutTransition> mPendingTransitions;
final ViewConfiguration mViewConfiguration;
@@ -672,6 +671,7 @@
if (!mTraversalScheduled) {
mTraversalScheduled = true;
+ //noinspection ConstantConditions
if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
final long now = System.nanoTime();
Log.d(TAG, "Latency: Scheduled traversal, it has been "
@@ -694,10 +694,32 @@
return mAppVisible ? mView.getVisibility() : View.GONE;
}
- void disposeResizeBitmap() {
- if (mResizeBitmap != null) {
- mResizeBitmap.recycle();
- mResizeBitmap = null;
+ void disposeResizeBuffer() {
+ if (mResizeBuffer != null) {
+ mResizeBuffer.destroy();
+ mResizeBuffer = null;
+ }
+ }
+
+ /**
+ * Add LayoutTransition to the list of transitions to be started in the next traversal.
+ * This list will be cleared after the transitions on the list are start()'ed. These
+ * transitionsa re added by LayoutTransition itself when it sets up animations. The setup
+ * happens during the layout phase of traversal, which we want to complete before any of the
+ * animations are started (because those animations may side-effect properties that layout
+ * depends upon, like the bounding rectangles of the affected views). So we add the transition
+ * to the list and it is started just prior to starting the drawing phase of traversal.
+ *
+ * @param transition The LayoutTransition to be started on the next traversal.
+ *
+ * @hide
+ */
+ public void requestTransitionStart(LayoutTransition transition) {
+ if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) {
+ if (mPendingTransitions == null) {
+ mPendingTransitions = new ArrayList<LayoutTransition>();
+ }
+ mPendingTransitions.add(transition);
}
}
@@ -819,15 +841,25 @@
mAttachInfo.mHardwareRenderer.isEnabled() &&
lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
- disposeResizeBitmap();
+ disposeResizeBuffer();
boolean completed = false;
+ HardwareCanvas canvas = null;
try {
- mResizeBitmap = Bitmap.createBitmap(mWidth, mHeight,
- Bitmap.Config.ARGB_8888);
- mResizeBitmap.setHasAlpha(false);
- Canvas canvas = new Canvas(mResizeBitmap);
+ if (mResizeBuffer == null) {
+ mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer(
+ mWidth, mHeight, false);
+ } else if (mResizeBuffer.getWidth() != mWidth ||
+ mResizeBuffer.getHeight() != mHeight) {
+ mResizeBuffer.resize(mWidth, mHeight);
+ }
+ canvas = mResizeBuffer.start(mAttachInfo.mHardwareCanvas);
+ canvas.setViewport(mWidth, mHeight);
+ canvas.onPreDraw(null);
+ final int restoreCount = canvas.save();
+
canvas.drawColor(0xff000000, PorterDuff.Mode.SRC);
+
int yoff;
final boolean scrolling = mScroller != null
&& mScroller.computeScrollOffset();
@@ -837,22 +869,32 @@
} else {
yoff = mScrollY;
}
+
canvas.translate(0, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
- canvas.setScreenDensity(mAttachInfo.mScalingRequired
- ? DisplayMetrics.DENSITY_DEVICE : 0);
+
mView.draw(canvas);
- mResizeBitmapStartTime = SystemClock.uptimeMillis();
- mResizeBitmapDuration = mView.getResources().getInteger(
+
+ mResizeBufferStartTime = SystemClock.uptimeMillis();
+ mResizeBufferDuration = mView.getResources().getInteger(
com.android.internal.R.integer.config_mediumAnimTime);
completed = true;
+
+ canvas.restoreToCount(restoreCount);
} catch (OutOfMemoryError e) {
Log.w(TAG, "Not enough memory for content change anim buffer", e);
} finally {
- if (!completed) {
- mResizeBitmap = null;
+ if (canvas != null) {
+ canvas.onPostDraw();
+ }
+ if (mResizeBuffer != null) {
+ mResizeBuffer.end(mAttachInfo.mHardwareCanvas);
+ if (!completed) {
+ mResizeBuffer.destroy();
+ mResizeBuffer = null;
+ }
}
}
}
@@ -1114,7 +1156,7 @@
if (mScroller != null) {
mScroller.abortAnimation();
}
- disposeResizeBitmap();
+ disposeResizeBuffer();
} else if (surfaceGenerationId != mSurface.getGenerationId() &&
mSurfaceHolder == null && mAttachInfo.mHardwareRenderer != null) {
fullRedrawNeeded = true;
@@ -1396,6 +1438,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;
@@ -1495,15 +1543,14 @@
int mResizeAlpha;
final Paint mResizePaint = new Paint();
- public void onHardwarePreDraw(Canvas canvas) {
+ public void onHardwarePreDraw(HardwareCanvas canvas) {
canvas.translate(0, -mHardwareYOffset);
}
- public void onHardwarePostDraw(Canvas canvas) {
- if (mResizeBitmap != null) {
- canvas.translate(0, mHardwareYOffset);
+ public void onHardwarePostDraw(HardwareCanvas canvas) {
+ if (mResizeBuffer != null) {
mResizePaint.setAlpha(mResizeAlpha);
- canvas.drawBitmap(mResizeBitmap, 0, 0, mResizePaint);
+ canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint);
}
}
@@ -1559,15 +1606,15 @@
boolean scalingRequired = mAttachInfo.mScalingRequired;
int resizeAlpha = 0;
- if (mResizeBitmap != null) {
- long deltaTime = SystemClock.uptimeMillis() - mResizeBitmapStartTime;
- if (deltaTime < mResizeBitmapDuration) {
- float amt = deltaTime/(float)mResizeBitmapDuration;
+ if (mResizeBuffer != null) {
+ long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime;
+ if (deltaTime < mResizeBufferDuration) {
+ float amt = deltaTime/(float) mResizeBufferDuration;
amt = mResizeInterpolator.getInterpolation(amt);
animating = true;
resizeAlpha = 255 - (int)(amt*255);
} else {
- disposeResizeBitmap();
+ disposeResizeBuffer();
}
}
@@ -1579,7 +1626,7 @@
if (mScroller != null) {
mScroller.abortAnimation();
}
- disposeResizeBitmap();
+ disposeResizeBuffer();
}
return;
}
@@ -1863,7 +1910,7 @@
if (scrollY != mScrollY) {
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
+ mScrollY + " , new=" + scrollY);
- if (!immediate && mResizeBitmap == null) {
+ if (!immediate && mResizeBuffer == null) {
if (mScroller == null) {
mScroller = new Scroller(mView.getContext());
}
@@ -1909,20 +1956,22 @@
public void focusableViewAvailable(View v) {
checkThread();
- if (mView != null && !mView.hasFocus()) {
- v.requestFocus();
- } else {
- // the one case where will transfer focus away from the current one
- // is if the current view is a view group that prefers to give focus
- // to its children first AND the view is a descendant of it.
- mFocusedView = mView.findFocus();
- boolean descendantsHaveDibsOnFocus =
- (mFocusedView instanceof ViewGroup) &&
- (((ViewGroup) mFocusedView).getDescendantFocusability() ==
- ViewGroup.FOCUS_AFTER_DESCENDANTS);
- if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
- // If a view gets the focus, the listener will be invoked from requestChildFocus()
+ if (mView != null) {
+ if (!mView.hasFocus()) {
v.requestFocus();
+ } else {
+ // the one case where will transfer focus away from the current one
+ // is if the current view is a view group that prefers to give focus
+ // to its children first AND the view is a descendant of it.
+ mFocusedView = mView.findFocus();
+ boolean descendantsHaveDibsOnFocus =
+ (mFocusedView instanceof ViewGroup) &&
+ (((ViewGroup) mFocusedView).getDescendantFocusability() ==
+ ViewGroup.FOCUS_AFTER_DESCENDANTS);
+ if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
+ // If a view gets the focus, the listener will be invoked from requestChildFocus()
+ v.requestFocus();
+ }
}
}
}
@@ -1986,9 +2035,7 @@
// At this point the resources have been updated to
// have the most recent config, whatever that is. Use
// the on in them which may be newer.
- if (mView != null) {
- config = mView.getResources().getConfiguration();
- }
+ config = mView.getResources().getConfiguration();
if (force || mLastConfiguration.diff(config) != 0) {
mLastConfiguration.setTo(config);
mView.dispatchConfigurationChanged(config);
@@ -2207,6 +2254,7 @@
if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
// The IME is trying to say this event is from the
// system! Bad bad bad!
+ //noinspection UnusedAssignment
event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
}
deliverKeyEventPostIme((KeyEvent)msg.obj, false);
@@ -2240,7 +2288,7 @@
}
}
- private void startInputEvent(InputEvent event, InputQueue.FinishedCallback finishedCallback) {
+ private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
if (mFinishedCallback != null) {
Slog.w(TAG, "Received a new input event from the input queue but there is "
+ "already an unfinished input event in progress.");
@@ -2454,9 +2502,6 @@
if (isDown) {
ensureTouchMode(true);
}
- if(false) {
- captureMotionLog("captureDispatchPointer", event);
- }
// Offset the scroll position.
if (mCurScrollY != 0) {
@@ -2534,6 +2579,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 +2905,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 +2993,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 +3388,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 +3425,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 +3436,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 +3443,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/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 1d56e9d..9eddf23 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -67,6 +67,19 @@
private boolean mDurationSet = false;
/**
+ * The startDelay of the underlying Animator object. By default, we don't set the startDelay
+ * on the Animator and just use its default startDelay. If the startDelay is ever set on this
+ * Animator, then we use the startDelay that it was set to.
+ */
+ private long mStartDelay = 0;
+
+ /**
+ * A flag indicating whether the startDelay has been set on this object. If not, we don't set
+ * the startDelay on the underlying Animator, but instead just use its default startDelay.
+ */
+ private boolean mStartDelaySet = false;
+
+ /**
* The interpolator of the underlying Animator object. By default, we don't set the interpolator
* on the Animator and just use its default interpolator. If the interpolator is ever set on
* this Animator, then we use the interpolator that it was set to.
@@ -233,6 +246,60 @@
}
/**
+ * Returns the current duration of property animations. If the duration was set on this
+ * object, that value is returned. Otherwise, the default value of the underlying Animator
+ * is returned.
+ *
+ * @see #setDuration(long)
+ * @return The duration of animations, in milliseconds.
+ */
+ public long getDuration() {
+ if (mStartDelaySet) {
+ return mStartDelay;
+ } else {
+ // Just return the default from ValueAnimator, since that's what we'd get if
+ // the value has not been set otherwise
+ return new ValueAnimator().getDuration();
+ }
+ }
+
+ /**
+ * Returns the current startDelay of property animations. If the startDelay was set on this
+ * object, that value is returned. Otherwise, the default value of the underlying Animator
+ * is returned.
+ *
+ * @see #setStartDelay(long)
+ * @return The startDelay of animations, in milliseconds.
+ */
+ public long getStartDelay() {
+ if (mStartDelaySet) {
+ return mStartDelay;
+ } else {
+ // Just return the default from ValueAnimator (0), since that's what we'd get if
+ // the value has not been set otherwise
+ return 0;
+ }
+ }
+
+ /**
+ * Sets the startDelay for the underlying animator that animates the requested properties.
+ * By default, the animator uses the default value for ValueAnimator. Calling this method
+ * will cause the declared value to be used instead.
+ * @param startDelay The delay of ensuing property animations, in milliseconds. The value
+ * cannot be negative.
+ * @return This object, allowing calls to methods in this class to be chained.
+ */
+ public ViewPropertyAnimator setStartDelay(long startDelay) {
+ if (startDelay < 0) {
+ throw new IllegalArgumentException("Animators cannot have negative duration: " +
+ startDelay);
+ }
+ mStartDelaySet = true;
+ mStartDelay = startDelay;
+ return this;
+ }
+
+ /**
* Sets the interpolator for the underlying animator that animates the requested properties.
* By default, the animator uses the default interpolator for ValueAnimator. Calling this method
* will cause the declared object to be used instead.
@@ -259,6 +326,33 @@
}
/**
+ * Starts the currently pending property animations immediately. Calling <code>start()</code>
+ * is optional because all animations start automatically at the next opportunity. However,
+ * if the animations are needed to start immediately and synchronously (not at the time when
+ * the next event is processed by the hierarchy, which is when the animations would begin
+ * otherwise), then this method can be used.
+ */
+ public void start() {
+ startAnimation();
+ }
+
+ /**
+ * Cancels all property animations that are currently running or pending.
+ */
+ public void cancel() {
+ if (mAnimatorMap.size() > 0) {
+ HashMap<Animator, PropertyBundle> mAnimatorMapCopy =
+ (HashMap<Animator, PropertyBundle>)mAnimatorMap.clone();
+ Set<Animator> animatorSet = mAnimatorMapCopy.keySet();
+ for (Animator runningAnim : animatorSet) {
+ runningAnim.cancel();
+ }
+ }
+ mPendingAnimations.clear();
+ mView.getHandler().removeCallbacks(mAnimationStarter);
+ }
+
+ /**
* This method will cause the View's <code>x</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
@@ -598,7 +692,7 @@
// on a property will cancel a previous animation on that property, so
// there can only ever be one such animation running.
if (bundle.mPropertyMask == NONE) {
- // the animation is not longer changing anything - cancel it
+ // the animation is no longer changing anything - cancel it
animatorToCancel = runningAnim;
break;
}
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index 976e786..12391df 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -39,9 +39,6 @@
// 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/L10nUtils.java b/core/java/android/webkit/L10nUtils.java
index f59d7d0..5b4fb1d 100644
--- a/core/java/android/webkit/L10nUtils.java
+++ b/core/java/android/webkit/L10nUtils.java
@@ -69,7 +69,8 @@
com.android.internal.R.string.autofill_card_number_re, // IDS_AUTOFILL_CARD_NUMBER_RE
com.android.internal.R.string.autofill_expiration_month_re, // IDS_AUTOFILL_EXPIRATION_MONTH_RE
com.android.internal.R.string.autofill_expiration_date_re, // IDS_AUTOFILL_EXPIRATION_DATE_RE
- com.android.internal.R.string.autofill_card_ignored_re // IDS_AUTOFILL_CARD_IGNORED_RE
+ com.android.internal.R.string.autofill_card_ignored_re, // IDS_AUTOFILL_CARD_IGNORED_RE
+ com.android.internal.R.string.autofill_fax_re // IDS_AUTOFILL_FAX_RE
};
private static Context mApplicationContext;
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 8ffbda2..66fcc27 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -183,7 +183,6 @@
private boolean mJavaScriptCanOpenWindowsAutomatically = false;
private boolean mUseDoubleTree = false;
private boolean mUseWideViewport = false;
- private boolean mUseFixedViewport = false;
private boolean mSupportMultipleWindows = false;
private boolean mShrinksStandaloneImagesToFit = false;
private long mMaximumDecodedImageSize = 0; // 0 means default
@@ -361,10 +360,9 @@
}
// User agent strings.
- private static final String DESKTOP_USERAGENT =
- "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)"
- + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0"
- + " Safari/530.17";
+ private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (X11; " +
+ "Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) " +
+ "Chrome/11.0.696.34 Safari/534.24";
private static final String IPHONE_USERAGENT =
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
+ " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
@@ -383,13 +381,6 @@
mDefaultTextEncoding = context.getString(com.android.internal.
R.string.default_text_encoding);
- // Detect tablet device for fixed viewport mode.
- final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- final int landscapeWidth = Math.max(metrics.widthPixels, metrics.heightPixels);
- final int minTabletWidth = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.min_xlarge_screen_width);
- mUseFixedViewport = (metrics.density == 1.0f && landscapeWidth >= minTabletWidth);
-
if (sLockForLocaleSettings == null) {
sLockForLocaleSettings = new Object();
sLocale = Locale.getDefault();
@@ -1652,11 +1643,11 @@
}
/**
- * Returns whether to use fixed viewport. Fixed viewport should operate only
- * when wide viewport is on.
+ * Returns whether to use fixed viewport. Use fixed viewport
+ * whenever wide viewport is on.
*/
/* package */ boolean getUseFixedViewport() {
- return getUseWideViewPort() && mUseFixedViewport;
+ return getUseWideViewPort();
}
/**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f774803..61a69ca 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -54,7 +54,9 @@
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
+import android.os.StrictMode;
import android.provider.Settings;
import android.speech.tts.TextToSpeech;
import android.text.Selection;
@@ -8933,15 +8935,14 @@
}
private static void checkThread() {
- if (!"main".equals(Thread.currentThread().getName())) {
- try {
- throw new RuntimeException("A WebView method was called on thread '" +
- Thread.currentThread().getName() + "'. " +
- "All WebView methods must be called on the UI thread. " +
- "Future versions of WebView may not support use on other threads.");
- } catch (RuntimeException e) {
- Log.e(LOGTAG, Log.getStackTraceString(e));
- }
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ RuntimeException exception = new RuntimeException(
+ "A WebView method was called on thread '" +
+ Thread.currentThread().getName() + "'. " +
+ "All WebView methods must be called on the UI thread. " +
+ "Future versions of WebView may not support use on other threads.");
+ Log.e(LOGTAG, Log.getStackTraceString(exception));
+ StrictMode.onWebViewMethodCalledOnWrongThread(exception);
}
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index e8083eb..db5ff54 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1865,7 +1865,7 @@
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;
@@ -2025,7 +2002,8 @@
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mContentSize);
if (draw.mBaseLayer == 0) {
- if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message");
+ mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
return;
}
webkitDraw(draw);
@@ -2439,8 +2417,7 @@
// in zoom overview mode.
tentativeScale = mInitialViewState.mTextWrapScale;
int tentativeViewWidth = Math.round(webViewWidth / tentativeScale);
- int windowWidth = calculateWindowWidth(tentativeViewWidth,
- tentativeViewWidth);
+ int windowWidth = calculateWindowWidth(tentativeViewWidth);
// In viewport setup time, since no content width is known, we assume
// the windowWidth will be the content width, to get a more likely
// zoom overview scale.
@@ -2449,8 +2426,7 @@
// 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/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 86fefaf..6f30452 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -696,7 +696,8 @@
mTotalLength += mDividerHeight;
}
- if (useLargestChild && heightMode == MeasureSpec.AT_MOST) {
+ if (useLargestChild &&
+ (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
@@ -809,6 +810,31 @@
} else {
alternativeMaxWidth = Math.max(alternativeMaxWidth,
weightedMaxWidth);
+
+
+ // We have no limit, so make all weighted views as tall as the largest child.
+ // Children will have already been measured once.
+ if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) {
+ for (int i = 0; i < count; i++) {
+ final View child = getVirtualChildAt(i);
+
+ if (child == null || child.getVisibility() == View.GONE) {
+ continue;
+ }
+
+ final LinearLayout.LayoutParams lp =
+ (LinearLayout.LayoutParams) child.getLayoutParams();
+
+ float childExtra = lp.weight;
+ if (childExtra > 0) {
+ child.measure(
+ MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
+ MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(largestChildHeight,
+ MeasureSpec.EXACTLY));
+ }
+ }
+ }
}
if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
@@ -1044,7 +1070,8 @@
maxHeight = Math.max(maxHeight, ascent + descent);
}
- if (useLargestChild && widthMode == MeasureSpec.AT_MOST) {
+ if (useLargestChild &&
+ (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
@@ -1200,6 +1227,29 @@
}
} else {
alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight);
+
+ // We have no limit, so make all weighted views as wide as the largest child.
+ // Children will have already been measured once.
+ if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) {
+ for (int i = 0; i < count; i++) {
+ final View child = getVirtualChildAt(i);
+
+ if (child == null || child.getVisibility() == View.GONE) {
+ continue;
+ }
+
+ final LinearLayout.LayoutParams lp =
+ (LinearLayout.LayoutParams) child.getLayoutParams();
+
+ float childExtra = lp.weight;
+ if (childExtra > 0) {
+ child.measure(
+ MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
+ MeasureSpec.EXACTLY));
+ }
+ }
+ }
}
if (!allFillParent && heightMode != MeasureSpec.EXACTLY) {
@@ -1331,7 +1381,7 @@
void layoutVertical() {
final int paddingLeft = mPaddingLeft;
- int childTop = mPaddingTop;
+ int childTop;
int childLeft;
// Where right end of child should go
@@ -1346,19 +1396,21 @@
final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
- if (majorGravity != Gravity.TOP) {
- switch (majorGravity) {
- case Gravity.BOTTOM:
- // mTotalLength contains the padding already, we add the top
- // padding to compensate
- childTop = mBottom - mTop + mPaddingTop - mTotalLength;
- break;
+ switch (majorGravity) {
+ case Gravity.BOTTOM:
+ // mTotalLength contains the padding already
+ childTop = mPaddingTop + mBottom - mTop - mTotalLength;
+ break;
- case Gravity.CENTER_VERTICAL:
- childTop += ((mBottom - mTop) - mTotalLength) / 2;
- break;
- }
-
+ // mTotalLength contains the padding already
+ case Gravity.CENTER_VERTICAL:
+ childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2;
+ break;
+
+ case Gravity.TOP:
+ default:
+ childTop = mPaddingTop;
+ break;
}
for (int i = 0; i < count; i++) {
@@ -1376,12 +1428,8 @@
if (gravity < 0) {
gravity = minorGravity;
}
-
- switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- childLeft = paddingLeft + lp.leftMargin;
- break;
+ switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = paddingLeft + ((childSpace - childWidth) / 2)
+ lp.leftMargin - lp.rightMargin;
@@ -1390,11 +1438,13 @@
case Gravity.RIGHT:
childLeft = childRight - childWidth - lp.rightMargin;
break;
+
+ case Gravity.LEFT:
default:
- childLeft = paddingLeft;
+ childLeft = paddingLeft + lp.leftMargin;
break;
}
-
+
if (hasDividerBeforeChildAt(i)) {
childTop += mDividerHeight;
}
@@ -1418,10 +1468,11 @@
* @see #onLayout(boolean, int, int, int, int)
*/
void layoutHorizontal() {
+ final boolean isLayoutRtl = isLayoutRtl();
final int paddingTop = mPaddingTop;
int childTop;
- int childLeft = mPaddingLeft;
+ int childLeft;
// Where bottom of child should go
final int height = mBottom - mTop;
@@ -1440,25 +1491,37 @@
final int[] maxAscent = mMaxAscent;
final int[] maxDescent = mMaxDescent;
- if (majorGravity != Gravity.LEFT) {
- switch (majorGravity) {
- case Gravity.RIGHT:
- // mTotalLength contains the padding already, we add the left
- // padding to compensate
- childLeft = mRight - mLeft + mPaddingLeft - mTotalLength;
- break;
+ switch (majorGravity) {
+ case Gravity.RIGHT:
+ // mTotalLength contains the padding already
+ childLeft = mPaddingLeft + mRight - mLeft - mTotalLength;
+ break;
- case Gravity.CENTER_HORIZONTAL:
- childLeft += ((mRight - mLeft) - mTotalLength) / 2;
- break;
- }
+ case Gravity.CENTER_HORIZONTAL:
+ // mTotalLength contains the padding already
+ childLeft = mPaddingLeft + (mRight - mLeft - mTotalLength) / 2;
+ break;
+
+ case Gravity.LEFT:
+ default:
+ childLeft = mPaddingLeft;
+ break;
+ }
+
+ int start = 0;
+ int dir = 1;
+ //In case of RTL, start drawing from the last child.
+ if (isLayoutRtl) {
+ start = count - 1;
+ dir = -1;
}
for (int i = 0; i < count; i++) {
- final View child = getVirtualChildAt(i);
+ int childIndex = start + dir * i;
+ final View child = getVirtualChildAt(childIndex);
if (child == null) {
- childLeft += measureNullChild(i);
+ childLeft += measureNullChild(childIndex);
} else if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
@@ -1512,7 +1575,7 @@
break;
}
- if (hasDividerBeforeChildAt(i)) {
+ if (hasDividerBeforeChildAt(childIndex)) {
childLeft += mDividerWidth;
}
@@ -1522,7 +1585,7 @@
childLeft += childWidth + lp.rightMargin +
getNextLocationOffset(child);
- i += getChildrenSkipCount(child, i);
+ i += getChildrenSkipCount(child, childIndex);
}
}
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 1e1a043..563fc26 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1171,8 +1171,7 @@
int bottomEdge = displayFrame.bottom;
if (ignoreBottomDecorations) {
Resources res = anchor.getContext().getResources();
- bottomEdge = res.getDisplayMetrics().heightPixels -
- (int) res.getDimension(com.android.internal.R.dimen.screen_margin_bottom);
+ bottomEdge = res.getDisplayMetrics().heightPixels;
}
final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 71c91e1..c4ba7c8 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -116,7 +116,7 @@
private static final int MIN_TIME_BETWEEN_INTERACTION_AND_AUTOADVANCE = 5000;
- private static long MIN_TIME_BETWEEN_SCROLLS = 100;
+ private static final long MIN_TIME_BETWEEN_SCROLLS = 100;
/**
* These variables are all related to the current state of touch interaction
@@ -213,8 +213,7 @@
* Animate the views between different relative indexes within the {@link AdapterViewAnimator}
*/
void transformViewForTransition(int fromIndex, int toIndex, final View view, boolean animate) {
- ObjectAnimator alphaOa = null;
- ObjectAnimator oldAlphaOa = null;
+ ObjectAnimator alphaOa;
if (!animate) {
((StackFrame) view).cancelSliderAnimator();
@@ -1276,13 +1275,11 @@
boolean firstPass = true;
parentRect.set(0, 0, 0, 0);
- int depth = 0;
while (p.getParent() != null && p.getParent() instanceof View
&& !parentRect.contains(globalInvalidateRect)) {
if (!firstPass) {
globalInvalidateRect.offset(p.getLeft() - p.getScrollX(), p.getTop()
- p.getScrollY());
- depth++;
}
firstPass = false;
p = (View) p.getParent();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d58c72b..3875765 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,11 +16,6 @@
package android.widget;
-import com.android.internal.util.FastMath;
-import com.android.internal.widget.EditableInputConnection;
-
-import org.xmlpull.v1.XmlPullParserException;
-
import android.R;
import android.content.ClipData;
import android.content.ClipData.Item;
@@ -129,6 +124,11 @@
import android.view.inputmethod.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/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 1e576ce..183cfbd 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -16,25 +16,27 @@
package com.android.internal.app;
+import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.SubMenuBuilder;
-import com.android.internal.widget.AbsActionBarView;
import com.android.internal.widget.ActionBarContainer;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.ActionBarView;
+import com.android.internal.widget.ScrollingTabContainerView;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
import android.app.ActionBar;
import android.app.Activity;
import android.app.Dialog;
import android.app.FragmentTransaction;
import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.ActionMode;
@@ -43,10 +45,7 @@
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
import android.view.Window;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.HorizontalScrollView;
import android.widget.SpinnerAdapter;
import java.lang.ref.WeakReference;
@@ -71,7 +70,7 @@
private ActionBarContextView mContextView;
private ActionBarContainer mSplitView;
private View mContentView;
- private ViewGroup mExternalTabView;
+ private ScrollingTabContainerView mTabScrollView;
private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
@@ -90,16 +89,17 @@
private static final int INVALID_POSITION = -1;
private int mContextDisplayMode;
+ private boolean mHasEmbeddedTabs;
+ private int mContentHeight;
final Handler mHandler = new Handler();
+ Runnable mTabSelector;
private Animator mCurrentShowAnim;
private Animator mCurrentModeAnim;
private boolean mShowHideAnimationEnabled;
boolean mWasHiddenBeforeMode;
- private static final TimeInterpolator sFadeOutInterpolator = new DecelerateInterpolator();
-
final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -150,21 +150,59 @@
"with a compatible window decor layout");
}
+ mHasEmbeddedTabs = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.action_bar_embed_tabs);
mActionView.setContextView(mContextView);
mContextDisplayMode = mActionView.isSplitActionBar() ?
CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;
- if (!mActionView.hasEmbeddedTabs()) {
- HorizontalScrollView tabScroller = new HorizontalScrollView(mContext);
- ViewGroup tabContainer = mActionView.createTabContainer();
- tabScroller.setHorizontalFadingEdgeEnabled(true);
- tabScroller.addView(tabContainer);
+ TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar);
+ mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
+ a.recycle();
+ }
+
+ public void onConfigurationChanged(Configuration newConfig) {
+ mHasEmbeddedTabs = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.action_bar_embed_tabs);
+
+ // Switch tab layout configuration if needed
+ if (!mHasEmbeddedTabs) {
+ mActionView.setEmbeddedTabView(null);
+ mContainerView.setTabContainer(mTabScrollView);
+ } else {
+ mContainerView.setTabContainer(null);
+ if (mTabScrollView != null) {
+ mTabScrollView.setVisibility(View.VISIBLE);
+ }
+ mActionView.setEmbeddedTabView(mTabScrollView);
+ }
+
+ TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar);
+ mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
+ a.recycle();
+
+ if (mTabScrollView != null) {
+ mTabScrollView.getLayoutParams().height = mContentHeight;
+ mTabScrollView.requestLayout();
+ }
+ }
+
+ private void ensureTabsExist() {
+ if (mTabScrollView != null) {
+ return;
+ }
+
+ ScrollingTabContainerView tabScroller = mActionView.createTabContainer();
+
+ if (mHasEmbeddedTabs) {
+ tabScroller.setVisibility(View.VISIBLE);
+ mActionView.setEmbeddedTabView(tabScroller);
+ } else {
tabScroller.setVisibility(getNavigationMode() == NAVIGATION_MODE_TABS ?
View.VISIBLE : View.GONE);
- mActionView.setExternalTabLayout(tabContainer);
mContainerView.setTabContainer(tabScroller);
- mExternalTabView = tabScroller;
}
+ mTabScrollView = tabScroller;
}
/**
@@ -269,7 +307,7 @@
selectTab(null);
}
mTabs.clear();
- mActionView.removeAllTabs();
+ mTabScrollView.removeAllTabs();
mSavedTabPosition = INVALID_POSITION;
}
@@ -365,7 +403,8 @@
@Override
public void addTab(Tab tab, boolean setSelected) {
- mActionView.addTab(tab, setSelected);
+ ensureTabsExist();
+ mTabScrollView.addTab(tab, setSelected);
configureTab(tab, mTabs.size());
if (setSelected) {
selectTab(tab);
@@ -374,7 +413,8 @@
@Override
public void addTab(Tab tab, int position, boolean setSelected) {
- mActionView.addTab(tab, position, setSelected);
+ ensureTabsExist();
+ mTabScrollView.addTab(tab, position, setSelected);
configureTab(tab, position);
if (setSelected) {
selectTab(tab);
@@ -393,9 +433,14 @@
@Override
public void removeTabAt(int position) {
+ if (mTabScrollView == null) {
+ // No tabs around to remove
+ return;
+ }
+
int selectedTabPosition = mSelectedTab != null
? mSelectedTab.getPosition() : mSavedTabPosition;
- mActionView.removeTabAt(position);
+ mTabScrollView.removeTabAt(position);
TabImpl removedTab = mTabs.remove(position);
if (removedTab != null) {
removedTab.setPosition(-1);
@@ -424,9 +469,10 @@
if (mSelectedTab == tab) {
if (mSelectedTab != null) {
mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans);
+ mTabScrollView.animateToTab(tab.getPosition());
}
} else {
- mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
+ mTabScrollView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
if (mSelectedTab != null) {
mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans);
}
@@ -705,7 +751,9 @@
@Override
public Tab setCustomView(View view) {
mCustomView = view;
- if (mPosition >= 0) mActionView.updateTab(mPosition);
+ if (mPosition >= 0) {
+ mTabScrollView.updateTab(mPosition);
+ }
return this;
}
@@ -736,7 +784,9 @@
@Override
public Tab setIcon(Drawable icon) {
mIcon = icon;
- if (mPosition >= 0) mActionView.updateTab(mPosition);
+ if (mPosition >= 0) {
+ mTabScrollView.updateTab(mPosition);
+ }
return this;
}
@@ -748,7 +798,9 @@
@Override
public Tab setText(CharSequence text) {
mText = text;
- if (mPosition >= 0) mActionView.updateTab(mPosition);
+ if (mPosition >= 0) {
+ mTabScrollView.updateTab(mPosition);
+ }
return this;
}
@@ -818,15 +870,16 @@
mSavedTabPosition = getSelectedNavigationIndex();
selectTab(null);
if (!mActionView.hasEmbeddedTabs()) {
- mExternalTabView.setVisibility(View.GONE);
+ mTabScrollView.setVisibility(View.GONE);
}
break;
}
mActionView.setNavigationMode(mode);
switch (mode) {
case NAVIGATION_MODE_TABS:
+ ensureTabsExist();
if (!mActionView.hasEmbeddedTabs()) {
- mExternalTabView.setVisibility(View.VISIBLE);
+ mTabScrollView.setVisibility(View.VISIBLE);
}
if (mSavedTabPosition != INVALID_POSITION) {
setSelectedNavigationItem(mSavedTabPosition);
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 0f086f6..5e9cd23 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -32,7 +32,6 @@
import 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/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index 290bf08..7b4f216 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -89,7 +89,6 @@
final int childCount = getChildCount();
final int midVertical = (top + bottom) / 2;
final int dividerWidth = getDividerWidth();
- boolean hasOverflow = false;
int overflowWidth = 0;
int nonOverflowWidth = 0;
int nonOverflowCount = 0;
@@ -102,7 +101,6 @@
LayoutParams p = (LayoutParams) v.getLayoutParams();
if (p.isOverflowButton) {
- hasOverflow = true;
overflowWidth = v.getMeasuredWidth();
if (hasDividerBeforeChildAt(i)) {
overflowWidth += dividerWidth;
@@ -125,15 +123,12 @@
}
}
- // Try to center non-overflow items with uniformly spaced padding, including on the edges.
- // Overflow will always pin to the right edge. If there isn't enough room for that,
- // center in the remaining space.
+ // Fill action items from the left. Overflow will always pin to the right edge.
if (nonOverflowWidth <= widthRemaining - overflowWidth) {
widthRemaining -= overflowWidth;
}
- final int spacing = (widthRemaining - nonOverflowWidth) / (nonOverflowCount + 1);
- int startLeft = getPaddingLeft() + overflowWidth + spacing;
+ int startLeft = getPaddingLeft();
for (int i = 0; i < childCount; i++) {
final View v = getChildAt(i);
final LayoutParams lp = (LayoutParams) v.getLayoutParams();
@@ -146,7 +141,7 @@
int height = v.getMeasuredHeight();
int t = midVertical - (height / 2);
v.layout(startLeft, t, startLeft + width, t + height);
- startLeft += width + lp.rightMargin + spacing;
+ startLeft += width + lp.rightMargin;
}
}
diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
index ad773ee..834041f 100644
--- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
@@ -76,6 +76,12 @@
return mParentMenu;
}
+ @Override
+ boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ return super.dispatchMenuItemSelected(menu, item) ||
+ mParentMenu.dispatchMenuItemSelected(menu, item);
+ }
+
public SubMenu setIcon(Drawable icon) {
mItem.setIcon(icon);
return this;
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index f1887eb..ff04735 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -78,7 +78,7 @@
private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.LEFT | Gravity.CENTER_VERTICAL;
- private final int mContentHeight;
+ private int mContentHeight;
private int mNavigationMode;
private int mDisplayOptions = ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP;
@@ -95,8 +95,7 @@
private TextView mSubtitleView;
private Spinner mSpinner;
private LinearLayout mListNavLayout;
- private HorizontalScrollView mTabScrollView;
- private ViewGroup mTabLayout;
+ private ScrollingTabContainerView mTabScrollView;
private View mCustomNavView;
private ProgressBar mProgressView;
private ProgressBar mIndeterminateProgressView;
@@ -122,6 +121,8 @@
private SpinnerAdapter mSpinnerAdapter;
private OnNavigationListener mCallback;
+ private Runnable mTabSelector;
+
private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView parent, View view, int position, long id) {
@@ -199,8 +200,6 @@
mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0);
mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0);
- mIncludeTabs = a.getBoolean(R.styleable.ActionBar_embeddedTabs, true);
-
setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT));
final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
@@ -229,6 +228,12 @@
}
@Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ removeCallbacks(mTabSelector);
+ }
+
+ @Override
public boolean shouldDelayChildPressedState() {
return false;
}
@@ -247,6 +252,11 @@
addView(mIndeterminateProgressView);
}
+ public void setContentHeight(int height) {
+ mContentHeight = height;
+ requestLayout();
+ }
+
public void setSplitActionBar(boolean splitActionBar) {
if (mSplitActionBar != splitActionBar) {
if (mMenuView != null) {
@@ -271,8 +281,9 @@
return mIncludeTabs;
}
- public void setExternalTabLayout(ViewGroup tabLayout) {
- mTabLayout = tabLayout;
+ public void setEmbeddedTabView(ScrollingTabContainerView tabs) {
+ mTabScrollView = tabs;
+ mIncludeTabs = tabs != null;
}
public void setCallback(OnNavigationListener callback) {
@@ -489,7 +500,7 @@
}
break;
case ActionBar.NAVIGATION_MODE_TABS:
- if (mTabScrollView != null) {
+ if (mTabScrollView != null && mIncludeTabs) {
removeView(mTabScrollView);
}
}
@@ -513,8 +524,7 @@
addView(mListNavLayout);
break;
case ActionBar.NAVIGATION_MODE_TABS:
- ensureTabsExist();
- if (mTabScrollView != null) {
+ if (mTabScrollView != null && mIncludeTabs) {
addView(mTabScrollView);
}
break;
@@ -523,24 +533,17 @@
requestLayout();
}
}
-
- private void ensureTabsExist() {
- if (!mIncludeTabs) return;
- if (mTabScrollView == null) {
- mTabScrollView = new HorizontalScrollView(getContext());
- mTabScrollView.setHorizontalFadingEdgeEnabled(true);
- mTabLayout = createTabContainer();
- mTabScrollView.addView(mTabLayout);
- }
- }
-
- public ViewGroup createTabContainer() {
- ViewGroup result = new LinearLayout(getContext(), null,
+ public ScrollingTabContainerView createTabContainer() {
+ final LinearLayout tabLayout = new LinearLayout(getContext(), null,
com.android.internal.R.attr.actionBarTabBarStyle);
- result.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
- mContentHeight));
- return result;
+ tabLayout.setMeasureWithLargestChildEnabled(true);
+ tabLayout.setLayoutParams(new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT, mContentHeight));
+
+ final ScrollingTabContainerView scroller = new ScrollingTabContainerView(mContext);
+ scroller.setTabLayout(tabLayout);
+ return scroller;
}
public void setDropdownAdapter(SpinnerAdapter adapter) {
@@ -574,51 +577,6 @@
return mDisplayOptions;
}
- private TabView createTabView(ActionBar.Tab tab) {
- final TabView tabView = new TabView(getContext(), tab);
- tabView.setFocusable(true);
-
- if (mTabClickListener == null) {
- mTabClickListener = new TabClickListener();
- }
- tabView.setOnClickListener(mTabClickListener);
- return tabView;
- }
-
- public void addTab(ActionBar.Tab tab, boolean setSelected) {
- ensureTabsExist();
- View tabView = createTabView(tab);
- mTabLayout.addView(tabView);
- if (setSelected) {
- tabView.setSelected(true);
- }
- }
-
- public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
- ensureTabsExist();
- final TabView tabView = createTabView(tab);
- mTabLayout.addView(tabView, position);
- if (setSelected) {
- tabView.setSelected(true);
- }
- }
-
- public void updateTab(int position) {
- ((TabView) mTabLayout.getChildAt(position)).update();
- }
-
- public void removeTabAt(int position) {
- if (mTabLayout != null) {
- mTabLayout.removeViewAt(position);
- }
- }
-
- public void removeAllTabs() {
- if (mTabLayout != null) {
- mTabLayout.removeAllViews();
- }
- }
-
@Override
protected LayoutParams generateDefaultLayoutParams() {
// Used by custom nav views if they don't supply layout params. Everything else
@@ -667,15 +625,6 @@
addView(mTitleLayout);
}
- public void setTabSelected(int position) {
- ensureTabsExist();
- final int tabCount = mTabLayout.getChildCount();
- for (int i = 0; i < tabCount; i++) {
- final View child = mTabLayout.getChildAt(i);
- child.setSelected(i == position);
- }
- }
-
public void setContextView(ActionBarContextView view) {
mContextView = view;
}
@@ -948,97 +897,6 @@
}
}
- private static class TabView extends LinearLayout {
- private ActionBar.Tab mTab;
- private TextView mTextView;
- private ImageView mIconView;
- private View mCustomView;
-
- public TabView(Context context, ActionBar.Tab tab) {
- super(context, null, com.android.internal.R.attr.actionBarTabStyle);
- mTab = tab;
-
- update();
-
- setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.MATCH_PARENT, 1));
- }
-
- public void update() {
- final ActionBar.Tab tab = mTab;
- final View custom = tab.getCustomView();
- if (custom != null) {
- addView(custom);
- mCustomView = custom;
- if (mTextView != null) mTextView.setVisibility(GONE);
- if (mIconView != null) {
- mIconView.setVisibility(GONE);
- mIconView.setImageDrawable(null);
- }
- } else {
- if (mCustomView != null) {
- removeView(mCustomView);
- mCustomView = null;
- }
-
- final Drawable icon = tab.getIcon();
- final CharSequence text = tab.getText();
-
- if (icon != null) {
- if (mIconView == null) {
- ImageView iconView = new ImageView(getContext());
- LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT);
- lp.gravity = Gravity.CENTER_VERTICAL;
- iconView.setLayoutParams(lp);
- addView(iconView, 0);
- mIconView = iconView;
- }
- mIconView.setImageDrawable(icon);
- mIconView.setVisibility(VISIBLE);
- } else if (mIconView != null) {
- mIconView.setVisibility(GONE);
- mIconView.setImageDrawable(null);
- }
-
- if (text != null) {
- if (mTextView == null) {
- TextView textView = new TextView(getContext(), null,
- com.android.internal.R.attr.actionBarTabTextStyle);
- textView.setSingleLine();
- textView.setEllipsize(TruncateAt.END);
- LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT);
- lp.gravity = Gravity.CENTER_VERTICAL;
- textView.setLayoutParams(lp);
- addView(textView);
- mTextView = textView;
- }
- mTextView.setText(text);
- mTextView.setVisibility(VISIBLE);
- } else {
- mTextView.setVisibility(GONE);
- }
- }
- }
-
- public ActionBar.Tab getTab() {
- return mTab;
- }
- }
-
- private class TabClickListener implements OnClickListener {
- public void onClick(View view) {
- TabView tabView = (TabView) view;
- tabView.getTab().select();
- final int tabCount = mTabLayout.getChildCount();
- for (int i = 0; i < tabCount; i++) {
- final View child = mTabLayout.getChildAt(i);
- child.setSelected(child == view);
- }
- }
- }
-
private static class HomeView extends FrameLayout {
private View mUpView;
private View mIconView;
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index d789584..bf1c637 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -385,6 +385,7 @@
.append(" ToolMinor=").append(coords.toolMinor, 3)
.append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
.append("deg")
+ .append(" Distance=").append(coords.getAxisValue(MotionEvent.AXIS_DISTANCE), 1)
.append(" VScroll=").append(coords.getAxisValue(MotionEvent.AXIS_VSCROLL), 1)
.append(" HScroll=").append(coords.getAxisValue(MotionEvent.AXIS_HSCROLL), 1)
.append(" ToolType=").append(MotionEvent.toolTypeToString(toolType))
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
new file mode 100644
index 0000000..c7d37f2
--- /dev/null
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget;
+
+import android.app.ActionBar;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils.TruncateAt;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.HorizontalScrollView;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class ScrollingTabContainerView extends HorizontalScrollView {
+ Runnable mTabSelector;
+ private TabClickListener mTabClickListener;
+
+ private LinearLayout mTabLayout;
+
+ int mMaxTabWidth;
+
+ public ScrollingTabContainerView(Context context) {
+ super(context);
+ setHorizontalScrollBarEnabled(false);
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ setFillViewport(widthMode == MeasureSpec.EXACTLY);
+
+ final int childCount = getChildCount();
+ if (childCount > 1 &&
+ (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
+ if (childCount > 2) {
+ mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f);
+ } else {
+ mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;
+ }
+ } else {
+ mMaxTabWidth = -1;
+ }
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ public void setTabSelected(int position) {
+ if (mTabLayout == null) {
+ return;
+ }
+
+ final int tabCount = mTabLayout.getChildCount();
+ for (int i = 0; i < tabCount; i++) {
+ final View child = mTabLayout.getChildAt(i);
+ final boolean isSelected = i == position;
+ child.setSelected(isSelected);
+ if (isSelected) {
+ animateToTab(position);
+ }
+ }
+ }
+
+ public void animateToTab(int position) {
+ final View tabView = mTabLayout.getChildAt(position);
+ if (mTabSelector != null) {
+ removeCallbacks(mTabSelector);
+ }
+ mTabSelector = new Runnable() {
+ public void run() {
+ final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
+ smoothScrollTo(scrollPos, 0);
+ mTabSelector = null;
+ }
+ };
+ post(mTabSelector);
+ }
+
+ public void setTabLayout(LinearLayout tabLayout) {
+ if (mTabLayout != tabLayout) {
+ if (mTabLayout != null) {
+ ((ViewGroup) mTabLayout.getParent()).removeView(mTabLayout);
+ }
+ if (tabLayout != null) {
+ addView(tabLayout);
+ }
+ mTabLayout = tabLayout;
+ }
+ }
+
+ public LinearLayout getTabLayout() {
+ return mTabLayout;
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mTabSelector != null) {
+ removeCallbacks(mTabSelector);
+ }
+ }
+
+ private TabView createTabView(ActionBar.Tab tab) {
+ final TabView tabView = new TabView(getContext(), tab);
+ tabView.setFocusable(true);
+
+ if (mTabClickListener == null) {
+ mTabClickListener = new TabClickListener();
+ }
+ tabView.setOnClickListener(mTabClickListener);
+ return tabView;
+ }
+
+ public void addTab(ActionBar.Tab tab, boolean setSelected) {
+ View tabView = createTabView(tab);
+ mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0,
+ LayoutParams.MATCH_PARENT, 1));
+ if (setSelected) {
+ tabView.setSelected(true);
+ }
+ }
+
+ public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
+ final TabView tabView = createTabView(tab);
+ mTabLayout.addView(tabView, position, new LinearLayout.LayoutParams(
+ 0, LayoutParams.MATCH_PARENT, 1));
+ if (setSelected) {
+ tabView.setSelected(true);
+ }
+ }
+
+ public void updateTab(int position) {
+ ((TabView) mTabLayout.getChildAt(position)).update();
+ }
+
+ public void removeTabAt(int position) {
+ if (mTabLayout != null) {
+ mTabLayout.removeViewAt(position);
+ }
+ }
+
+ public void removeAllTabs() {
+ if (mTabLayout != null) {
+ mTabLayout.removeAllViews();
+ }
+ }
+
+ private class TabView extends LinearLayout {
+ private ActionBar.Tab mTab;
+ private TextView mTextView;
+ private ImageView mIconView;
+ private View mCustomView;
+
+ public TabView(Context context, ActionBar.Tab tab) {
+ super(context, null, com.android.internal.R.attr.actionBarTabStyle);
+ mTab = tab;
+
+ update();
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ // Re-measure if we went beyond our maximum size.
+ if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) {
+ super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY),
+ heightMeasureSpec);
+ }
+ }
+
+ public void update() {
+ final ActionBar.Tab tab = mTab;
+ final View custom = tab.getCustomView();
+ if (custom != null) {
+ addView(custom);
+ mCustomView = custom;
+ if (mTextView != null) mTextView.setVisibility(GONE);
+ if (mIconView != null) {
+ mIconView.setVisibility(GONE);
+ mIconView.setImageDrawable(null);
+ }
+ } else {
+ if (mCustomView != null) {
+ removeView(mCustomView);
+ mCustomView = null;
+ }
+
+ final Drawable icon = tab.getIcon();
+ final CharSequence text = tab.getText();
+
+ if (icon != null) {
+ if (mIconView == null) {
+ ImageView iconView = new ImageView(getContext());
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ iconView.setLayoutParams(lp);
+ addView(iconView, 0);
+ mIconView = iconView;
+ }
+ mIconView.setImageDrawable(icon);
+ mIconView.setVisibility(VISIBLE);
+ } else if (mIconView != null) {
+ mIconView.setVisibility(GONE);
+ mIconView.setImageDrawable(null);
+ }
+
+ if (text != null) {
+ if (mTextView == null) {
+ TextView textView = new TextView(getContext(), null,
+ com.android.internal.R.attr.actionBarTabTextStyle);
+ textView.setSingleLine();
+ textView.setEllipsize(TruncateAt.END);
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ textView.setLayoutParams(lp);
+ addView(textView);
+ mTextView = textView;
+ }
+ mTextView.setText(text);
+ mTextView.setVisibility(VISIBLE);
+ } else {
+ mTextView.setVisibility(GONE);
+ }
+ }
+ }
+
+ public ActionBar.Tab getTab() {
+ return mTab;
+ }
+ }
+
+ private class TabClickListener implements OnClickListener {
+ public void onClick(View view) {
+ TabView tabView = (TabView) view;
+ tabView.getTab().select();
+ final int tabCount = mTabLayout.getChildCount();
+ for (int i = 0; i < tabCount; i++) {
+ final View child = mTabLayout.getChildAt(i);
+ child.setSelected(child == view);
+ }
+ }
+ }
+}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index b787e9f..e610640 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -808,8 +808,11 @@
* 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/android_os_ParcelFileDescriptor.cpp b/core/jni/android_os_ParcelFileDescriptor.cpp
index 4ec131c..99a2d04 100644
--- a/core/jni/android_os_ParcelFileDescriptor.cpp
+++ b/core/jni/android_os_ParcelFileDescriptor.cpp
@@ -34,20 +34,37 @@
jfieldID mFileDescriptor;
} gParcelFileDescriptorOffsets;
-static int android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env,
+static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromFd(JNIEnv* env,
+ jobject clazz, jint origfd)
+{
+ int fd = dup(origfd);
+ if (fd < 0) {
+ jniThrowException(env, "java/io/IOException", strerror(errno));
+ return NULL;
+ }
+ return jniCreateFileDescriptor(env, fd);
+}
+
+static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromFdNoDup(JNIEnv* env,
+ jobject clazz, jint fd)
+{
+ return jniCreateFileDescriptor(env, fd);
+}
+
+static void android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env,
jobject clazz, jobjectArray outFds)
{
int fds[2];
if (pipe(fds) < 0) {
- return -errno;
+ int therr = errno;
+ jniThrowException(env, "java/io/IOException", strerror(therr));
+ return;
}
for (int i=0; i<2; i++) {
jobject fdObj = jniCreateFileDescriptor(env, fds[i]);
env->SetObjectArrayElement(outFds, i, fdObj);
}
-
- return 0;
}
static jint getFd(JNIEnv* env, jobject clazz)
@@ -102,7 +119,11 @@
}
static const JNINativeMethod gParcelFileDescriptorMethods[] = {
- {"createPipeNative", "([Ljava/io/FileDescriptor;)I",
+ {"getFileDescriptorFromFd", "(I)Ljava/io/FileDescriptor;",
+ (void*)android_os_ParcelFileDescriptor_getFileDescriptorFromFd},
+ {"getFileDescriptorFromFdNoDup", "(I)Ljava/io/FileDescriptor;",
+ (void*)android_os_ParcelFileDescriptor_getFileDescriptorFromFdNoDup},
+ {"createPipeNative", "([Ljava/io/FileDescriptor;)V",
(void*)android_os_ParcelFileDescriptor_createPipeNative},
{"getStatSize", "()J",
(void*)android_os_ParcelFileDescriptor_getStatSize},
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index dced1a5..59b97c2 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -351,7 +351,7 @@
{
DBusMessage *msg, *reply;
DBusError err;
- bool oob = TRUE;
+ dbus_bool_t oob = TRUE;
if (!dbus_connection_register_object_path(nat->conn, agent_path,
&agent_vtable, nat)) {
diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp
index e627e4a..86fd9cb 100644
--- a/core/jni/com_android_internal_os_ZygoteInit.cpp
+++ b/core/jni/com_android_internal_os_ZygoteInit.cpp
@@ -131,28 +131,6 @@
} 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/drawable-hdpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png
deleted file mode 100644
index 61db22c..0000000
--- a/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png
deleted file mode 100644
index d6478fb..0000000
--- a/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_code_lock_default.png b/core/res/res/drawable-mdpi/btn_code_lock_default.png
old mode 100755
new mode 100644
index f524317..45cc20d
--- a/core/res/res/drawable-mdpi/btn_code_lock_default.png
+++ b/core/res/res/drawable-mdpi/btn_code_lock_default.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_code_lock_touched.png b/core/res/res/drawable-mdpi/btn_code_lock_touched.png
old mode 100755
new mode 100644
index 5cd436c..45cc20d
--- a/core/res/res/drawable-mdpi/btn_code_lock_touched.png
+++ b/core/res/res/drawable-mdpi/btn_code_lock_touched.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png
index 20f3d50..0fbdbfa 100644
--- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png
index d09ce53..ae97453 100644
--- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png
index a9e008c..4127d1e 100644
--- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
index 1ed3065..525ab8a 100644
--- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
index 5710ebf..eb05820 100644
--- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
index dd7d89e..416b2c7 100644
--- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png
index d29c6c3..97ac023 100644
--- a/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png
+++ b/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_charging.png b/core/res/res/drawable-mdpi/ic_lock_idle_charging.png
old mode 100755
new mode 100644
index 20d6320..4210db2
--- a/core/res/res/drawable-mdpi/ic_lock_idle_charging.png
+++ b/core/res/res/drawable-mdpi/ic_lock_idle_charging.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_lock.png b/core/res/res/drawable-mdpi/ic_lock_idle_lock.png
old mode 100755
new mode 100644
index 0206aee..1060f5a
--- a/core/res/res/drawable-mdpi/ic_lock_idle_lock.png
+++ b/core/res/res/drawable-mdpi/ic_lock_idle_lock.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png b/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png
old mode 100755
new mode 100644
index bb96782..72e4afa
--- a/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png
+++ b/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
index 7ddeba5..0bc86c3 100644
--- a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
index 7201e58..2ab4547 100644
--- a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
old mode 100755
new mode 100644
index 8546c5f..fe72d00
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
old mode 100755
new mode 100644
index a98a29a..be666c6
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png
old mode 100755
new mode 100644
index 6d579cb..9627197
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png
deleted file mode 100644
index 6e703af..0000000
--- a/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_delete_holo.png b/core/res/res/drawable-mdpi/sym_keyboard_delete_holo.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/sym_keyboard_delete_holo.png
rename to core/res/res/drawable-mdpi/sym_keyboard_delete_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_shift.png b/core/res/res/drawable-mdpi/sym_keyboard_shift.png
index 0566e5a..91d6e32 100644
--- a/core/res/res/drawable-mdpi/sym_keyboard_shift.png
+++ b/core/res/res/drawable-mdpi/sym_keyboard_shift.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png b/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png
old mode 100755
new mode 100644
index ccaf05d..2bd0536
--- a/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png
+++ b/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_activated_holo_dark.9.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/textfield_bg_activated_holo_dark.9.png
rename to core/res/res/drawable-mdpi/textfield_bg_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_default_holo_dark.9.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/textfield_bg_default_holo_dark.9.png
rename to core/res/res/drawable-mdpi/textfield_bg_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_disabled_focused_holo_dark.9.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_focused_holo_dark.9.png
rename to core/res/res/drawable-mdpi/textfield_bg_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_disabled_holo_dark.9.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_holo_dark.9.png
rename to core/res/res/drawable-mdpi/textfield_bg_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_focused_holo_dark.9.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/textfield_bg_focused_holo_dark.9.png
rename to core/res/res/drawable-mdpi/textfield_bg_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_default.png b/core/res/res/drawable-mdpi/unlock_default.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/unlock_default.png
rename to core/res/res/drawable-mdpi/unlock_default.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_halo.png b/core/res/res/drawable-mdpi/unlock_halo.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/unlock_halo.png
rename to core/res/res/drawable-mdpi/unlock_halo.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_ring.png b/core/res/res/drawable-mdpi/unlock_ring.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/unlock_ring.png
rename to core/res/res/drawable-mdpi/unlock_ring.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_wave.png b/core/res/res/drawable-mdpi/unlock_wave.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/unlock_wave.png
rename to core/res/res/drawable-mdpi/unlock_wave.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png b/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png
deleted file mode 100644
index 45cc20d..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png b/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png
deleted file mode 100644
index 45cc20d..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal.9.png
deleted file mode 100644
index 0fbdbfa..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png
deleted file mode 100644
index ae97453..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png
deleted file mode 100644
index 4127d1e..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
deleted file mode 100644
index 525ab8a..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
deleted file mode 100644
index eb05820..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
deleted file mode 100644
index 416b2c7..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png
deleted file mode 100644
index 97ac023..0000000
--- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png
deleted file mode 100644
index 4210db2..0000000
--- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png
deleted file mode 100644
index 1060f5a..0000000
--- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png
deleted file mode 100644
index 72e4afa..0000000
--- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png
deleted file mode 100644
index 0bc86c3..0000000
--- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png
deleted file mode 100644
index 2ab4547..0000000
--- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png
deleted file mode 100644
index fe72d00..0000000
--- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png
deleted file mode 100644
index be666c6..0000000
--- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png
deleted file mode 100644
index 9627197..0000000
--- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift.png b/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift.png
deleted file mode 100644
index 91d6e32..0000000
--- a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift_locked.png b/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift_locked.png
deleted file mode 100644
index 2bd0536..0000000
--- a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift_locked.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/extract_edit_text.xml b/core/res/res/drawable/extract_edit_text.xml
deleted file mode 100644
index c7f66f6..0000000
--- a/core/res/res/drawable/extract_edit_text.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true" android:drawable="@drawable/keyboard_textfield_selected" />
- <item android:drawable="@drawable/textfield_disabled" />
-</selector>
-
diff --git a/core/res/res/layout-xlarge/keyguard.xml b/core/res/res/layout-sw600dp/keyguard.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard.xml
rename to core/res/res/layout-sw600dp/keyguard.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_glogin_unlock.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_glogin_unlock.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_lock.xml b/core/res/res/layout-sw600dp/keyguard_screen_lock.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_lock.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_lock.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_status_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_status_land.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_status_port.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_status_port.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
similarity index 96%
rename from core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
index 8acb656..e3d7a3f 100644
--- a/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
@@ -47,13 +47,12 @@
<RelativeLayout
android:layout_weight="1"
android:layout_width="0dip"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:gravity="center_vertical|center_horizontal">
<com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
android:layout_width="354dip"
android:layout_height="354dip"
- android:layout_marginLeft="143dip"
- android:layout_marginTop="201dip"
android:layout_gravity="center_vertical"
/>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
diff --git a/core/res/res/layout/input_method_extract_view.xml b/core/res/res/layout/input_method_extract_view.xml
index 689ba7b..7d59d02 100644
--- a/core/res/res/layout/input_method_extract_view.xml
+++ b/core/res/res/layout/input_method_extract_view.xml
@@ -31,7 +31,6 @@
android:gravity="top"
android:minLines="1"
android:inputType="text"
- android:background="@android:drawable/extract_edit_text"
>
</android.inputmethodservice.ExtractEditText>
diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-sw600dp/config.xml
similarity index 99%
rename from core/res/res/values-xlarge/config.xml
rename to core/res/res/values-sw600dp/config.xml
index 4c8bbe6..49ace34 100644
--- a/core/res/res/values-xlarge/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -27,6 +27,7 @@
<!-- Show sliding tab before lockscreen -->
<bool name="config_enableSlidingTabFirst">false</bool>
+
<!-- Enable lockscreen rotation -->
<bool name="config_enableLockScreenRotation">true</bool>
diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml
index e058442..b906e1a 100644
--- a/core/res/res/values-xlarge/dimens.xml
+++ b/core/res/res/values-xlarge/dimens.xml
@@ -25,10 +25,6 @@
<!-- Size of the giant number (unread count) in the notifications -->
<dimen name="status_bar_content_number_size">48sp</dimen>
- <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
- <!-- Margin for permanent screen decorations at the bottom. -->
- <dimen name="screen_margin_bottom">48dip</dimen>
-
<!-- Default height of a key in the password keyboard for alpha -->
<dimen name="password_keyboard_key_height_alpha">75dip</dimen>
<!-- Default height of a key in the password keyboard for numeric -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 6c18089..c1e81c3 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -823,6 +823,10 @@
Default value is false. EditText content is always selectable. -->
<attr name="textIsSelectable" format="boolean" />
+ <!-- When true, IME suggestions will be displayed when the user double taps on editable text.
+ The default value is true. -->
+ <attr name="suggestionsEnabled" format="boolean" />
+
<!-- Where to ellipsize text. -->
<attr name="ellipsize">
<enum name="none" value="0" />
@@ -2877,6 +2881,8 @@
<!-- Indicates that the content of a non-editable text can be selected. -->
<attr name="textIsSelectable" />
+ <!-- Suggestions will be displayed when the user double taps on editable text. -->
+ <attr name="suggestionsEnabled" />
</declare-styleable>
<!-- An <code>input-extras</code> is a container for extra data to supply to
an input method. Contains
@@ -4907,9 +4913,6 @@
<!-- Specifies padding that should be applied to the left and right sides of
system-provided items in the bar. -->
<attr name="itemPadding" format="dimension" />
- <!-- Specifies whether tabs should be embedded within the bar itself (true)
- or displayed elsewhere (false). -->
- <attr name="embeddedTabs" format="boolean" />
</declare-styleable>
<declare-styleable name="ActionMode">
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 39d2329..6529fe1 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -77,6 +77,7 @@
<drawable name="dialog_holo_light_frame">@drawable/dialog_full_holo_light</drawable>
<drawable name="input_method_fullscreen_background">#fff9f9f9</drawable>
+ <drawable name="input_method_fullscreen_background_holo">@drawable/screen_background_holo_dark</drawable>
<!-- For date picker widget -->
<drawable name="selected_day_background">#ff0092f4</drawable>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 8ad8f67..1957b2a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1670,5 +1670,6 @@
<public type="attr" name="horizontalDirection" />
<public type="attr" name="fullBackupAgent" />
+ <public type="attr" name="suggestionsEnabled" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 158d524..816546b 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1852,7 +1852,7 @@
<!-- Do not translate. WebView User Agent string -->
<string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
- AppleWebKit/534.16 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.16</string>
+ AppleWebKit/534.20 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.20</string>
<!-- Do not translate. WebView User Agent targeted content -->
<string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
@@ -1998,6 +1998,9 @@
<!-- Do not translate. Regex used by AutoFill. -->
<string name="autofill_card_ignored_re">^card</string>
+ <!-- Do not translate. Regex used by AutoFill. -->
+ <string name="autofill_fax_re">fax<!-- fr-FR -->|télécopie|telecopie<!-- ja-JP -->|ファックス<!-- ru -->|факс<!-- zh-CN -->|传真<!-- zh-TW -->|傳真</string>
+
<!-- Title of an application permission, listed so the user can choose whether
they want to allow the application to do this. -->
<string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string>
@@ -2449,6 +2452,20 @@
<!-- See SMS_DIALOG. This is a button choice to disallow sending the SMSes.. -->
<string name="sms_control_no">Cancel</string>
+ <!-- SIM swap and device reboot Dialog --> <skip />
+ <!-- See SIM_REMOVED_DIALOG. This is the title of that dialog. -->
+ <string name="sim_removed_title">SIM card removed</string>
+ <!-- See SIM_REMOVED_DIALOG. This is the message of that dialog. -->
+ <string name="sim_removed_message">The mobile network will be unavailable until you replace the SIM card.</string>
+ <!-- See SIM_REMOVED_DIALOG. This is the button of that dialog. -->
+ <string name="sim_done_button">Done</string>
+ <!-- See SIM_ADDED_DIALOG. This is the title of that dialog. -->
+ <string name="sim_added_title">SIM card added</string>
+ <!-- See SIM_ADDED_DIALOG. This is the message of that dialog. -->
+ <string name="sim_added_message">You must restart your device to access the mobile network.</string>
+ <!-- See SIM_ADDED_DIALOG. This is the button of that dialog. -->
+ <string name="sim_restart_button">Restart</string>
+
<!-- Date/Time picker dialogs strings -->
<!-- The title of the time picker dialog. [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 26b5d95..e95094f0 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1078,7 +1078,6 @@
<item name="android:progressBarStyle">@android:style/Widget.ProgressBar.Horizontal</item>
<item name="android:indeterminateProgressStyle">@android:style/Widget.ProgressBar.Small</item>
<item name="android:homeLayout">@android:layout/action_bar_home</item>
- <item name="android:embeddedTabs">@android:bool/action_bar_embed_tabs</item>
</style>
<style name="Widget.ActionMode">
@@ -1122,6 +1121,7 @@
</style>
<style name="Widget.ActionBarView_TabView">
+ <item name="android:gravity">center_horizontal</item>
<item name="android:background">@drawable/minitab_lt</item>
<item name="android:paddingLeft">4dip</item>
<item name="android:paddingRight">4dip</item>
@@ -1795,6 +1795,9 @@
</style>
<style name="Widget.Holo.ActionBarView_TabBar" parent="Widget.ActionBarView_TabBar">
+ <item name="android:divider">?android:attr/dividerVertical</item>
+ <item name="android:showDividers">middle</item>
+ <item name="android:dividerPadding">8dip</item>
</style>
<style name="Widget.Holo.ActionBarView_TabText" parent="Widget.ActionBarView_TabText">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index aa9ddff..0a614b2 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -681,7 +681,7 @@
and a few custom attributes. -->
<style name="Theme.Holo.InputMethod" parent="Theme.Holo.Panel">
<item name="android:windowAnimationStyle">@android:style/Animation.InputMethod</item>
- <item name="android:imeFullscreenBackground">@android:drawable/input_method_fullscreen_background</item>
+ <item name="android:imeFullscreenBackground">@android:drawable/input_method_fullscreen_background_holo</item>
<item name="android:imeExtractEnterAnimation">@android:anim/input_method_extract_enter</item>
<item name="android:imeExtractExitAnimation">@android:anim/input_method_extract_exit</item>
</style>
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
new file mode 100644
index 0000000..45719c2
--- /dev/null
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+@SmallTest
+public class NetworkStatsTest extends TestCase {
+
+ private static final String TEST_IFACE = "test0";
+
+ public void testFindIndex() throws Exception {
+ final NetworkStats stats = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024)
+ .addEntry(TEST_IFACE, 102, 1024, 1024).build();
+
+ assertEquals(2, stats.findIndex(TEST_IFACE, 102));
+ assertEquals(2, stats.findIndex(TEST_IFACE, 102));
+ assertEquals(0, stats.findIndex(TEST_IFACE, 100));
+ assertEquals(-1, stats.findIndex(TEST_IFACE, 6));
+ }
+
+ public void testSubtractIdenticalData() throws Exception {
+ final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+ final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+ final NetworkStats result = after.subtract(before);
+
+ assertEquals(0, result.rx[0]);
+ assertEquals(0, result.tx[0]);
+ assertEquals(0, result.rx[1]);
+ assertEquals(0, result.tx[1]);
+ }
+
+ public void testSubtractIdenticalRows() throws Exception {
+ final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+ final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1025, 2)
+ .addEntry(TEST_IFACE, 101, 3, 1028).build();
+
+ final NetworkStats result = after.subtract(before);
+
+ // expect delta between measurements
+ assertEquals(1, result.rx[0]);
+ assertEquals(2, result.tx[0]);
+ assertEquals(3, result.rx[1]);
+ assertEquals(4, result.tx[1]);
+ }
+
+ public void testSubtractNewRows() throws Exception {
+ final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+ final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024)
+ .addEntry(TEST_IFACE, 102, 1024, 1024).build();
+
+ final NetworkStats result = after.subtract(before);
+
+ // its okay to have new rows
+ assertEquals(0, result.rx[0]);
+ assertEquals(0, result.tx[0]);
+ assertEquals(0, result.rx[1]);
+ assertEquals(0, result.tx[1]);
+ assertEquals(1024, result.rx[2]);
+ assertEquals(1024, result.tx[2]);
+ }
+
+}
diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h
index b02a057..32cd4f5 100644
--- a/include/android_runtime/AndroidRuntime.h
+++ b/include/android_runtime/AndroidRuntime.h
@@ -39,6 +39,13 @@
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/keystore/java/android/security/IKeyChainAliasResponse.aidl b/keystore/java/android/security/IKeyChainAliasResponse.aidl
new file mode 100644
index 0000000..e042001
--- /dev/null
+++ b/keystore/java/android/security/IKeyChainAliasResponse.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security;
+
+/**
+ * Used by the {@code KeyChainActivity} to return alias for {@link KeyStore#chooseAlias}.
+ *
+ * @hide
+ */
+interface IKeyChainAliasResponse {
+
+ void alias(String alias);
+}
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index be59f23..2763e46 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -15,8 +15,6 @@
*/
package android.security;
-import android.os.Bundle;
-
/**
* Caller is required to ensure that {@link KeyStore#unlock
* KeyStore.unlock} was successful.
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 08e05ef..ec820cf 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -17,9 +17,11 @@
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
+import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -56,64 +58,123 @@
public static final String ACCOUNT_TYPE = "com.android.keychain";
/**
- * Returns an {@code Intent} for use with {@link
- * android.app.Activity#startActivityForResult
- * startActivityForResult}. The result will be returned via {@link
- * android.app.Activity#onActivityResult onActivityResult} with
- * {@link android.app.Activity#RESULT_OK RESULT_OK} and the alias
- * in the returned {@code Intent}'s extra data with key {@link
- * android.content.Intent#EXTRA_TEXT Intent.EXTRA_TEXT}.
+ * @hide Also used by KeyChainActivity implementation
*/
- public static Intent chooseAlias() {
- return new Intent("com.android.keychain.CHOOSER");
+ public static final String EXTRA_RESPONSE = "response";
+
+ /**
+ * Launches an {@code Activity} for the user to select the alias
+ * for a private key and certificate pair for authentication. The
+ * selected alias or null will be returned via the
+ * IKeyChainAliasResponse callback.
+ */
+ public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasResponse response) {
+ if (activity == null) {
+ throw new NullPointerException("activity == null");
+ }
+ if (response == null) {
+ throw new NullPointerException("response == null");
+ }
+ Intent intent = new Intent("com.android.keychain.CHOOSER");
+ intent.putExtra(EXTRA_RESPONSE, new AliasResponse(activity, response));
+ activity.startActivity(intent);
+ }
+
+ private static class AliasResponse extends IKeyChainAliasResponse.Stub {
+ private final Activity activity;
+ private final KeyChainAliasResponse keyChainAliasResponse;
+ private AliasResponse(Activity activity, KeyChainAliasResponse keyChainAliasResponse) {
+ this.activity = activity;
+ this.keyChainAliasResponse = keyChainAliasResponse;
+ }
+ @Override public void alias(String alias) {
+ if (alias == null) {
+ keyChainAliasResponse.alias(null);
+ return;
+ }
+ AccountManager accountManager = AccountManager.get(activity);
+ accountManager.getAuthToken(getAccount(activity),
+ alias,
+ null,
+ activity,
+ new AliasAccountManagerCallback(keyChainAliasResponse,
+ alias),
+ null);
+ }
+ }
+
+ private static class AliasAccountManagerCallback implements AccountManagerCallback<Bundle> {
+ private final KeyChainAliasResponse keyChainAliasResponse;
+ private final String alias;
+ private AliasAccountManagerCallback(KeyChainAliasResponse keyChainAliasResponse,
+ String alias) {
+ this.keyChainAliasResponse = keyChainAliasResponse;
+ this.alias = alias;
+ }
+ @Override public void run(AccountManagerFuture<Bundle> future) {
+ Bundle bundle;
+ try {
+ bundle = future.getResult();
+ } catch (OperationCanceledException e) {
+ keyChainAliasResponse.alias(null);
+ return;
+ } catch (IOException e) {
+ keyChainAliasResponse.alias(null);
+ return;
+ } catch (AuthenticatorException e) {
+ keyChainAliasResponse.alias(null);
+ return;
+ }
+ String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
+ if (authToken != null) {
+ keyChainAliasResponse.alias(alias);
+ } else {
+ keyChainAliasResponse.alias(null);
+ }
+ }
}
/**
- * Returns a new {@code KeyChainResult} instance.
+ * Returns the {@code PrivateKey} for the requested alias, or null
+ * if no there is no result.
*/
- public static KeyChainResult get(Context context, String alias)
+ public static PrivateKey getPrivateKey(Context context, String alias)
throws InterruptedException, RemoteException {
if (alias == null) {
throw new NullPointerException("alias == null");
}
KeyChainConnection keyChainConnection = bind(context);
try {
- // Account is created if necessary during binding of the IKeyChainService
- AccountManager accountManager = AccountManager.get(context);
- Account account = accountManager.getAccountsByType(ACCOUNT_TYPE)[0];
- AccountManagerFuture<Bundle> future = accountManager.getAuthToken(account,
- alias,
- false,
- null,
- null);
- Bundle bundle;
- try {
- bundle = future.getResult();
- } catch (OperationCanceledException e) {
- throw new AssertionError(e);
- } catch (IOException e) {
- throw new AssertionError(e);
- } catch (AuthenticatorException e) {
- throw new AssertionError(e);
- }
- Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
- if (intent != null) {
- Bundle result = new Bundle();
- // we don't want this Eclair compatability flag,
- // it will prevent onActivityResult from being called
- intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
- return new KeyChainResult(intent);
- }
-
- String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
+ String authToken = authToken(context, alias);
if (authToken == null) {
- throw new AssertionError("Invalid authtoken");
+ return null;
}
IKeyChainService keyChainService = keyChainConnection.getService();
byte[] privateKeyBytes = keyChainService.getPrivateKey(alias, authToken);
+ return toPrivateKey(privateKeyBytes);
+ } finally {
+ keyChainConnection.close();
+ }
+ }
+
+ /**
+ * Returns the {@code X509Certificate} chain for the requested
+ * alias, or null if no there is no result.
+ */
+ public static X509Certificate[] getCertificateChain(Context context, String alias)
+ throws InterruptedException, RemoteException {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+ KeyChainConnection keyChainConnection = bind(context);
+ try {
+ String authToken = authToken(context, alias);
+ if (authToken == null) {
+ return null;
+ }
+ IKeyChainService keyChainService = keyChainConnection.getService();
byte[] certificateBytes = keyChainService.getCertificate(alias, authToken);
- return new KeyChainResult(toPrivateKey(privateKeyBytes),
- toCertificate(certificateBytes));
+ return new X509Certificate[] { toCertificate(certificateBytes) };
} finally {
keyChainConnection.close();
}
@@ -146,6 +207,50 @@
}
}
+ private static String authToken(Context context, String alias) {
+ AccountManager accountManager = AccountManager.get(context);
+ AccountManagerFuture<Bundle> future = accountManager.getAuthToken(getAccount(context),
+ alias,
+ false,
+ null,
+ null);
+ Bundle bundle;
+ try {
+ bundle = future.getResult();
+ } catch (OperationCanceledException e) {
+ throw new AssertionError(e);
+ } catch (IOException e) {
+ // KeyChainAccountAuthenticator doesn't do I/O
+ throw new AssertionError(e);
+ } catch (AuthenticatorException e) {
+ throw new AssertionError(e);
+ }
+ Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
+ if (intent != null) {
+ return null;
+ }
+ String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
+ if (authToken == null) {
+ throw new AssertionError("Invalid authtoken");
+ }
+ return authToken;
+ }
+
+ private static Account getAccount(Context context) {
+ AccountManager accountManager = AccountManager.get(context);
+ Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
+ if (accounts.length == 0) {
+ try {
+ // Account is created if necessary during binding of the IKeyChainService
+ bind(context).close();
+ } catch (InterruptedException e) {
+ throw new AssertionError(e);
+ }
+ accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
+ }
+ return accounts[0];
+ }
+
/**
* @hide for reuse by CertInstaller and Settings.
* @see KeyChain#bind
diff --git a/keystore/java/android/security/KeyChainAliasResponse.java b/keystore/java/android/security/KeyChainAliasResponse.java
new file mode 100644
index 0000000..bcca123
--- /dev/null
+++ b/keystore/java/android/security/KeyChainAliasResponse.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security;
+
+import android.content.Intent;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+/**
+ * The KeyChainAliasResponse is the callback for {@link
+ * KeyChain#chooseAlias}.
+ *
+ * @hide
+ */
+public interface KeyChainAliasResponse {
+
+ /**
+ * Called with the alias of the certificate chosen by the user, or
+ * null if no value was chosen.
+ */
+ public void alias(String alias);
+}
diff --git a/keystore/java/android/security/KeyChainResult.java b/keystore/java/android/security/KeyChainResult.java
deleted file mode 100644
index 85a2921..0000000
--- a/keystore/java/android/security/KeyChainResult.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.security;
-
-import android.content.Intent;
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-
-/**
- * The KeyChainResult is the complex result value from {@link
- * KeyChain#get}. The caller should first inspect {@link #getIntent}
- * to determine if the user needs to grant the application access to
- * the protected contents. If {@code getIntent} returns null, access
- * has been granted and the methods {@link #getPrivateKey} and {@link
- * #getCertificate} can be used to access the credentials.
- *
- * @hide
- */
-public final class KeyChainResult {
-
- private final Intent intent;
- private final PrivateKey privateKey;
- private final X509Certificate certificate;
-
- KeyChainResult(Intent intent) {
- this(intent, null, null);
- }
-
- KeyChainResult(PrivateKey privateKey, X509Certificate certificate) {
- this(null, privateKey, certificate);
- }
-
- private KeyChainResult(Intent intent, PrivateKey privateKey, X509Certificate certificate) {
- this.intent = intent;
- this.privateKey = privateKey;
- this.certificate = certificate;
- }
-
- public Intent getIntent() {
- return intent;
- }
-
- public PrivateKey getPrivateKey() {
- checkIntent();
- return privateKey;
- }
-
- public X509Certificate getCertificate() {
- checkIntent();
- return certificate;
- }
-
- private void checkIntent() {
- if (intent != null) {
- throw new IllegalStateException("non-null Intent, check getIntent()");
- }
- }
-
-}
diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp
index 9f55a71..dd0052a 100644
--- a/libs/utils/RefBase.cpp
+++ b/libs/utils/RefBase.cpp
@@ -419,7 +419,8 @@
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
if (impl->mStrong == INITIAL_STRONG_VALUE)
- delete impl->mBase;
+ if (impl->mBase)
+ impl->mBase->destroy();
else {
// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
delete impl;
@@ -427,7 +428,8 @@
} else {
impl->mBase->onLastWeakRef(id);
if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
- delete impl->mBase;
+ if (impl->mBase)
+ impl->mBase->destroy();
}
}
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e1daede..253010c 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -828,29 +828,64 @@
* or {@link #SCO_AUDIO_STATE_CONNECTED}
*
* @see #startBluetoothSco()
+ * @deprecated Use {@link #ACTION_SCO_AUDIO_STATE_UPDATED} instead
*/
+ @Deprecated
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_SCO_AUDIO_STATE_CHANGED =
"android.media.SCO_AUDIO_STATE_CHANGED";
+
+ /**
+ * Sticky broadcast intent action indicating that the bluetoooth SCO audio
+ * connection state has been updated.
+ * <p>This intent has two extras:
+ * <ul>
+ * <li> {@link #EXTRA_SCO_AUDIO_STATE} - The new SCO audio state. </li>
+ * <li> {@link #EXTRA_SCO_AUDIO_PREVIOUS_STATE}- The previous SCO audio state. </li>
+ * </ul>
+ * <p> EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE can be any of:
+ * <ul>
+ * <li> {@link #SCO_AUDIO_STATE_DISCONNECTED}, </li>
+ * <li> {@link #SCO_AUDIO_STATE_CONNECTING} or </li>
+ * <li> {@link #SCO_AUDIO_STATE_CONNECTED}, </li>
+ * </ul>
+ * @see #startBluetoothSco()
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SCO_AUDIO_STATE_UPDATED =
+ "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
+
/**
- * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} containing the new
- * bluetooth SCO connection state.
+ * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} or
+ * {@link #ACTION_SCO_AUDIO_STATE_UPDATED} containing the new bluetooth SCO connection state.
*/
public static final String EXTRA_SCO_AUDIO_STATE =
"android.media.extra.SCO_AUDIO_STATE";
/**
- * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that the
- * SCO audio channel is not established
+ * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED} containing the previous
+ * bluetooth SCO connection state.
+ */
+ public static final String EXTRA_SCO_AUDIO_PREVIOUS_STATE =
+ "android.media.extra.SCO_AUDIO_PREVIOUS_STATE";
+
+ /**
+ * Value for extra EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE
+ * indicating that the SCO audio channel is not established
*/
public static final int SCO_AUDIO_STATE_DISCONNECTED = 0;
/**
- * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that the
- * SCO audio channel is established
+ * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} or {@link #EXTRA_SCO_AUDIO_PREVIOUS_STATE}
+ * indicating that the SCO audio channel is established
*/
public static final int SCO_AUDIO_STATE_CONNECTED = 1;
/**
- * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that
+ * Value for extra EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE
+ * indicating that the SCO audio channel is being established
+ */
+ public static final int SCO_AUDIO_STATE_CONNECTING = 2;
+ /**
+ * Value for extra EXTRA_SCO_AUDIO_STATE indicating that
* there was an error trying to obtain the state
*/
public static final int SCO_AUDIO_STATE_ERROR = -1;
@@ -878,29 +913,37 @@
* to/from a bluetooth SCO headset while the phone is not in call.
* <p>As the SCO connection establishment can take several seconds,
* applications should not rely on the connection to be available when the method
- * returns but instead register to receive the intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED}
+ * returns but instead register to receive the intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED}
* and wait for the state to be {@link #SCO_AUDIO_STATE_CONNECTED}.
- * <p>As the connection is not guaranteed to succeed, applications must wait for this intent with
- * a timeout.
- * <p>When finished with the SCO connection or if the establishment times out,
- * the application must call {@link #stopBluetoothSco()} to clear the request and turn
- * down the bluetooth connection.
+ * <p>As the ACTION_SCO_AUDIO_STATE_UPDATED intent is sticky, the application can check the SCO
+ * audio state before calling startBluetoothSco() by reading the intent returned by the receiver
+ * registration. If the state is already CONNECTED, no state change will be received via the
+ * intent after calling startBluetoothSco(). It is however useful to call startBluetoothSco()
+ * so that the connection stays active in case the current initiator stops the connection.
+ * <p>Unless the connection is already active as described above, the state will always
+ * transition from DISCONNECTED to CONNECTING and then either to CONNECTED if the connection
+ * succeeds or back to DISCONNECTED if the connection fails (e.g no headset is connected).
+ * <p>When finished with the SCO connection or if the establishment fails, the application must
+ * call {@link #stopBluetoothSco()} to clear the request and turn down the bluetooth connection.
* <p>Even if a SCO connection is established, the following restrictions apply on audio
* output streams so that they can be routed to SCO headset:
- * - the stream type must be {@link #STREAM_VOICE_CALL}
- * - the format must be mono
- * - the sampling must be 16kHz or 8kHz
+ * <ul>
+ * <li> the stream type must be {@link #STREAM_VOICE_CALL} </li>
+ * <li> the format must be mono </li>
+ * <li> the sampling must be 16kHz or 8kHz </li>
+ * </ul>
* <p>The following restrictions apply on input streams:
- * - the format must be mono
- * - the sampling must be 8kHz
- *
+ * <ul>
+ * <li> the format must be mono </li>
+ * <li> the sampling must be 8kHz </li>
+ * </ul>
* <p>Note that the phone application always has the priority on the usage of the SCO
* connection for telephony. If this method is called while the phone is in call
* it will be ignored. Similarly, if a call is received or sent while an application
* is using the SCO connection, the connection will be lost for the application and NOT
* returned automatically when the call ends.
* @see #stopBluetoothSco()
- * @see #ACTION_SCO_AUDIO_STATE_CHANGED
+ * @see #ACTION_SCO_AUDIO_STATE_UPDATED
*/
public void startBluetoothSco(){
IAudioService service = getService();
@@ -917,7 +960,7 @@
* {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
* <p>This method must be called by applications having requested the use of
* bluetooth SCO audio with {@link #startBluetoothSco()}
- * when finished with the SCO connection or if the establishment times out.
+ * when finished with the SCO connection or if connection fails.
* @see #startBluetoothSco()
*/
public void stopBluetoothSco(){
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 504cfde..4e77fcb 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -112,9 +112,12 @@
private static final int MSG_LOAD_SOUND_EFFECTS = 9;
private static final int MSG_SET_FORCE_USE = 10;
private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11;
-
+ private static final int MSG_BT_HEADSET_CNCT_FAILED = 12;
private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
+ // Timeout for connection to bluetooth headset service
+ private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
+
/** @see AudioSystemThread */
private AudioSystemThread mAudioSystemThread;
@@ -273,11 +276,22 @@
private int mScoAudioState;
// SCO audio state is not active
private static final int SCO_STATE_INACTIVE = 0;
+ // SCO audio activation request waiting for headset service to connect
+ private static final int SCO_STATE_ACTIVATE_REQ = 1;
// SCO audio state is active or starting due to a local request to start a virtual call
- private static final int SCO_STATE_ACTIVE_INTERNAL = 1;
+ private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
+ // SCO audio deactivation request waiting for headset service to connect
+ private static final int SCO_STATE_DEACTIVATE_REQ = 5;
+
// SCO audio state is active due to an action in BT handsfree (either voice recognition or
// in call audio)
private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
+ // Deactivation request for all SCO connections (initiated by audio mode change)
+ // waiting for headset service to connect
+ private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
+
+ // Current connection state indicated by bluetooth headset
+ private int mScoConnectionState;
// true if boot sequence has been completed
private boolean mBootCompleted;
@@ -335,13 +349,6 @@
AudioSystem.setErrorCallback(mAudioSystemCallback);
- mBluetoothHeadsetDevice = null;
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null) {
- adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
- BluetoothProfile.HEADSET);
- }
-
// Register for device connection intent broadcasts.
IntentFilter intentFilter =
new IntentFilter(Intent.ACTION_HEADSET_PLUG);
@@ -768,17 +775,7 @@
if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
AudioService.this.mMode = mode;
if (mode != AudioSystem.MODE_NORMAL) {
- synchronized(mScoClients) {
- checkScoAudioState();
- if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
- mBluetoothHeadset.stopVoiceRecognition(
- mBluetoothHeadsetDevice);
- mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
- } else {
- clearAllScoClients(mCb, true);
- }
- }
+ disconnectBluetoothSco(mCb);
}
}
}
@@ -856,16 +853,7 @@
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
// SCO connections not started by the application changing the mode
if (mode != AudioSystem.MODE_NORMAL) {
- synchronized(mScoClients) {
- checkScoAudioState();
- if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
- mBluetoothHeadset.stopVoiceRecognition(mBluetoothHeadsetDevice);
- mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
- } else {
- clearAllScoClients(cb, true);
- }
- }
+ disconnectBluetoothSco(cb);
}
}
}
@@ -1213,7 +1201,8 @@
/** @see AudioManager#startBluetoothSco() */
public void startBluetoothSco(IBinder cb){
- if (!checkAudioSettingsPermission("startBluetoothSco()")) {
+ if (!checkAudioSettingsPermission("startBluetoothSco()") ||
+ !mBootCompleted) {
return;
}
ScoClient client = getScoClient(cb, true);
@@ -1222,7 +1211,8 @@
/** @see AudioManager#stopBluetoothSco() */
public void stopBluetoothSco(IBinder cb){
- if (!checkAudioSettingsPermission("stopBluetoothSco()")) {
+ if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
+ !mBootCompleted) {
return;
}
ScoClient client = getScoClient(cb, false);
@@ -1322,25 +1312,57 @@
}
private void requestScoState(int state) {
- if (mBluetoothHeadset == null) {
- return;
- }
-
checkScoAudioState();
-
- if (totalCount() == 0 &&
- mBluetoothHeadsetDevice != null) {
- // Accept SCO audio activation only in NORMAL audio mode or if the mode is
- // currently controlled by the same client.
- if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL ||
- mSetModeDeathHandlers.get(0).getBinder() == mCb) &&
- state == BluetoothHeadset.STATE_AUDIO_CONNECTED &&
- mScoAudioState == SCO_STATE_INACTIVE) {
- mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- mBluetoothHeadset.startScoUsingVirtualVoiceCall(mBluetoothHeadsetDevice);
+ if (totalCount() == 0) {
+ if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+ // Make sure that the state transitions to CONNECTING even if we cannot initiate
+ // the connection.
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
+ // Accept SCO audio activation only in NORMAL audio mode or if the mode is
+ // currently controlled by the same client.
+ if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL ||
+ mSetModeDeathHandlers.get(0).getBinder() == mCb) &&
+ mBluetoothHeadsetDevice != null &&
+ (mScoAudioState == SCO_STATE_INACTIVE ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
+ if (mScoAudioState == SCO_STATE_INACTIVE) {
+ if (mBluetoothHeadset != null) {
+ if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice)) {
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ } else {
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
+ } else if (getBluetoothHeadset()) {
+ mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+ }
+ } else {
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+ }
+ } else {
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
- mScoAudioState == SCO_STATE_ACTIVE_INTERNAL){
- mBluetoothHeadset.stopScoUsingVirtualVoiceCall(mBluetoothHeadsetDevice);
+ mBluetoothHeadsetDevice != null &&
+ (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
+ mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
+ if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
+ if (mBluetoothHeadset != null) {
+ if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice)) {
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
+ } else if (getBluetoothHeadset()) {
+ mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
+ }
+ } else {
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
}
}
}
@@ -1348,7 +1370,7 @@
private void checkScoAudioState() {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
- mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
+ mScoAudioState == SCO_STATE_INACTIVE &&
mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
!= BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
@@ -1391,10 +1413,72 @@
}
}
+ private boolean getBluetoothHeadset() {
+ boolean result = false;
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+ BluetoothProfile.HEADSET);
+ }
+ // If we could not get a bluetooth headset proxy, send a failure message
+ // without delay to reset the SCO audio state and clear SCO clients.
+ // If we could get a proxy, send a delayed failure message that will reset our state
+ // in case we don't receive onServiceConnected().
+ sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+ SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
+ return result;
+ }
+
+ private void disconnectBluetoothSco(IBinder exceptBinder) {
+ synchronized(mScoClients) {
+ checkScoAudioState();
+ if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
+ if (mBluetoothHeadsetDevice != null) {
+ if (mBluetoothHeadset != null) {
+ if (!mBluetoothHeadset.stopVoiceRecognition(
+ mBluetoothHeadsetDevice) ||
+ !mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice)) {
+ sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+ SENDMSG_REPLACE, 0, 0, null, 0);
+ }
+ } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
+ getBluetoothHeadset()) {
+ mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
+ }
+ }
+ } else {
+ clearAllScoClients(exceptBinder, true);
+ }
+ }
+ }
+
+ private void resetBluetoothSco() {
+ synchronized(mScoClients) {
+ clearAllScoClients(null, false);
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
+ }
+
+ private void broadcastScoConnectionState(int state) {
+ if (state != mScoConnectionState) {
+ Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
+ newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
+ newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
+ mScoConnectionState);
+ mContext.sendStickyBroadcast(newIntent);
+ mScoConnectionState = state;
+ }
+ }
+
private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
synchronized (mScoClients) {
+ // Discard timeout message
+ mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
mBluetoothHeadset = (BluetoothHeadset) proxy;
List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
if (deviceList.size() > 0) {
@@ -1402,19 +1486,41 @@
} else {
mBluetoothHeadsetDevice = null;
}
+ // Refresh SCO audio state
+ checkScoAudioState();
+ // Continue pending action if any
+ if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
+ boolean status = false;
+ if (mBluetoothHeadsetDevice != null) {
+ switch (mScoAudioState) {
+ case SCO_STATE_ACTIVATE_REQ:
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice);
+ break;
+ case SCO_STATE_DEACTIVATE_REQ:
+ status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice);
+ break;
+ case SCO_STATE_DEACTIVATE_EXT_REQ:
+ status = mBluetoothHeadset.stopVoiceRecognition(
+ mBluetoothHeadsetDevice) &&
+ mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice);
+ }
+ }
+ if (!status) {
+ sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+ SENDMSG_REPLACE, 0, 0, null, 0);
+ }
+ }
}
}
public void onServiceDisconnected(int profile) {
synchronized (mScoClients) {
- if (mBluetoothHeadset != null) {
- List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
- if (devices.size() == 0) {
- mBluetoothHeadsetDevice = null;
- clearAllScoClients(null, false);
- mScoAudioState = SCO_STATE_INACTIVE;
- }
- mBluetoothHeadset = null;
- }
+ mBluetoothHeadset = null;
}
}
};
@@ -2041,6 +2147,10 @@
case MSG_PERSIST_MEDIABUTTONRECEIVER:
persistMediaButtonReceiver( (ComponentName) msg.obj );
break;
+
+ case MSG_BT_HEADSET_CNCT_FAILED:
+ resetBluetoothSco();
+ break;
}
}
}
@@ -2241,8 +2351,7 @@
address);
mConnectedDevices.remove(device);
mBluetoothHeadsetDevice = null;
- clearAllScoClients(null, false);
- mScoAudioState = SCO_STATE_INACTIVE;
+ resetBluetoothSco();
} else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
AudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_AVAILABLE,
@@ -2332,26 +2441,34 @@
}
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
boolean broadcast = false;
- int audioState = AudioManager.SCO_AUDIO_STATE_ERROR;
+ int state = AudioManager.SCO_AUDIO_STATE_ERROR;
synchronized (mScoClients) {
int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
- if (!mScoClients.isEmpty() && mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
+ // broadcast intent if the connection was initated by AudioService
+ if (!mScoClients.isEmpty() &&
+ (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
+ mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
broadcast = true;
}
switch (btState) {
case BluetoothHeadset.STATE_AUDIO_CONNECTED:
- audioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
- if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) {
+ state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
+ if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
+ mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
+ mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
}
break;
case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
- audioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
+ state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
mScoAudioState = SCO_STATE_INACTIVE;
clearAllScoClients(null, false);
break;
case BluetoothHeadset.STATE_AUDIO_CONNECTING:
- if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) {
+ if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
+ mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
+ mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
}
default:
@@ -2361,14 +2478,23 @@
}
}
if (broadcast) {
+ broadcastScoConnectionState(state);
+ //FIXME: this is to maintain compatibility with deprecated intent
+ // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
- newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, audioState);
+ newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
mContext.sendStickyBroadcast(newIntent);
}
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
mBootCompleted = true;
sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP,
0, 0, null, 0);
+
+ mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
+ resetBluetoothSco();
+ getBluetoothHeadset();
+ //FIXME: this is to maintain compatibility with deprecated intent
+ // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 84f588e..0e161a8 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -628,9 +628,11 @@
* and cannot be directly compared between different media sources or different
* instances of the same media source, or across multiple runs of the same
* program.
- * @hide
*/
public void setTexture(SurfaceTexture st) {
+ if (mScreenOnWhilePlaying && st != null && mSurfaceTexture == null) {
+ Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for SurfaceTexture");
+ }
mSurfaceHolder = null;
mSurface = null;
mSurfaceTexture = st;
@@ -960,6 +962,9 @@
*/
public void setScreenOnWhilePlaying(boolean screenOn) {
if (mScreenOnWhilePlaying != screenOn) {
+ if (screenOn && mSurfaceTexture != null) {
+ Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for SurfaceTexture");
+ }
mScreenOnWhilePlaying = screenOn;
updateSurfaceScreenOn();
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index fb7a871..70053ea 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -400,6 +400,7 @@
}
void AwesomePlayer::reset() {
+ LOGI("reset");
Mutex::Autolock autoLock(mLock);
reset_l();
}
@@ -413,8 +414,10 @@
Playback::STOP, 0);
mDecryptHandle = NULL;
mDrmManagerClient = NULL;
+ LOGI("DRM manager client stopped");
}
+
if (mFlags & PLAYING) {
uint32_t params = IMediaPlayerService::kBatteryDataTrackDecoder;
if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) {
@@ -447,6 +450,7 @@
mPreparedCondition.wait(mLock);
}
+ LOGI("cancel player events");
cancelPlayerEvents();
mWVMExtractor.clear();
@@ -1081,6 +1085,7 @@
usleep(1000);
}
IPCThreadState::self()->flushCommands();
+ LOGI("video decoder shutdown completed");
}
void AwesomePlayer::setNativeWindow_l(const sp<ANativeWindow> &native) {
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 0f0ffd4..ba495cc 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -3443,7 +3443,7 @@
}
status_t OMXCodec::stop() {
- CODEC_LOGV("stop mState=%d", mState);
+ CODEC_LOGI("stop mState=%d", mState);
Mutex::Autolock autoLock(mLock);
@@ -3505,6 +3505,7 @@
mLeftOverBuffer = NULL;
}
+ CODEC_LOGI("stopping video source");
mSource->stop();
CODEC_LOGI("stopped in state %d", mState);
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
index 83a1eaa..26eda0c 100644
--- a/media/libstagefright/WVMExtractor.cpp
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -45,8 +45,7 @@
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/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp
index 3e4e493..805bd48 100644
--- a/media/libstagefright/chromium_http/support.cpp
+++ b/media/libstagefright/chromium_http/support.cpp
@@ -23,7 +23,7 @@
#include "support.h"
#include "android/net/android_network_library_impl.h"
-#include "base/thread.h"
+#include "base/threading/thread.h"
#include "net/base/cert_verifier.h"
#include "net/base/host_resolver.h"
#include "net/base/ssl_config_service.h"
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index 13e1662..cffbfb5 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -360,10 +360,14 @@
mFramesConfigured = true;
}
- uint32_t timestamp = 0xFFFFFFFF;
+ uint32_t useExtTimestamp = (inHeader->nOffset == 0);
+
+ // decoder deals in ms, OMX in us.
+ uint32_t timestamp =
+ useExtTimestamp ? (inHeader->nTimeStamp + 500) / 1000 : 0xFFFFFFFF;
+
int32_t bufferSize = inHeader->nFilledLen;
- uint32_t useExtTimestamp = 0;
if (PVDecodeVideoFrame(
mHandle, &bitstream, ×tamp, &bufferSize,
&useExtTimestamp,
@@ -379,13 +383,20 @@
return;
}
- outHeader->nTimeStamp = inHeader->nTimeStamp;
+ // decoder deals in ms, OMX in us.
+ outHeader->nTimeStamp = timestamp * 1000;
- inInfo->mOwnedByUs = false;
- inQueue.erase(inQueue.begin());
- inInfo = NULL;
- notifyEmptyBufferDone(inHeader);
- inHeader = NULL;
+ CHECK_LE(bufferSize, inHeader->nFilledLen);
+ inHeader->nOffset += inHeader->nFilledLen - bufferSize;
+ inHeader->nFilledLen = bufferSize;
+
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
++mInputBufferCount;
diff --git a/media/libstagefright/include/SimpleSoftOMXComponent.h b/media/libstagefright/include/SimpleSoftOMXComponent.h
index 2a29a7d..50cd275 100644
--- a/media/libstagefright/include/SimpleSoftOMXComponent.h
+++ b/media/libstagefright/include/SimpleSoftOMXComponent.h
@@ -36,7 +36,7 @@
OMX_PTR appData,
OMX_COMPONENTTYPE **component);
- virtual ~SimpleSoftOMXComponent();
+ virtual void prepareForDestruction();
void onMessageReceived(const sp<AMessage> &msg);
diff --git a/media/libstagefright/include/SoftOMXComponent.h b/media/libstagefright/include/SoftOMXComponent.h
index 053bc22..a808611 100644
--- a/media/libstagefright/include/SoftOMXComponent.h
+++ b/media/libstagefright/include/SoftOMXComponent.h
@@ -38,6 +38,8 @@
void setLibHandle(void *libHandle);
void *libHandle() const;
+ virtual void prepareForDestruction() {}
+
protected:
virtual ~SoftOMXComponent();
diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h
index 62e5aa5..deecd25 100644
--- a/media/libstagefright/include/WVMExtractor.h
+++ b/media/libstagefright/include/WVMExtractor.h
@@ -25,6 +25,15 @@
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/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 179b2a0..f7330f3 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -45,7 +45,11 @@
PRIORITY_AUDIO);
}
-SimpleSoftOMXComponent::~SimpleSoftOMXComponent() {
+void SimpleSoftOMXComponent::prepareForDestruction() {
+ // The looper's queue may still contain messages referencing this
+ // object. Make sure those are flushed before returning so that
+ // a subsequent dlunload() does not pull out the rug from under us.
+
mLooper->unregisterHandler(mHandler->id());
mLooper->stop();
}
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index 6bd6624..04ca39e 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -21,6 +21,7 @@
#include "SoftOMXPlugin.h"
#include "include/SoftOMXComponent.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AString.h>
#include <dlfcn.h>
@@ -126,8 +127,11 @@
(SoftOMXComponent *)
((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+ me->prepareForDestruction();
+
void *libHandle = me->libHandle();
+ CHECK_EQ(me->getStrongCount(), 1);
me->decStrong(this);
me = NULL;
diff --git a/native/include/android/configuration.h b/native/include/android/configuration.h
index 91533c8..39fef21 100644
--- a/native/include/android/configuration.h
+++ b/native/include/android/configuration.h
@@ -77,6 +77,7 @@
ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01,
ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02,
ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03,
+ ACONFIGURATION_UI_MODE_TYPE_TELEVISION = 0x04,
ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
ACONFIGURATION_UI_MODE_NIGHT_NO = 0x1,
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index afefee6..ff45edc 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -14,7 +14,6 @@
EGL/eglApi.cpp \
EGL/trace.cpp \
EGL/getProcAddress.cpp.arm \
- EGL/hooks.cpp \
EGL/Loader.cpp \
#
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index da26229..e94e50e 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -26,11 +26,10 @@
#include <EGL/egl.h>
-#include "hooks.h"
-#include "egl_impl.h"
-
-#include "Loader.h"
+#include "egldefs.h"
#include "glesv2dbg.h"
+#include "hooks.h"
+#include "Loader.h"
// ----------------------------------------------------------------------------
namespace android {
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 31fe306..b11db32 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -33,6 +33,7 @@
#include <utils/String8.h>
+#include "egldefs.h"
#include "egl_impl.h"
#include "egl_tls.h"
#include "glesv2dbg.h"
@@ -278,6 +279,71 @@
return res;
}
+void gl_unimplemented() {
+ LOGE("called unimplemented OpenGL ES API");
+}
+
+// ----------------------------------------------------------------------------
+
+#if USE_FAST_TLS_KEY
+
+// We have a dedicated TLS slot in bionic
+static inline gl_hooks_t const * volatile * get_tls_hooks() {
+ volatile void *tls_base = __get_tls();
+ gl_hooks_t const * volatile * tls_hooks =
+ reinterpret_cast<gl_hooks_t const * volatile *>(tls_base);
+ return tls_hooks;
+}
+
+void setGlThreadSpecific(gl_hooks_t const *value) {
+ gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
+ tls_hooks[TLS_SLOT_OPENGL_API] = value;
+}
+
+gl_hooks_t const* getGlThreadSpecific() {
+ gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
+ gl_hooks_t const* hooks = tls_hooks[TLS_SLOT_OPENGL_API];
+ if (hooks) return hooks;
+ return &gHooksNoContext;
+}
+
+#else
+
+void setGlThreadSpecific(gl_hooks_t const *value) {
+ pthread_setspecific(gGLWrapperKey, value);
+}
+
+gl_hooks_t const* getGlThreadSpecific() {
+ gl_hooks_t const* hooks = static_cast<gl_hooks_t*>(pthread_getspecific(gGLWrapperKey));
+ if (hooks) return hooks;
+ return &gHooksNoContext;
+}
+
+#endif
+
+// ----------------------------------------------------------------------------
+// GL / EGL hooks
+// ----------------------------------------------------------------------------
+
+#undef GL_ENTRY
+#undef EGL_ENTRY
+#define GL_ENTRY(_r, _api, ...) #_api,
+#define EGL_ENTRY(_r, _api, ...) #_api,
+
+char const * const gl_names[] = {
+ #include "entries.in"
+ NULL
+};
+
+char const * const egl_names[] = {
+ #include "egl_entries.in"
+ NULL
+};
+
+#undef GL_ENTRY
+#undef EGL_ENTRY
+
+
// ----------------------------------------------------------------------------
}; // namespace android
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 8c482c3..113595f 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -30,6 +30,7 @@
#include <utils/SortedVector.h>
#include <utils/threads.h>
+#include "egldefs.h"
#include "hooks.h"
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h
index 8b31468..a7989ef 100644
--- a/opengl/libs/EGL/egl_tls.h
+++ b/opengl/libs/EGL/egl_tls.h
@@ -21,7 +21,12 @@
#include <EGL/egl.h>
+#include "egldefs.h"
+#include "hooks.h"
+
+// ----------------------------------------------------------------------------
namespace android {
+// ----------------------------------------------------------------------------
class DbgContext;
@@ -58,6 +63,16 @@
#define setError(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
+// ----------------------------------------------------------------------------
+
+#if EGL_TRACE
+
+extern gl_hooks_t const* getGLTraceThreadSpecific();
+
+#endif
+
+// ----------------------------------------------------------------------------
}; // namespace android
+// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_TLS_H
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
new file mode 100644
index 0000000..107acd9
--- /dev/null
+++ b/opengl/libs/EGL/egldefs.h
@@ -0,0 +1,71 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#ifndef ANDROID_EGLDEFS_H
+#define ANDROID_EGLDEFS_H
+
+#include "hooks.h"
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 4
+
+// EGLDisplay are global, not attached to a given thread
+const unsigned int NUM_DISPLAYS = 1;
+
+enum {
+ IMPL_HARDWARE = 0,
+ IMPL_SOFTWARE,
+ IMPL_NUM_IMPLEMENTATIONS
+};
+
+enum {
+ GLESv1_INDEX = 0,
+ GLESv2_INDEX = 1,
+};
+
+// ----------------------------------------------------------------------------
+
+struct egl_connection_t
+{
+ inline egl_connection_t() : dso(0) { }
+ void * dso;
+ gl_hooks_t * hooks[2];
+ EGLint major;
+ EGLint minor;
+ egl_t egl;
+};
+
+// ----------------------------------------------------------------------------
+
+extern gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS];
+extern gl_hooks_t gHooksNoContext;
+extern pthread_key_t gGLWrapperKey;
+extern "C" void gl_unimplemented();
+
+extern char const * const gl_names[];
+extern char const * const egl_names[];
+
+extern egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+#endif /* ANDROID_EGLDEFS_H */
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index dcf8735c..f89c865 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -20,6 +20,7 @@
#include <cutils/log.h>
+#include "egldefs.h"
#include "hooks.h"
// ----------------------------------------------------------------------------
@@ -34,7 +35,7 @@
#undef GL_EXTENSION_LIST
#undef GET_TLS
-#if defined(__arm__)
+#if USE_FAST_TLS_KEY
#ifdef HAVE_ARM_TLS_REGISTER
#define GET_TLS(reg) \
@@ -77,7 +78,7 @@
#define GL_EXTENSION(_n)
- #warning "eglGetProcAddress() partially supported on this architecture"
+ #warning "eglGetProcAddress() partially supported"
#endif
diff --git a/opengl/libs/EGL/hooks.cpp b/opengl/libs/EGL/hooks.cpp
deleted file mode 100644
index 72ad6b3..0000000
--- a/opengl/libs/EGL/hooks.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- ** Copyright 2009, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-#include <ctype.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include <cutils/log.h>
-
-#include "hooks.h"
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-void gl_unimplemented() {
- LOGE("called unimplemented OpenGL ES API");
-}
-
-
-// ----------------------------------------------------------------------------
-// GL / EGL hooks
-// ----------------------------------------------------------------------------
-
-#undef GL_ENTRY
-#undef EGL_ENTRY
-#define GL_ENTRY(_r, _api, ...) #_api,
-#define EGL_ENTRY(_r, _api, ...) #_api,
-
-char const * const gl_names[] = {
- #include "entries.in"
- NULL
-};
-
-char const * const egl_names[] = {
- #include "egl_entries.in"
- NULL
-};
-
-#undef GL_ENTRY
-#undef EGL_ENTRY
-
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
-
diff --git a/opengl/libs/EGL/trace.cpp b/opengl/libs/EGL/trace.cpp
index f3e101b..0e934e2 100644
--- a/opengl/libs/EGL/trace.cpp
+++ b/opengl/libs/EGL/trace.cpp
@@ -26,6 +26,7 @@
#include <cutils/log.h>
+#include "egl_tls.h"
#include "hooks.h"
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index d24b047..a809316 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -25,27 +25,12 @@
#include "hooks.h"
-#define VERSION_MAJOR 1
-#define VERSION_MINOR 4
-
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
-struct egl_connection_t
-{
- inline egl_connection_t() : dso(0) { }
- void * dso;
- gl_hooks_t * hooks[2];
- EGLint major;
- EGLint minor;
- egl_t egl;
-};
-
EGLAPI EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image);
-extern egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
-
// ----------------------------------------------------------------------------
}; // namespace android
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h
index 812e26d..7ac88cd 100644
--- a/opengl/libs/hooks.h
+++ b/opengl/libs/hooks.h
@@ -54,22 +54,6 @@
namespace android {
// ----------------------------------------------------------------------------
-// EGLDisplay are global, not attached to a given thread
-const unsigned int NUM_DISPLAYS = 1;
-
-enum {
- IMPL_HARDWARE = 0,
- IMPL_SOFTWARE,
- IMPL_NUM_IMPLEMENTATIONS
-};
-
-enum {
- GLESv1_INDEX = 0,
- GLESv2_INDEX = 1,
-};
-
-// ----------------------------------------------------------------------------
-
// GL / EGL hooks
#undef GL_ENTRY
@@ -92,60 +76,8 @@
#undef GL_ENTRY
#undef EGL_ENTRY
-
-// ----------------------------------------------------------------------------
-
-extern gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS];
-extern gl_hooks_t gHooksNoContext;
-extern pthread_key_t gGLWrapperKey;
-extern "C" void gl_unimplemented();
-
-extern char const * const gl_names[];
-extern char const * const egl_names[];
-
-// ----------------------------------------------------------------------------
-
-#if USE_FAST_TLS_KEY
-
-// We have a dedicated TLS slot in bionic
-static inline gl_hooks_t const * volatile * get_tls_hooks() {
- volatile void *tls_base = __get_tls();
- gl_hooks_t const * volatile * tls_hooks =
- reinterpret_cast<gl_hooks_t const * volatile *>(tls_base);
- return tls_hooks;
-}
-
-static inline void setGlThreadSpecific(gl_hooks_t const *value) {
- gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
- tls_hooks[TLS_SLOT_OPENGL_API] = value;
-}
-
-static gl_hooks_t const* getGlThreadSpecific() {
- gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
- gl_hooks_t const* hooks = tls_hooks[TLS_SLOT_OPENGL_API];
- if (hooks) return hooks;
- return &gHooksNoContext;
-}
-
-#else
-
-static inline void setGlThreadSpecific(gl_hooks_t const *value) {
- pthread_setspecific(gGLWrapperKey, value);
-}
-
-static gl_hooks_t const* getGlThreadSpecific() {
- gl_hooks_t const* hooks = static_cast<gl_hooks_t*>(pthread_getspecific(gGLWrapperKey));
- if (hooks) return hooks;
- return &gHooksNoContext;
-}
-
-#endif
-
-#if EGL_TRACE
-
-extern gl_hooks_t const* getGLTraceThreadSpecific();
-
-#endif
+EGLAPI void setGlThreadSpecific(gl_hooks_t const *value);
+EGLAPI gl_hooks_t const* getGlThreadSpecific();
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/packages/SystemUI/res/layout-large/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_input_methods_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_item.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_input_methods_item.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_item.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_input_methods_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_input_methods_panel.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_panel.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_area.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_notification_area.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_notification_panel.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_panel_title.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel_title.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_notification_panel_title.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel_title.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_peek.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_peek.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_notification_peek.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_notification_peek.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_row.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_row.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_notification_row.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_notification_row.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_pocket_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_pocket_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_pocket_panel.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_pocket_panel.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_recent_item.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_recent_panel.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_recent_panel_footer.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel_footer.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_recent_panel_footer.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel_footer.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_settings_view.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_settings_view.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_settings_view.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_settings_view.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_ticker_compat.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_ticker_compat.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_ticker_compat.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_ticker_compat.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_ticker_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_ticker_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_ticker_panel.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_ticker_panel.xml
diff --git a/packages/SystemUI/res/values-xlarge/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
similarity index 100%
rename from packages/SystemUI/res/values-xlarge/styles.xml
rename to packages/SystemUI/res/values-sw600dp/styles.xml
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a6ba1a0..8e86eda 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -87,40 +87,20 @@
static const nsecs_t kWarningThrottle = seconds(5);
-#define AUDIOFLINGER_SECURITY_ENABLED 1
-
// ----------------------------------------------------------------------------
static bool recordingAllowed() {
-#ifndef HAVE_ANDROID_OS
- return true;
-#endif
-#if AUDIOFLINGER_SECURITY_ENABLED
if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
bool ok = checkCallingPermission(String16("android.permission.RECORD_AUDIO"));
if (!ok) LOGE("Request requires android.permission.RECORD_AUDIO");
return ok;
-#else
- if (!checkCallingPermission(String16("android.permission.RECORD_AUDIO")))
- LOGW("WARNING: Need to add android.permission.RECORD_AUDIO to manifest");
- return true;
-#endif
}
static bool settingsAllowed() {
-#ifndef HAVE_ANDROID_OS
- return true;
-#endif
-#if AUDIOFLINGER_SECURITY_ENABLED
if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"));
if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS");
return ok;
-#else
- if (!checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")))
- LOGW("WARNING: Need to add android.permission.MODIFY_AUDIO_SETTINGS to manifest");
- return true;
-#endif
}
// To collect the amplifier usage
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index ff4b11a..af30887 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -101,7 +101,7 @@
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/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index ca2adb4..d2fd04b 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -543,11 +543,7 @@
* active
*/
public NetworkInfo getActiveNetworkInfo() {
- enforceAccessPermission();
- if (mActiveDefaultNetwork != -1) {
- return mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
- }
- return null;
+ return getNetworkInfo(mActiveDefaultNetwork);
}
public NetworkInfo getNetworkInfo(int networkType) {
@@ -579,18 +575,7 @@
* none is active
*/
public LinkProperties getActiveLinkProperties() {
- enforceAccessPermission();
- for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
- if (mNetConfigs[type] == null || !mNetConfigs[type].isDefault()) {
- continue;
- }
- NetworkStateTracker t = mNetTrackers[type];
- NetworkInfo info = t.getNetworkInfo();
- if (info.isConnected()) {
- return t.getLinkProperties();
- }
- }
- return null;
+ return getLinkProperties(mActiveDefaultNetwork);
}
public LinkProperties getLinkProperties(int networkType) {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index ae04b7f..f78dca9 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -163,6 +163,7 @@
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/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 7c613c1..8f179f5 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -16,51 +16,34 @@
package com.android.server;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
import android.content.pm.PackageManager;
-import android.net.NetworkStats;
-import android.net.Uri;
-import android.net.InterfaceConfiguration;
import android.net.INetworkManagementEventObserver;
+import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
+import android.net.NetworkStats;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.os.Binder;
import android.os.INetworkManagementService;
-import android.os.Handler;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
-import java.util.ArrayList;
-import java.util.NoSuchElementException;
-import java.util.StringTokenizer;
-import android.provider.Settings;
-import android.content.ContentResolver;
-import android.database.ContentObserver;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileReader;
import java.io.IOException;
-import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.RandomAccessFile;
-import java.io.Reader;
-import java.lang.IllegalStateException;
-import java.net.InetAddress;
import java.net.Inet4Address;
-import java.net.UnknownHostException;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;
import libcore.io.IoUtils;
@@ -69,14 +52,16 @@
* @hide
*/
class NetworkManagementService extends INetworkManagementService.Stub {
-
- private static final String TAG = "NetworkManagmentService";
+ private static final String TAG = "NetworkManagementService";
private static final boolean DBG = false;
private static final String NETD_TAG = "NetdConnector";
private static final int ADD = 1;
private static final int REMOVE = 2;
+ /** Base path to UID-granularity network statistics. */
+ private static final File PATH_PROC_UID_STAT = new File("/proc/uid_stat");
+
class NetdResponseCode {
public static final int InterfaceListResult = 110;
public static final int TetherInterfaceListResult = 111;
@@ -891,7 +876,7 @@
return -1;
}
- /** {@inheritDoc} */
+ @Override
public NetworkStats getNetworkStatsSummary() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
@@ -909,31 +894,46 @@
return stats.build();
}
- /** {@inheritDoc} */
+ @Override
public NetworkStats getNetworkStatsDetail() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- final File procPath = new File("/proc/uid_stat");
- final String[] knownUids = procPath.list();
+ final String[] knownUids = PATH_PROC_UID_STAT.list();
final NetworkStats.Builder stats = new NetworkStats.Builder(
SystemClock.elapsedRealtime(), knownUids.length);
- // TODO: kernel module will provide interface-level stats in future
- // TODO: migrate these stats to come across netd in bulk, instead of all
- // these individual file reads.
for (String uid : knownUids) {
- final File uidPath = new File(procPath, uid);
- final int rx = readSingleIntFromFile(new File(uidPath, "tcp_rcv"));
- final int tx = readSingleIntFromFile(new File(uidPath, "tcp_snd"));
-
final int uidInt = Integer.parseInt(uid);
- stats.addEntry(NetworkStats.IFACE_ALL, uidInt, rx, tx);
+ collectNetworkStatsDetail(stats, uidInt);
}
return stats.build();
}
+ @Override
+ public NetworkStats getNetworkStatsUidDetail(int uid) {
+ if (Binder.getCallingUid() != uid) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+ }
+
+ final NetworkStats.Builder stats = new NetworkStats.Builder(
+ SystemClock.elapsedRealtime(), 1);
+ collectNetworkStatsDetail(stats, uid);
+ return stats.build();
+ }
+
+ private void collectNetworkStatsDetail(NetworkStats.Builder stats, int uid) {
+ // TODO: kernel module will provide interface-level stats in future
+ // TODO: migrate these stats to come across netd in bulk, instead of all
+ // these individual file reads.
+ final File uidPath = new File(PATH_PROC_UID_STAT, Integer.toString(uid));
+ final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
+ final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
+ stats.addEntry(NetworkStats.IFACE_ALL, uid, rx, tx);
+ }
+
public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
@@ -994,22 +994,17 @@
}
/**
- * Utility method to read a single plain-text {@link Integer} from the given
+ * Utility method to read a single plain-text {@link Long} from the given
* {@link File}, usually from a {@code /proc/} filesystem.
*/
- private static int readSingleIntFromFile(File file) {
- RandomAccessFile f = null;
+ private static long readSingleLongFromFile(File file) {
try {
- f = new RandomAccessFile(file, "r");
- byte[] buffer = new byte[(int) f.length()];
- f.readFully(buffer);
- return Integer.parseInt(new String(buffer).trim());
+ final byte[] buffer = IoUtils.readFileAsByteArray(file.toString());
+ return Long.parseLong(new String(buffer).trim());
} catch (NumberFormatException e) {
return -1;
} catch (IOException e) {
return -1;
- } finally {
- IoUtils.closeQuietly(f);
}
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cd8915d..5355d44 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -18,6 +18,7 @@
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.am.ActivityManagerService;
+import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.pm.PackageManagerService;
import com.android.server.usb.UsbService;
import com.android.server.wm.WindowManagerService;
@@ -119,6 +120,7 @@
LightsService lights = null;
PowerManagerService power = null;
BatteryService battery = null;
+ NetworkPolicyManagerService networkPolicy = null;
ConnectivityService connectivity = null;
IPackageManager pm = null;
Context context = null;
@@ -282,6 +284,15 @@
}
try {
+ Slog.i(TAG, "NetworkPolicy Service");
+ networkPolicy = new NetworkPolicyManagerService(
+ context, ActivityManagerService.self(), power);
+ ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting Connectivity Service", e);
+ }
+
+ try {
Slog.i(TAG, "NetworkManagement Service");
ServiceManager.addService(
Context.NETWORKMANAGEMENT_SERVICE,
@@ -528,6 +539,7 @@
// These are needed to propagate to the runnable below.
final Context contextF = context;
final BatteryService batteryF = battery;
+ final NetworkPolicyManagerService networkPolicyF = networkPolicy;
final ConnectivityService connectivityF = connectivity;
final DockObserver dockF = dock;
final UsbService usbF = usb;
@@ -553,6 +565,7 @@
startSystemUi(contextF);
if (batteryF != null) batteryF.systemReady();
+ if (networkPolicyF != null) networkPolicyF.systemReady();
if (connectivityF != null) connectivityF.systemReady();
if (dockF != null) dockF.systemReady();
if (usbF != null) usbF.systemReady();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5aae539..cf5592c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -43,6 +43,7 @@
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
+import android.app.IProcessObserver;
import android.app.IServiceConnection;
import android.app.IThumbnailReceiver;
import android.app.IThumbnailRetriever;
@@ -752,8 +753,6 @@
*/
final UsageStatsService mUsageStatsService;
- final NetworkPolicyManagerService mNetworkPolicyService;
-
/**
* Current configuration information. HistoryRecord objects are given
* a reference to this object to indicate which configuration they are
@@ -854,14 +853,6 @@
* Current sequence id for process LRU updating.
*/
int mLruSeq = 0;
-
- /**
- * Set to true if the ANDROID_SIMPLE_PROCESS_MANAGEMENT envvar
- * is set, indicating the user wants processes started in such a way
- * that they can use ANDROID_PROCESS_WRAPPER and know what will be
- * running in each process (thus no pre-initialized process, etc).
- */
- boolean mSimpleProcessManagement = false;
/**
* System monitoring: number of processes that died since the last
@@ -885,7 +876,10 @@
final RemoteCallbackList<IActivityWatcher> mWatchers
= new RemoteCallbackList<IActivityWatcher>();
-
+
+ final RemoteCallbackList<IProcessObserver> mProcessObservers
+ = new RemoteCallbackList<IProcessObserver>();
+
/**
* Callback of last caller to {@link #requestPss}.
*/
@@ -1277,16 +1271,15 @@
}
} break;
case DISPATCH_FOREGROUND_ACTIVITIES_CHANGED: {
- // Flag might have changed during dispatch, but it's always
- // consistent since we dispatch for every change.
final ProcessRecord app = (ProcessRecord) msg.obj;
- mNetworkPolicyService.onForegroundActivitiesChanged(
- app.info.uid, app.pid, app.foregroundActivities);
+ final boolean foregroundActivities = msg.arg1 != 0;
+ dispatchForegroundActivitiesChanged(
+ app.pid, app.info.uid, foregroundActivities);
break;
}
case DISPATCH_PROCESS_DIED: {
final ProcessRecord app = (ProcessRecord) msg.obj;
- mNetworkPolicyService.onProcessDied(app.info.uid, app.pid);
+ dispatchProcessDied(app.pid, app.info.uid);
break;
}
}
@@ -1358,7 +1351,6 @@
m.mBatteryStatsService.publish(context);
m.mUsageStatsService.publish(context);
- m.mNetworkPolicyService.publish(context);
synchronized (thr) {
thr.mReady = true;
@@ -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();
@@ -1480,8 +1463,6 @@
mUsageStatsService = new UsageStatsService(new File(
systemDir, "usagestats").toString());
- mNetworkPolicyService = new NetworkPolicyManagerService();
-
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
@@ -1937,8 +1918,7 @@
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
int pid = Process.start("android.app.ActivityThread",
- mSimpleProcessManagement ? app.processName : null, uid, uid,
- gids, debugFlags, null);
+ app.processName, uid, uid, gids, debugFlags, null);
BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
synchronized (bs) {
if (bs.isOnBattery()) {
@@ -2152,6 +2132,36 @@
mWatchers.finishBroadcast();
}
+ private void dispatchForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+ int i = mProcessObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
+ if (observer != null) {
+ try {
+ observer.onForegroundActivitiesChanged(pid, uid, foregroundActivities);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mProcessObservers.finishBroadcast();
+ }
+
+ private void dispatchProcessDied(int pid, int uid) {
+ int i = mProcessObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
+ if (observer != null) {
+ try {
+ observer.onProcessDied(pid, uid);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mProcessObservers.finishBroadcast();
+ }
+
final void doPendingActivityLaunchesLocked(boolean doResume) {
final int N = mPendingActivityLaunches.size();
if (N <= 0) {
@@ -6084,7 +6094,6 @@
mUsageStatsService.shutdown();
mBatteryStatsService.shutdown();
- mNetworkPolicyService.shutdown();
return timedout;
}
@@ -6241,6 +6250,14 @@
}
}
+ public void registerProcessObserver(IProcessObserver observer) {
+ mProcessObservers.register(observer);
+ }
+
+ public void unregisterProcessObserver(IProcessObserver observer) {
+ mProcessObservers.unregister(observer);
+ }
+
public void setImmersive(IBinder token, boolean immersive) {
synchronized(this) {
int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1;
@@ -12755,7 +12772,8 @@
app.curSchedGroup = schedGroup;
if (hadForegroundActivities != app.foregroundActivities) {
- mHandler.obtainMessage(DISPATCH_FOREGROUND_ACTIVITIES_CHANGED, app).sendToTarget();
+ mHandler.obtainMessage(DISPATCH_FOREGROUND_ACTIVITIES_CHANGED,
+ app.foregroundActivities ? 1 : 0, 0, app).sendToTarget();
}
return adj;
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index b1ab05b..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/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index a7a4f07..d083d01 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -19,13 +19,20 @@
import static android.Manifest.permission.MANAGE_APP_TOKENS;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.net.NetworkPolicyManager.POLICY_NONE;
-import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID;
import static android.net.NetworkPolicyManager.POLICY_REJECT_BACKGROUND;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID;
+import android.app.IActivityManager;
+import android.app.IProcessObserver;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.net.INetworkPolicyManager;
-import android.os.ServiceManager;
+import android.os.IPowerManager;
+import android.os.RemoteException;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -38,92 +45,139 @@
private static final String TAG = "NetworkPolicy";
private static final boolean LOGD = true;
- private static final String SERVICE_NAME = "netpolicy";
-
private Context mContext;
+ private IActivityManager mActivityManager;
+ private IPowerManager mPowerManager;
+
+ private Object mRulesLock = new Object();
+
+ private boolean mScreenOn = false;
/** Current network policy for each UID. */
- private SparseIntArray mUidPolicy;
+ private SparseIntArray mUidPolicy = new SparseIntArray();
/** Foreground at both UID and PID granularity. */
- private SparseBooleanArray mUidForeground;
- private SparseArray<SparseBooleanArray> mUidPidForeground;
+ private SparseBooleanArray mUidForeground = new SparseBooleanArray();
+ private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
+ SparseBooleanArray>();
// TODO: periodically poll network stats and write to disk
// TODO: save/restore policy information from disk
- // TODO: watch screen on/off broadcasts to track foreground
+ public NetworkPolicyManagerService(
+ Context context, IActivityManager activityManager, IPowerManager powerManager) {
+ mContext = checkNotNull(context, "missing context");
+ mActivityManager = checkNotNull(activityManager, "missing activityManager");
+ mPowerManager = checkNotNull(powerManager, "missing powerManager");
+ }
- public void publish(Context context) {
- mContext = context;
- ServiceManager.addService(SERVICE_NAME, asBinder());
-
- mUidPolicy = new SparseIntArray();
- mUidForeground = new SparseBooleanArray();
- mUidPidForeground = new SparseArray<SparseBooleanArray>();
-
- // TODO: register for NetworkManagementService callbacks
+ public void systemReady() {
// TODO: read current policy+stats from disk and generate NMS rules
- }
- public void shutdown() {
- // TODO: persist any pending stats during clean shutdown
+ updateScreenOn();
- mUidPolicy = null;
- mUidForeground = null;
- mUidPidForeground = null;
- }
-
- @Override
- public void onForegroundActivitiesChanged(int uid, int pid, boolean foreground) {
- // only someone like AMS should only be calling us
- mContext.enforceCallingOrSelfPermission(
- MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
-
- // because a uid can have multiple pids running inside, we need to
- // remember all pid states and summarize foreground at uid level.
-
- // record foreground for this specific pid
- SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
- if (pidForeground == null) {
- pidForeground = new SparseBooleanArray(2);
- mUidPidForeground.put(uid, pidForeground);
+ try {
+ mActivityManager.registerProcessObserver(mProcessObserver);
+ } catch (RemoteException e) {
+ // ouch, no foregroundActivities updates means some processes may
+ // never get network access.
+ Slog.e(TAG, "unable to register IProcessObserver", e);
}
- pidForeground.put(pid, foreground);
- computeUidForeground(uid);
+
+ // TODO: traverse existing processes to know foreground state, or have
+ // activitymanager dispatch current state when new observer attached.
+
+ final IntentFilter screenFilter = new IntentFilter();
+ screenFilter.addAction(Intent.ACTION_SCREEN_ON);
+ screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ mContext.registerReceiver(mScreenReceiver, screenFilter);
+
+ final IntentFilter shutdownFilter = new IntentFilter();
+ shutdownFilter.addAction(Intent.ACTION_SHUTDOWN);
+ mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
+
}
- @Override
- public void onProcessDied(int uid, int pid) {
- // only someone like AMS should only be calling us
- mContext.enforceCallingOrSelfPermission(
- MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
+ private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
+ @Override
+ public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+ // only someone like AMS should only be calling us
+ mContext.enforceCallingOrSelfPermission(
+ MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
- // clear records and recompute, when they exist
- final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
- if (pidForeground != null) {
- pidForeground.delete(pid);
- computeUidForeground(uid);
+ synchronized (mRulesLock) {
+ // because a uid can have multiple pids running inside, we need to
+ // remember all pid states and summarize foreground at uid level.
+
+ // record foreground for this specific pid
+ SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+ if (pidForeground == null) {
+ pidForeground = new SparseBooleanArray(2);
+ mUidPidForeground.put(uid, pidForeground);
+ }
+ pidForeground.put(pid, foregroundActivities);
+ computeUidForegroundL(uid);
+ }
}
- }
+
+ @Override
+ public void onProcessDied(int pid, int uid) {
+ // only someone like AMS should only be calling us
+ mContext.enforceCallingOrSelfPermission(
+ MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
+
+ synchronized (mRulesLock) {
+ // clear records and recompute, when they exist
+ final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+ if (pidForeground != null) {
+ pidForeground.delete(pid);
+ computeUidForegroundL(uid);
+ }
+ }
+ }
+ };
+
+ private BroadcastReceiver mScreenReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mRulesLock) {
+ // screen-related broadcasts are protected by system, no need
+ // for permissions check.
+ updateScreenOn();
+ }
+ }
+ };
+
+ private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // TODO: persist any pending stats during clean shutdown
+ Log.d(TAG, "persisting stats");
+ }
+ };
@Override
public void setUidPolicy(int uid, int policy) {
mContext.enforceCallingOrSelfPermission(
UPDATE_DEVICE_STATS, "requires UPDATE_DEVICE_STATS permission");
- mUidPolicy.put(uid, policy);
+
+ synchronized (mRulesLock) {
+ mUidPolicy.put(uid, policy);
+ }
}
@Override
public int getUidPolicy(int uid) {
- return mUidPolicy.get(uid, POLICY_NONE);
+ synchronized (mRulesLock) {
+ return mUidPolicy.get(uid, POLICY_NONE);
+ }
}
/**
* Foreground for PID changed; recompute foreground at UID level. If
- * changed, will trigger {@link #updateRulesForUid(int)}.
+ * changed, will trigger {@link #updateRulesForUidL(int)}.
*/
- private void computeUidForeground(int uid) {
+ private void computeUidForegroundL(int uid) {
final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
// current pid is dropping foreground; examine other pids
@@ -140,12 +194,37 @@
if (oldUidForeground != uidForeground) {
// foreground changed, push updated rules
mUidForeground.put(uid, uidForeground);
- updateRulesForUid(uid);
+ updateRulesForUidL(uid);
}
}
- private void updateRulesForUid(int uid) {
- final boolean uidForeground = mUidForeground.get(uid, false);
+ private void updateScreenOn() {
+ synchronized (mRulesLock) {
+ try {
+ mScreenOn = mPowerManager.isScreenOn();
+ } catch (RemoteException e) {
+ }
+ updateRulesForScreenL();
+ }
+ }
+
+ /**
+ * Update rules that might be changed by {@link #mScreenOn} value.
+ */
+ private void updateRulesForScreenL() {
+ // only update rules for anyone with foreground activities
+ final int size = mUidForeground.size();
+ for (int i = 0; i < size; i++) {
+ if (mUidForeground.valueAt(i)) {
+ final int uid = mUidForeground.keyAt(i);
+ updateRulesForUidL(uid);
+ }
+ }
+ }
+
+ private void updateRulesForUidL(int uid) {
+ // only really in foreground when screen on
+ final boolean uidForeground = mUidForeground.get(uid, false) && mScreenOn;
final int uidPolicy = getUidPolicy(uid);
if (LOGD) {
@@ -162,4 +241,10 @@
}
}
+ private static <T> T checkNotNull(T value, String message) {
+ if (value == null) {
+ throw new NullPointerException(message);
+ }
+ return value;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
index f20d5e5..ca33d32 100644
--- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
@@ -42,6 +42,7 @@
import android.os.SystemClock;
import android.provider.Settings;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.Suppress;
import android.text.format.DateUtils;
import android.util.Log;
@@ -54,6 +55,7 @@
/**
* Tests for {@link ThrottleService}.
*/
+@LargeTest
public class ThrottleServiceTest extends AndroidTestCase {
private static final String TAG = "ThrottleServiceTest";
diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java
index 9fc4667..0c4581b 100644
--- a/telephony/java/com/android/internal/telephony/BaseCommands.java
+++ b/telephony/java/com/android/internal/telephony/BaseCommands.java
@@ -25,6 +25,11 @@
import android.os.SystemProperties;
import android.util.Log;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
* {@hide}
*/
@@ -794,6 +799,34 @@
}
/**
+ * The contents of the /proc/cmdline file
+ */
+ private static String getProcCmdLine()
+ {
+ String cmdline = "";
+ FileInputStream is = null;
+ try {
+ is = new FileInputStream("/proc/cmdline");
+ byte [] buffer = new byte[2048];
+ int count = is.read(buffer);
+ if (count > 0) {
+ cmdline = new String(buffer, 0, count);
+ }
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "No /proc/cmdline exception=" + e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ Log.d(LOG_TAG, "/proc/cmdline=" + cmdline);
+ return cmdline;
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -801,6 +834,17 @@
return getLteOnCdmaModeStatic();
}
+ /** Kernel command line */
+ private static final String sKernelCmdLine = getProcCmdLine();
+
+ /** Pattern for selecting the product type from the kernel command line */
+ private static final Pattern sProductTypePattern =
+ Pattern.compile("\\sproduct_type\\s*=\\s*(\\w+)");
+
+ /** The ProductType used for LTE on CDMA devices */
+ private static final String sLteOnCdmaProductType =
+ SystemProperties.get(TelephonyProperties.PROPERTY_LTE_ON_CDMA_PRODUCT_TYPE, "");
+
/**
* Return if the current radio is LTE on CDMA. This
* is a tri-state return value as for a period of time
@@ -810,9 +854,24 @@
* or {@link Phone#LTE_ON_CDMA_TRUE}
*/
public static int getLteOnCdmaModeStatic() {
- int retVal = SystemProperties.getInt(TelephonyProperties.PROPERTY_NETWORK_LTE_ON_CDMA,
- Phone.LTE_ON_CDMA_FALSE);
- Log.d(LOG_TAG, "getLteOnCdmaMode=" + retVal);
+ int retVal;
+ String productType;
+
+ Matcher matcher = sProductTypePattern.matcher(sKernelCmdLine);
+ if (matcher.find()) {
+ productType = matcher.group(1);
+ if (sLteOnCdmaProductType.equals(productType)) {
+ retVal = Phone.LTE_ON_CDMA_TRUE;
+ } else {
+ retVal = Phone.LTE_ON_CDMA_FALSE;
+ }
+ } else {
+ retVal = Phone.LTE_ON_CDMA_FALSE;
+ productType = "";
+ }
+
+ Log.d(LOG_TAG, "getLteOnCdmaMode=" + retVal + " product_type='" + productType +
+ "' lteOnCdmaProductType='" + sLteOnCdmaProductType + "'");
return retVal;
}
}
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 2e781b2..3636baa 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -22,8 +22,10 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import android.net.LinkAddress;
import android.net.LinkCapabilities;
import android.net.LinkProperties;
+import android.net.NetworkUtils;
import android.net.ProxyProperties;
import android.os.AsyncResult;
import android.os.Bundle;
@@ -33,6 +35,7 @@
import android.os.SystemProperties;
import android.text.TextUtils;
+import java.util.Collection;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
@@ -202,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
@@ -249,6 +253,8 @@
Message m = dp.onCompletedMsg;
if (TextUtils.equals(dp.reason, Phone.REASON_RADIO_TURNED_OFF)) {
discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
+ } else if (TextUtils.equals(dp.reason, Phone.REASON_PDP_RESET)) {
+ discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
}
}
if (phone.mCM.getRadioState().isOn()) {
@@ -408,49 +414,6 @@
return mRetryMgr.configure(configStr);
}
- private AtomicInteger mRefCount = new AtomicInteger(0);
-
- /**
- * Set refCount.
- *
- * @param val is new refCount
- */
- public void setRefCount(int val) {
- mRefCount.set(val);
- }
-
- /**
- * Get refCount
- *
- * @return refCount
- */
- public int getRefCount() {
- return mRefCount.get();
- }
-
- /**
- * @return decrement and return refCount
- *
- * TODO: Consider using the refCount for defining the
- * life time of a connection. When this goes zero the
- * DataConnection could tear itself down.
- */
- public int decAndGetRefCount() {
- int v = mRefCount.decrementAndGet();
- if (v < 0) {
- log("BUG: decAndGetRefCount caused refCount to be < 0");
- mRefCount.set(0);
- }
- return v;
- }
-
- /**
- * @return increment and return refCount
- */
- public int incAndGetRefCount() {
- return mRefCount.incrementAndGet();
- }
-
/*
* **************************************************************************
* End members owned by DataConnectionTracker
@@ -466,6 +429,7 @@
createTime = -1;
lastFailTime = -1;
lastFailCause = FailCause.NONE;
+ mRefCount = 0;
mLinkProperties = new LinkProperties();
mApn = null;
@@ -531,8 +495,10 @@
return response.setLinkProperties(lp, okToUseSystemPropertyDns);
}
- private boolean updateLinkProperty(DataCallState newState) {
- boolean changed = false;
+ private DataConnectionAc.LinkPropertyChangeAction updateLinkProperty(
+ DataCallState newState) {
+ DataConnectionAc.LinkPropertyChangeAction changed =
+ DataConnectionAc.LinkPropertyChangeAction.NONE;
if (newState == null) return changed;
@@ -551,9 +517,23 @@
if (DBG) log("old LP=" + mLinkProperties);
if (DBG) log("new LP=" + newLp);
+ // Check consistency of link address. Currently we expect
+ // only one "global" address is assigned per each IP type.
+ Collection<LinkAddress> oLinks = mLinkProperties.getLinkAddresses();
+ Collection<LinkAddress> nLinks = newLp.getLinkAddresses();
+ for (LinkAddress oldLink : oLinks) {
+ for (LinkAddress newLink : nLinks) {
+ if ((NetworkUtils.addressTypeMatches(oldLink.getAddress(),
+ newLink.getAddress())) &&
+ (oldLink.equals(newLink) == false)) {
+ return DataConnectionAc.LinkPropertyChangeAction.RESET;
+ }
+ }
+ }
+
if (mLinkProperties == null || !mLinkProperties.equals(newLp)) {
mLinkProperties = newLp;
- changed = true;
+ changed = DataConnectionAc.LinkPropertyChangeAction.CHANGED;
}
return changed;
@@ -631,15 +611,14 @@
}
case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: {
DataCallState newState = (DataCallState) msg.obj;
- int updated = updateLinkProperty(newState) ? 1 : 0;
+ DataConnectionAc.LinkPropertyChangeAction action = updateLinkProperty(newState);
if (DBG) {
- log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE updated="
- + (updated == 1)
- + " newState=" + newState);
+ log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE action="
+ + action + " newState=" + newState);
}
mAc.replyToMessage(msg,
DataConnectionAc.RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE,
- updated);
+ action.ordinal());
break;
}
case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: {
@@ -654,6 +633,11 @@
mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
transitionTo(mInactiveState);
break;
+ case DataConnectionAc.REQ_GET_REFCOUNT: {
+ log("REQ_GET_REFCOUNT refCount=" + mRefCount);
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_REFCOUNT, mRefCount);
+ break;
+ }
case EVENT_CONNECT:
if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
@@ -754,9 +738,13 @@
break;
case EVENT_CONNECT:
- if (DBG) log("DcInactiveState msg.what=EVENT_CONNECT");
ConnectionParams cp = (ConnectionParams) msg.obj;
cp.tag = mTag;
+ if (DBG) {
+ log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = "
+ + mRefCount);
+ }
+ mRefCount = 1;
onConnect(cp);
transitionTo(mActivatingState);
retVal = HANDLED;
@@ -784,7 +772,15 @@
switch (msg.what) {
case EVENT_DISCONNECT:
- if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT");
+ if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT"
+ + mRefCount);
+ deferMessage(msg);
+ retVal = HANDLED;
+ break;
+
+ case EVENT_CONNECT:
+ if (DBG) log("DcActivatingState deferring msg.what=EVENT_CONNECT refCount = "
+ + mRefCount);
deferMessage(msg);
retVal = HANDLED;
break;
@@ -908,12 +904,28 @@
boolean retVal;
switch (msg.what) {
+ case EVENT_CONNECT:
+ mRefCount++;
+ if (DBG) log("DcActiveState msg.what=EVENT_CONNECT RefCount=" + mRefCount);
+ if (msg.obj != null) {
+ notifyConnectCompleted((ConnectionParams) msg.obj, FailCause.NONE);
+ }
+ retVal = HANDLED;
+ break;
case EVENT_DISCONNECT:
- if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT");
- DisconnectParams dp = (DisconnectParams) msg.obj;
- dp.tag = mTag;
- tearDownData(dp);
- transitionTo(mDisconnectingState);
+ mRefCount--;
+ if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" + mRefCount);
+ if (mRefCount == 0)
+ {
+ DisconnectParams dp = (DisconnectParams) msg.obj;
+ dp.tag = mTag;
+ tearDownData(dp);
+ transitionTo(mDisconnectingState);
+ } else {
+ if (msg.obj != null) {
+ notifyDisconnectCompleted((DisconnectParams) msg.obj);
+ }
+ }
retVal = HANDLED;
break;
@@ -936,6 +948,13 @@
boolean retVal;
switch (msg.what) {
+ case EVENT_CONNECT:
+ if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = "
+ + mRefCount);
+ deferMessage(msg);
+ retVal = HANDLED;
+ break;
+
case EVENT_DEACTIVATE_DONE:
if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE");
AsyncResult ar = (AsyncResult) msg.obj;
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
index 2ab6184..a0d9b0f 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
@@ -59,6 +59,29 @@
public static final int REQ_RESET = BASE + 14;
public static final int RSP_RESET = BASE + 15;
+ public static final int REQ_GET_REFCOUNT = BASE + 16;
+ public static final int RSP_GET_REFCOUNT = BASE + 17;
+
+ /**
+ * enum used to notify action taken or necessary to be
+ * taken after the link property is changed.
+ */
+ public enum LinkPropertyChangeAction {
+ NONE, CHANGED, RESET;
+
+ public static LinkPropertyChangeAction fromInt(int value) {
+ if (value == NONE.ordinal()) {
+ return NONE;
+ } else if (value == CHANGED.ordinal()) {
+ return CHANGED;
+ } else if (value == RESET.ordinal()) {
+ return RESET;
+ } else {
+ throw new RuntimeException("LinkPropertyChangeAction.fromInt: bad value=" + value);
+ }
+ }
+ }
+
public DataConnectionAc(DataConnection dc, String logTag) {
dataConnection = dc;
mLogTag = logTag;
@@ -132,6 +155,40 @@
}
/**
+ * Request the Reference Count.
+ * Response {@link #rspRefCount}
+ */
+ public void reqRefCount() {
+ sendMessage(REQ_GET_REFCOUNT);
+ if (DBG) log("reqRefCount");
+ }
+
+ /**
+ * Evaluate a RSP_GET_REFCOUNT message and return the refCount.
+ *
+ * @param response Message
+ * @return ref count or -1 if an error
+ */
+ public int rspRefCount(Message response) {
+ int retVal = response.arg1;
+ if (DBG) log("rspRefCount=" + retVal);
+ return retVal;
+ }
+
+ /**
+ * @return connection id or -1 if an error
+ */
+ public int getRefCountSync() {
+ Message response = sendMessageSynchronously(REQ_GET_REFCOUNT);
+ if ((response != null) && (response.what == RSP_GET_REFCOUNT)) {
+ return rspRefCount(response);
+ } else {
+ log("rspRefCount error response=" + response);
+ return -1;
+ }
+ }
+
+ /**
* Request the connections ApnSetting.
* Response {@link #rspApnSetting}
*/
@@ -234,8 +291,8 @@
if (DBG) log("reqUpdateLinkPropertiesDataCallState");
}
- public boolean rspUpdateLinkPropertiesDataCallState(Message response) {
- boolean retVal = response.arg1 == 1;
+ public LinkPropertyChangeAction rspUpdateLinkPropertiesDataCallState(Message response) {
+ LinkPropertyChangeAction retVal = LinkPropertyChangeAction.fromInt(response.arg1);
if (DBG) log("rspUpdateLinkPropertiesState=" + retVal);
return retVal;
}
@@ -245,7 +302,7 @@
*
* @return true if link property has been updated. false otherwise.
*/
- public boolean updateLinkPropertiesDataCallStateSync(DataCallState newState) {
+ public LinkPropertyChangeAction updateLinkPropertiesDataCallStateSync(DataCallState newState) {
Message response =
sendMessageSynchronously(REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, newState);
if ((response != null) &&
@@ -253,7 +310,7 @@
return rspUpdateLinkPropertiesDataCallState(response);
} else {
log("getLinkProperties error response=" + response);
- return false;
+ return LinkPropertyChangeAction.NONE;
}
}
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index b7ac879..5ddfcd1 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -987,7 +987,7 @@
resetAllRetryCounts();
onTrySetupData(Phone.REASON_DATA_ENABLED);
} else {
- onCleanUpConnection(true, APN_DEFAULT_ID, Phone.REASON_DATA_DISABLED);
+ onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
}
}
}
diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index 1b49d2d..a516b49 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -18,13 +18,20 @@
import static android.Manifest.permission.READ_PHONE_STATE;
import android.app.ActivityManagerNative;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
+import android.content.res.Resources;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
+import android.os.Power;
+import android.os.PowerManager;
import android.os.Registrant;
import android.os.RegistrantList;
import android.util.Log;
+import android.view.WindowManager;
import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.CommandsInterface.RadioState;
@@ -32,6 +39,8 @@
import android.os.SystemProperties;
+import com.android.internal.R;
+
/**
* {@hide}
*/
@@ -88,6 +97,8 @@
private static final int EVENT_QUERY_FACILITY_FDN_DONE = 10;
private static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11;
private static final int EVENT_ICC_STATUS_CHANGED = 12;
+ private static final int EVENT_CARD_REMOVED = 13;
+ private static final int EVENT_CARD_ADDED = 14;
/*
UNKNOWN is a transient state, for example, after uesr inputs ICC pin under
@@ -106,6 +117,11 @@
public boolean isPinLocked() {
return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
}
+
+ public boolean iccCardExist() {
+ return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)
+ || (this == NETWORK_LOCKED) || (this == READY));
+ }
}
public State getState() {
@@ -400,6 +416,8 @@
boolean transitionedIntoPinLocked;
boolean transitionedIntoAbsent;
boolean transitionedIntoNetworkLocked;
+ boolean isIccCardRemoved;
+ boolean isIccCardAdded;
State oldState, newState;
@@ -416,24 +434,36 @@
transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT);
transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED
&& newState == State.NETWORK_LOCKED);
+ isIccCardRemoved = (oldState != null &&
+ oldState.iccCardExist() && newState == State.ABSENT);
+ isIccCardAdded = (oldState == State.ABSENT &&
+ newState != null && newState.iccCardExist());
if (transitionedIntoPinLocked) {
- if(mDbg) log("Notify SIM pin or puk locked.");
+ if (mDbg) log("Notify SIM pin or puk locked.");
mPinLockedRegistrants.notifyRegistrants();
broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
(newState == State.PIN_REQUIRED) ?
INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK);
} else if (transitionedIntoAbsent) {
- if(mDbg) log("Notify SIM missing.");
+ if (mDbg) log("Notify SIM missing.");
mAbsentRegistrants.notifyRegistrants();
broadcastIccStateChangedIntent(INTENT_VALUE_ICC_ABSENT, null);
} else if (transitionedIntoNetworkLocked) {
- if(mDbg) log("Notify SIM network locked.");
+ if (mDbg) log("Notify SIM network locked.");
mNetworkLockedRegistrants.notifyRegistrants();
broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
INTENT_VALUE_LOCKED_NETWORK);
}
+ if (isIccCardRemoved) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null));
+ } else if (isIccCardAdded) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
+ }
+
+
+
/*
* TODO: We need to try to remove this, maybe if the RIL sends up a RIL_UNSOL_SIM_REFRESH?
*/
@@ -443,6 +473,48 @@
((SIMRecords)mPhone.mIccRecords).onSimReady();
}
}
+
+ }
+
+ private void onIccSwap(boolean isAdded) {
+ // TODO: Here we assume the device can't handle SIM hot-swap
+ // and has to reboot. We may want to add a property,
+ // e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support
+ // hot-swap.
+ DialogInterface.OnClickListener listener = null;
+
+
+ // TODO: SimRecords is not reset while SIM ABSENT (only reset while
+ // Radio_off_or_not_available). Have to reset in both both
+ // added or removed situation.
+ listener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ if (mDbg) log("Reboot due to SIM swap");
+ PowerManager pm = (PowerManager) mPhone.getContext()
+ .getSystemService(Context.POWER_SERVICE);
+ pm.reboot("SIM is added.");
+ }
+ }
+
+ };
+
+ Resources r = Resources.getSystem();
+
+ String title = (isAdded) ? r.getString(R.string.sim_added_title) :
+ r.getString(R.string.sim_removed_title);
+ String message = (isAdded) ? r.getString(R.string.sim_added_message) :
+ r.getString(R.string.sim_removed_message);
+ String buttonTxt = r.getString(R.string.sim_restart_button);
+
+ AlertDialog dialog = new AlertDialog.Builder(mPhone.getContext())
+ .setTitle(title)
+ .setMessage(message)
+ .setPositiveButton(buttonTxt, listener)
+ .create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
}
/**
@@ -609,6 +681,12 @@
Log.d(mLogTag, "Received Event EVENT_ICC_STATUS_CHANGED");
mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
break;
+ case EVENT_CARD_REMOVED:
+ onIccSwap(false);
+ break;
+ case EVENT_CARD_ADDED:
+ onIccSwap(true);
+ break;
default:
Log.e(mLogTag, "[IccCard] Unknown Event " + msg.what);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index f5d6c51..93fc9ce 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -113,6 +113,7 @@
/* Deactivate data call reasons */
int DEACTIVATE_REASON_NONE = 0;
int DEACTIVATE_REASON_RADIO_OFF = 1;
+ int DEACTIVATE_REASON_PDP_RESET = 2;
/*
cat include/telephony/ril.h | \
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 4927006..4309309 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -72,10 +72,12 @@
*/
static final String PROPERTY_OPERATOR_ISO_COUNTRY = "gsm.operator.iso-country";
- /** 'true' if device supports both LTE and CDMA mode of operation.
- * Availability: Set only on devices supporting LTE and CDMA.
+ /**
+ * The contents of this property is the value of the kernel command line
+ * product_type variable that corresponds to a product that supports LTE on CDMA.
+ * {@see BaseCommands#getLteOnCdmaMode()}
*/
- static final String PROPERTY_NETWORK_LTE_ON_CDMA = "telephony.lte_on_cdma";
+ static final String PROPERTY_LTE_ON_CDMA_PRODUCT_TYPE = "telephony.lteOnCdmaProductType";
static final String CURRENT_ACTIVE_PHONE = "gsm.current.phone-type";
diff --git a/telephony/java/com/android/internal/telephony/cat/CatService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java
index 33cc97e..fb53686 100644
--- a/telephony/java/com/android/internal/telephony/cat/CatService.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatService.java
@@ -32,6 +32,7 @@
import java.io.ByteArrayOutputStream;
+import java.util.Locale;
/**
* Enumeration for representing the tag value of COMPREHENSION-TLV objects. If
@@ -273,8 +274,20 @@
sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
break;
case PROVIDE_LOCAL_INFORMATION:
- sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
- return;
+ ResponseData resp;
+ switch (cmdParams.cmdDet.commandQualifier) {
+ case CommandParamsFactory.DTTZ_SETTING:
+ resp = new DTTZResponseData(null);
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
+ break;
+ case CommandParamsFactory.LANGUAGE_SETTING:
+ resp = new LanguageResponseData(Locale.getDefault().getLanguage());
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
+ break;
+ default:
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ return;
+ }
case LAUNCH_BROWSER:
case SELECT_ITEM:
case GET_INPUT:
diff --git a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 12204a0..686fe46 100644
--- a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -53,6 +53,7 @@
static final int REFRESH_UICC_RESET = 0x04;
// Command Qualifier values for PLI command
+ static final int DTTZ_SETTING = 0x03;
static final int LANGUAGE_SETTING = 0x04;
static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
@@ -883,6 +884,10 @@
throws ResultException {
CatLog.d(this, "process ProvideLocalInfo");
switch (cmdDet.commandQualifier) {
+ case DTTZ_SETTING:
+ CatLog.d(this, "PLI [DTTZ_SETTING]");
+ mCmdParams = new CommandParams(cmdDet);
+ break;
case LANGUAGE_SETTING:
CatLog.d(this, "PLI [LANGUAGE_SETTING]");
mCmdParams = new CommandParams(cmdDet);
diff --git a/telephony/java/com/android/internal/telephony/cat/ResponseData.java b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
index 95f0399..55a2b63 100644
--- a/telephony/java/com/android/internal/telephony/cat/ResponseData.java
+++ b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
@@ -18,6 +18,8 @@
import com.android.internal.telephony.EncodeException;
import com.android.internal.telephony.GsmAlphabet;
+import java.util.Calendar;
+import com.android.internal.telephony.cat.AppInterface.CommandType;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
@@ -147,4 +149,109 @@
}
}
+// For "PROVIDE LOCAL INFORMATION" command.
+// See TS 31.111 section 6.4.15/ETSI TS 102 223
+// TS 31.124 section 27.22.4.15 for test spec
+class LanguageResponseData extends ResponseData {
+ private String lang;
+
+ public LanguageResponseData(String lang) {
+ super();
+ this.lang = lang;
+ }
+
+ @Override
+ public void format(ByteArrayOutputStream buf) {
+ if (buf == null) {
+ return;
+ }
+
+ // Text string object
+ int tag = 0x80 | ComprehensionTlvTag.LANGUAGE.value();
+ buf.write(tag); // tag
+
+ byte[] data;
+
+ if (lang != null && lang.length() > 0) {
+ data = GsmAlphabet.stringToGsm8BitPacked(lang);
+ }
+ else {
+ data = new byte[0];
+ }
+
+ buf.write(data.length);
+
+ for (byte b : data) {
+ buf.write(b);
+ }
+ }
+}
+
+// For "PROVIDE LOCAL INFORMATION" command.
+// See TS 31.111 section 6.4.15/ETSI TS 102 223
+// TS 31.124 section 27.22.4.15 for test spec
+class DTTZResponseData extends ResponseData {
+ private Calendar calendar;
+
+ public DTTZResponseData(Calendar cal) {
+ super();
+ calendar = cal;
+ }
+
+ @Override
+ public void format(ByteArrayOutputStream buf) {
+ if (buf == null) {
+ return;
+ }
+
+ // DTTZ object
+ int tag = 0x80 | CommandType.PROVIDE_LOCAL_INFORMATION.value();
+ buf.write(tag); // tag
+
+ byte[] data = new byte[8];
+ byte btmp; // temp variable
+
+ data[0] = 0x07; // Write length of DTTZ data
+
+ if (calendar == null) {
+ calendar = Calendar.getInstance();
+ }
+ // Fill year byte
+ btmp = (byte) (calendar.get(java.util.Calendar.YEAR) % 100);
+ data[1] = (byte) (btmp / 10);
+ data[1] += (byte) ((btmp % 10) << 4);
+
+ // Fill month byte
+ btmp = (byte) (calendar.get(java.util.Calendar.MONTH) + 1);
+ data[2] = (byte) (btmp / 10);
+ data[2] += (byte) ((btmp % 10) << 4);
+
+ // Fill day byte
+ btmp = (byte) (calendar.get(java.util.Calendar.DATE));
+ data[3] = (byte) (btmp / 10);
+ data[3] += (byte) ((btmp % 10) << 4);
+
+ // Fill hour byte
+ btmp = (byte) (calendar.get(java.util.Calendar.HOUR_OF_DAY));
+ data[4] = (byte) (btmp / 10);
+ data[4] += (byte) ((btmp % 10) << 4);
+
+ // Fill minute byte
+ btmp = (byte) (calendar.get(java.util.Calendar.MINUTE));
+ data[5] = (byte) (btmp / 10);
+ data[5] += (byte) ((btmp % 10) << 4);
+
+ // Fill second byte
+ btmp = (byte) (calendar.get(java.util.Calendar.SECOND));
+ data[6] = (byte) (btmp / 10);
+ data[6] += (byte) ((btmp % 10) << 4);
+
+ // No time zone info
+ data[7] = (byte) 0xFF;
+
+ for (byte b : data) {
+ buf.write(b);
+ }
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 375d0d1..e3e3d78 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -521,19 +521,7 @@
* 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 6b4054a..e1a6fef 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -127,10 +127,7 @@
if (apnContext != null) {
apnContext.setReason(reason);
if (apnContext.getState() == State.FAILED) {
- Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
- msg.arg1 = 0; // tearDown is false
- msg.obj = (ApnContext)apnContext;
- sendMessage(msg);
+ apnContext.setState(State.IDLE);
}
sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext));
}
@@ -615,28 +612,26 @@
*
* @param apnContext
* @param tearDown
- * @return refCount
+ * @return none
*/
- private int releaseApnContext(ApnContext apnContext, boolean tearDown) {
+ private void releaseApnContext(ApnContext apnContext, boolean tearDown) {
if (apnContext == null) {
if (DBG) loge("releaseApnContext: apnContext null should not happen, ignore");
- return -1;
+ return;
}
DataConnection dc = apnContext.getDataConnection();
if (dc == null) {
if (DBG) loge("releaseApnContext: apnContext dc == null should not happen, ignore");
- return -1;
+ return;
}
- int refCount = dc.decAndGetRefCount();
- if (DBG) log("releaseApnContext: dec refCount=" + refCount + " tearDown=" + tearDown);
- if (tearDown && (refCount == 0)) {
+ if (tearDown) {
if (DBG) log("releaseApnContext: tearing down");
Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext);
apnContext.getDataConnection().tearDown(apnContext.getReason(), msg);
}
apnContext.setDataConnection(null);
apnContext.setDataConnectionAc(null);
- return refCount;
+ return;
}
private void setupDataOnReadyApns(String reason) {
@@ -644,7 +639,7 @@
for (ApnContext apnContext : mApnContexts.values()) {
if (apnContext.isReady()) {
if (apnContext.getState() == State.FAILED) {
- cleanUpConnection(false, apnContext);
+ cleanApnContextBeforeRestart(apnContext);
if (apnContext.getDataConnection() != null) {
apnContext.getDataConnection().resetRetryCount();
}
@@ -800,6 +795,34 @@
if (DBG) {
log("cleanUpConnection: tearDown=" + tearDown + " reason=" + apnContext.getReason());
}
+ if (tearDown && cleanApnContextBeforeRestart(apnContext)) {
+ // if the request is tearDown and ApnContext does not hold an active connection,
+ // we're ok to return here.
+ return;
+ }
+
+ DataConnectionAc dcac = apnContext.getDataConnectionAc();
+ if (dcac != null) {
+ if (tearDown) {
+ apnContext.setState(State.DISCONNECTING);
+ releaseApnContext(apnContext, tearDown);
+ } else {
+ dcac.resetSync();
+ apnContext.setState(State.IDLE);
+ mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
+ apnContext.setDataConnection(null);
+ apnContext.setDataConnectionAc(null);
+ }
+ }
+ }
+
+ /**
+ * @param APNContext to clean
+ * @return true if ApnContext is not connected anymore.
+ * false if ApnContext still holds a connection.
+ */
+ private boolean cleanApnContextBeforeRestart(ApnContext apnContext) {
+ if (apnContext == null) return true;
// Clear the reconnect alarm, if set.
if (apnContext.getReconnectIntent() != null) {
@@ -811,32 +834,16 @@
if (apnContext.getState() == State.IDLE || apnContext.getState() == State.DISCONNECTING) {
if (DBG) log("cleanUpConnection: state= " + apnContext.getState());
- return;
+ return true;
}
if (apnContext.getState() == State.FAILED) {
- if (DBG) log("cleanUpConnection: state is in FAILED");
apnContext.setState(State.IDLE);
- return;
+ return true;
}
-
- DataConnection conn = apnContext.getDataConnection();
- if (conn != null) {
- DataConnectionAc dcac = mDataConnectionAsyncChannels.get(conn.getDataConnectionId());
- apnContext.setState(State.DISCONNECTING);
- if (tearDown) {
- releaseApnContext(apnContext, tearDown);
- } else {
- if (dcac != null) {
- dcac.resetSync();
- }
- apnContext.setState(State.IDLE);
- mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
- }
- }
+ return false;
}
-
/**
* @param types comma delimited list of APN types
* @return array of APN types
@@ -927,37 +934,48 @@
return false;
}
- dc = findReadyDataConnection(apn);
+ // First, check to see if ApnContext already has DC.
+ // This could happen if the retries are currently engaged.
+ dc = (GsmDataConnection)apnContext.getDataConnection();
if (dc == null) {
- if (DBG) log("setupData: No ready GsmDataConnection found!");
- // TODO: When allocating you are mapping type to id. If more than 1 free,
- // then could findFreeDataConnection get the wrong one??
- dc = findFreeDataConnection();
- }
- if (dc == null) {
- dc = createDataConnection();
- }
+ dc = (GsmDataConnection) checkForConnectionForApnContext(apnContext);
- if (dc == null) {
- if (DBG) log("setupData: No free GsmDataConnection found!");
- return false;
- }
+ if (dc == null) {
+ dc = findReadyDataConnection(apn);
+ }
- dc.setProfileId( profileId );
- dc.setActiveApnType(apnContext.getApnType());
- int refCount = dc.incAndGetRefCount();
- if (DBG) log("setupData: init dc and apnContext refCount=" + refCount);
+ if (dc == null) {
+ if (DBG) log("setupData: No ready GsmDataConnection found!");
+ // TODO: When allocating you are mapping type to id. If more than 1 free,
+ // then could findFreeDataConnection get the wrong one??
+ dc = findFreeDataConnection();
+ }
- // configure retry count if no other Apn is using the same connection.
- if (refCount == 1) {
- configureRetry(dc, apnContext.getApnType());
+ if (dc == null) {
+ dc = createDataConnection();
+ }
+
+ if (dc == null) {
+ if (DBG) log("setupData: No free GsmDataConnection found!");
+ return false;
+ }
+
+ DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId());
+ dc.setProfileId( profileId );
+ dc.setActiveApnType(apnContext.getApnType());
+ int refCount = dcac.getRefCountSync();
+ if (DBG) log("setupData: init dc and apnContext refCount=" + refCount);
+
+ // configure retry count if no other Apn is using the same connection.
+ if (refCount == 0) {
+ configureRetry(dc, apnContext.getApnType());
+ }
+ apnContext.setDataConnectionAc(mDataConnectionAsyncChannels.get(dc.getDataConnectionId()));
+ apnContext.setApnSetting(apn);
+ apnContext.setDataConnection(dc);
}
- DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId());
- apnContext.setDataConnectionAc(mDataConnectionAsyncChannels.get(dc.getDataConnectionId()));
- apnContext.setApnSetting(apn);
- apnContext.setDataConnection(dc);
Message msg = obtainMessage();
msg.what = EVENT_DATA_SETUP_COMPLETE;
@@ -1053,25 +1071,37 @@
if (DBG) log("onDataStateChanged(ar): Found ConnId=" + connectionId
+ " newState=" + newState.toString());
if (newState.active != 0) {
- boolean changed
- = dcac.updateLinkPropertiesDataCallStateSync(newState);
- if (changed) {
+ boolean resetConnection;
+ switch (dcac.updateLinkPropertiesDataCallStateSync(newState)) {
+ case NONE:
+ if (DBG) log("onDataStateChanged(ar): Found but no change, skip");
+ resetConnection = false;
+ break;
+ case CHANGED:
if (DBG) log("onDataStateChanged(ar): Found and changed, notify");
mPhone.notifyDataConnection(Phone.REASON_LINK_PROPERTIES_CHANGED,
- apnContext.getApnType());
- // Temporary hack, if false we'll reset connections and at this
- // time a transition from CDMA -> Global fails. The DEACTIVATE
- // fails with a GENERIC_FAILURE and the VZWINTERNET connection is
- // never setup. @see bug/
+ apnContext.getApnType());
+ // Temporary hack, at this time a transition from CDMA -> Global
+ // fails so we'll hope for the best and not reset the connection.
+ // @see bug/4455071
if (SystemProperties.getBoolean("telephony.ignore-state-changes",
- true)) {
+ true)) {
log("onDataStateChanged(ar): STOPSHIP don't reset, continue");
- continue;
+ resetConnection = false;
+ } else {
+ // Things changed so reset connection, when hack is removed
+ // this is the normal path.
+ log("onDataStateChanged(ar): changed so resetting connection");
+ resetConnection = true;
}
- } else {
- if (DBG) log("onDataStateChanged(ar): Found but no change, skip");
- continue;
+ break;
+ case RESET:
+ default:
+ if (DBG) log("onDataStateChanged(ar): an error, reset connection");
+ resetConnection = true;
+ break;
}
+ if (resetConnection == false) continue;
}
}
@@ -1214,10 +1244,30 @@
preTxPkts = mTxPkts;
preRxPkts = mRxPkts;
- mTxPkts = TrafficStats.getMobileTxPackets();
- mRxPkts = TrafficStats.getMobileRxPackets();
+ long txSum = 0, rxSum = 0;
+ for (ApnContext apnContext : mApnContexts.values()) {
+ if (apnContext.getState() == State.CONNECTED) {
+ DataConnectionAc dcac = apnContext.getDataConnectionAc();
+ if (dcac == null) continue;
- //log("rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts));
+ LinkProperties linkProp = dcac.getLinkPropertiesSync();
+ if (linkProp == null) continue;
+
+ String iface = linkProp.getInterfaceName();
+
+ if (iface != null) {
+ long stats = TrafficStats.getTxPackets(iface);
+ if (stats > 0) txSum += stats;
+ stats = TrafficStats.getRxPackets(iface);
+ if (stats > 0) rxSum += stats;
+ }
+ }
+ }
+
+ mTxPkts = txSum;
+ mRxPkts = rxSum;
+
+ // log("tx " + mTxPkts + " rx " + mRxPkts);
if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
sent = mTxPkts - preTxPkts;
@@ -1333,7 +1383,8 @@
loge("reconnectAfterFail: apnContext == null, impossible");
return;
}
- if (apnContext.getState() == State.FAILED) {
+ if ((apnContext.getState() == State.FAILED) &&
+ (apnContext.getDataConnection() != null)) {
if (!apnContext.getDataConnection().isRetryNeeded()) {
if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) {
mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
@@ -1353,23 +1404,7 @@
}
int nextReconnectDelay = apnContext.getDataConnection().getRetryTimer();
- if (DBG) {
- log("reconnectAfterFail: activate failed. Scheduling next attempt for "
- + (nextReconnectDelay / 1000) + "s");
- }
-
- AlarmManager am =
- (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(INTENT_RECONNECT_ALARM);
- intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
- // Should put an extra of apn type?
- intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnContext.getApnType());
- apnContext.setReconnectIntent(PendingIntent.getBroadcast (
- mPhone.getContext(), 0, intent, 0));
- am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + nextReconnectDelay,
- apnContext.getReconnectIntent());
-
+ startAlarmForReconnect(nextReconnectDelay, apnContext);
apnContext.getDataConnection().increaseRetryCount();
if (!shouldPostNotification(lastFailCauseCode)) {
@@ -1383,6 +1418,25 @@
}
}
+ private void startAlarmForReconnect(int delay, ApnContext apnContext) {
+
+ if (DBG) {
+ log("Schedule alarm for reconnect: activate failed. Scheduling next attempt for "
+ + (delay / 1000) + "s");
+ }
+
+ AlarmManager am =
+ (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
+ Intent intent = new Intent(INTENT_RECONNECT_ALARM);
+ intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
+ intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnContext.getApnType());
+ apnContext.setReconnectIntent(PendingIntent.getBroadcast (
+ mPhone.getContext(), 0, intent, 0));
+ am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + delay, apnContext.getReconnectIntent());
+
+ }
+
private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode,
ApnContext apnContext) {
if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
@@ -1437,23 +1491,10 @@
} else {
apnContext.setReason(Phone.REASON_DATA_ENABLED);
}
- DataConnection conn = checkForConnectionForApnContext(apnContext);
- if (conn == null) {
- if (apnContext.getState() == State.FAILED) {
- apnContext.setState(State.IDLE);
- }
- trySetup = true;
- } else {
- int refCount = conn.incAndGetRefCount();
- apnContext.setDataConnection(conn);
- apnContext.setDataConnectionAc(
- mDataConnectionAsyncChannels.get(conn.getDataConnectionId()));
- if (DBG) {
- log("applyNewState: Found existing connection for " +
- apnContext.getApnType() + " inc refCount=" + refCount +
- " conn=" + conn);
- }
+ if (apnContext.getState() == State.FAILED) {
+ apnContext.setState(State.IDLE);
}
+ trySetup = true;
}
}
apnContext.setEnabled(enabled);
@@ -1588,7 +1629,7 @@
if (DBG) {
log(String.format("onDataSetupComplete: success apn=%s",
- apnContext.getWaitingApns().get(0).apn) + " refCount=" + dc.getRefCount());
+ apnContext.getWaitingApns().get(0).apn));
}
ApnSetting apn = apnContext.getApnSetting();
if (apn.proxy != null && apn.proxy.length() != 0) {
@@ -1655,6 +1696,11 @@
}
apnContext.setState(State.FAILED);
mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
+
+ releaseApnContext(apnContext, false);
+ if (DBG) {
+ log("onDataSetupComplete: permanent error apn=%s" + apnString );
+ }
} else {
if (DBG) log("onDataSetupComplete: Not all permanent failures, retry");
startDelayedRetry(cause, apnContext);
@@ -1664,13 +1710,7 @@
apnContext.setState(State.SCANNING);
// Wait a bit before trying the next APN, so that
// we're not tying up the RIL command channel
- sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext),
- APN_DELAY_MILLIS);
- }
-
- int refCount = releaseApnContext(apnContext, false);
- if (DBG) {
- log("onDataSetupComplete: error apn=%s" + apnString + " refCount=" + refCount);
+ startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
}
}
}
@@ -1710,7 +1750,7 @@
// Wait a bit before trying the next APN, so that
// we're not tying up the RIL command channel.
// This also helps in any external dependency to turn off the context.
- sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext),APN_DELAY_MILLIS);
+ startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
}
}
@@ -1753,7 +1793,10 @@
protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
if (DBG) log("onCleanUpConnection");
ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
- cleanUpConnection(tearDown, apnContext);
+ if (apnContext != null) {
+ apnContext.setReason(reason);
+ cleanUpConnection(tearDown, apnContext);
+ }
}
protected boolean isConnected() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimCard.java b/telephony/java/com/android/internal/telephony/gsm/SimCard.java
index b7b0af3..643f709 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimCard.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimCard.java
@@ -50,6 +50,7 @@
if(mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
mPhone.mCM.registerForNVReady(mHandler, EVENT_ICC_READY, null);
+ mPhone.mCM.registerForIccStatusChanged(mHandler, EVENT_ICC_LOCKED_OR_ABSENT, null);
}
}
@@ -60,6 +61,11 @@
mPhone.mCM.unregisterForSIMLockedOrAbsent(mHandler);
mPhone.mCM.unregisterForOffOrNotAvailable(mHandler);
mPhone.mCM.unregisterForSIMReady(mHandler);
+
+ if(mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
+ mPhone.mCM.unregisterForNVReady(mHandler);
+ mPhone.mCM.unregisterForIccStatusChanged(mHandler);
+ }
}
@Override
diff --git a/tests/BiDiTests/AndroidManifest.xml b/tests/BiDiTests/AndroidManifest.xml
index 727f980..8a77519 100644
--- a/tests/BiDiTests/AndroidManifest.xml
+++ b/tests/BiDiTests/AndroidManifest.xml
@@ -14,24 +14,49 @@
limitations under the License.
-->
-<!-- Declare the contents of this Android application. The namespace
- attribute brings in the Android platform namespace, and the package
- supplies a unique name for the application. When writing your
- own application, the package name must be changed from "com.example.*"
- to come from a domain that you own or have control over. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.bidi"
android:versionCode="1"
android:versionName="1.0">
<application android:label="BiDiTests">
- <activity android:name="BiDiTestActivity"
- android:windowSoftInputMode="stateAlwaysHidden">
+
+ <activity android:name=".BiDiTestActivity"
+ android:windowSoftInputMode="stateAlwaysHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name=".BiDiTestBasicActivity"
+ android:windowSoftInputMode="stateAlwaysHidden">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".BiDiTestCanvasActivity"
+ android:windowSoftInputMode="stateAlwaysHidden">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".BiDiTestLinearLayoutLtrActivity"
+ android:windowSoftInputMode="stateAlwaysHidden">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".BiDiTestLinearLayoutRtlActivity"
+ android:windowSoftInputMode="stateAlwaysHidden">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
</application>
+
</manifest>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/biditest_main.xml b/tests/BiDiTests/res/layout/basic.xml
similarity index 79%
rename from tests/BiDiTests/res/layout/biditest_main.xml
rename to tests/BiDiTests/res/layout/basic.xml
index 044a355..c4807ff 100644
--- a/tests/BiDiTests/res/layout/biditest_main.xml
+++ b/tests/BiDiTests/res/layout/basic.xml
@@ -20,7 +20,7 @@
android:layout_height="match_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@@ -39,7 +39,7 @@
android:text="@string/textview_text"
/>
- <EditText android:id="@+id/textview"
+ <EditText android:id="@+id/edittext"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:textSize="32dip"
@@ -47,16 +47,4 @@
</LinearLayout>
- <SeekBar android:id="@+id/seekbar"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- />
-
- <view class="com.android.bidi.BiDiTestView"
- android:id="@+id/main"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#FF0000"
- />
-
</LinearLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/canvas.xml b/tests/BiDiTests/res/layout/canvas.xml
new file mode 100644
index 0000000..371cc23
--- /dev/null
+++ b/tests/BiDiTests/res/layout/canvas.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <SeekBar android:id="@+id/seekbar"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ />
+
+ <view class="com.android.bidi.BiDiTestView"
+ android:id="@+id/testview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#FF0000"
+ />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/linear_layout_ltr.xml b/tests/BiDiTests/res/layout/linear_layout_ltr.xml
new file mode 100644
index 0000000..a95fb0e
--- /dev/null
+++ b/tests/BiDiTests/res/layout/linear_layout_ltr.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/layouttest"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:horizontalDirection="ltr">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:horizontalDirection="inherit">
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button1_text"
+ android:textSize="32dip"
+ />
+
+ <TextView android:id="@+id/textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="32dip"
+ android:text="@string/textview_text"
+ />
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button2_text"
+ android:textSize="32dip"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:horizontalDirection="ltr">
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button1_text"
+ android:textSize="32dip"
+ />
+
+ <TextView android:id="@+id/textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="32dip"
+ android:text="@string/textview_text"
+ />
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button2_text"
+ android:textSize="32dip"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:horizontalDirection="rtl">
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button1_text"
+ android:textSize="32dip"
+ />
+
+ <TextView android:id="@+id/textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="32dip"
+ android:text="@string/textview_text"
+ />
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button2_text"
+ android:textSize="32dip"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:horizontalDirection="inherit">
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button1_text"
+ android:textSize="32dip"
+ />
+
+ <TextView android:id="@+id/textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="32dip"
+ android:text="@string/textview_text"
+ />
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button2_text"
+ android:textSize="32dip"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:horizontalDirection="ltr">
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button1_text"
+ android:textSize="32dip"
+ />
+
+ <TextView android:id="@+id/textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="32dip"
+ android:text="@string/textview_text"
+ />
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button2_text"
+ android:textSize="32dip"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:horizontalDirection="rtl">
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button1_text"
+ android:textSize="32dip"
+ />
+
+ <TextView android:id="@+id/textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="32dip"
+ android:text="@string/textview_text"
+ />
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button2_text"
+ android:textSize="32dip"
+ />
+
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/linear_layout_rtl.xml b/tests/BiDiTests/res/layout/linear_layout_rtl.xml
new file mode 100644
index 0000000..0d60950
--- /dev/null
+++ b/tests/BiDiTests/res/layout/linear_layout_rtl.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/layouttest"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:horizontalDirection="rtl">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:horizontalDirection="inherit">
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button1_text"
+ android:textSize="32dip"
+ />
+
+ <TextView android:id="@+id/textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="32dip"
+ android:text="@string/textview_text"
+ />
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button2_text"
+ android:textSize="32dip"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:horizontalDirection="ltr">
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button1_text"
+ android:textSize="32dip"
+ />
+
+ <TextView android:id="@+id/textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="32dip"
+ android:text="@string/textview_text"
+ />
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button2_text"
+ android:textSize="32dip"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:horizontalDirection="rtl">
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button1_text"
+ android:textSize="32dip"
+ />
+
+ <TextView android:id="@+id/textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="32dip"
+ android:text="@string/textview_text"
+ />
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button2_text"
+ android:textSize="32dip"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:horizontalDirection="inherit">
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button1_text"
+ android:textSize="32dip"
+ />
+
+ <TextView android:id="@+id/textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="32dip"
+ android:text="@string/textview_text"
+ />
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button2_text"
+ android:textSize="32dip"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:horizontalDirection="ltr">
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button1_text"
+ android:textSize="32dip"
+ />
+
+ <TextView android:id="@+id/textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="32dip"
+ android:text="@string/textview_text"
+ />
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button2_text"
+ android:textSize="32dip"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:horizontalDirection="rtl">
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button1_text"
+ android:textSize="32dip"
+ />
+
+ <TextView android:id="@+id/textview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="32dip"
+ android:text="@string/textview_text"
+ />
+
+ <Button android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/button2_text"
+ android:textSize="32dip"
+ />
+
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/main.xml b/tests/BiDiTests/res/layout/main.xml
new file mode 100644
index 0000000..e39d1d6
--- /dev/null
+++ b/tests/BiDiTests/res/layout/main.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/tabhost"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="5dp">
+
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="5dp" />
+
+ </LinearLayout>
+
+</TabHost>
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
index c272df1..3795d4b 100644
--- a/tests/BiDiTests/res/values/strings.xml
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -14,6 +14,9 @@
-->
<resources>
<string name="button_text">Button</string>
+ <string name="button1_text">Button1</string>
+ <string name="button2_text">Button2</string>
+ <string name="button_requestlayout_text">Request Layout</string>
<string name="textview_text">This is a text for a TextView</string>
<string name="edittext_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
<string name="normal_text">Normal String</string>
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
index 6c71574..0d9b4f7 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -16,56 +16,43 @@
package com.android.bidi;
-import android.app.Activity;
+import android.app.TabActivity;
+import android.content.Intent;
import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.SeekBar;
+import android.widget.TabHost;
-import static com.android.bidi.BiDiTestConstants.FONT_MIN_SIZE;
-import static com.android.bidi.BiDiTestConstants.FONT_MAX_SIZE;
-
-public class BiDiTestActivity extends Activity {
-
- static final String TAG = "BiDiTestActivity";
-
- static final int INIT_TEXT_SIZE = (FONT_MAX_SIZE - FONT_MIN_SIZE) / 2;
-
- private BiDiTestView textView;
- private SeekBar textSizeSeekBar;
+public class BiDiTestActivity extends TabActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.biditest_main);
+ setContentView(R.layout.main);
- textView = (BiDiTestView) findViewById(R.id.main);
- textView.setCurrentTextSize(INIT_TEXT_SIZE);
+ TabHost tabHost = getTabHost();
+ TabHost.TabSpec spec;
+ Intent intent;
- textSizeSeekBar = (SeekBar) findViewById(R.id.seekbar);
- textSizeSeekBar.setProgress(INIT_TEXT_SIZE);
- textSizeSeekBar.setMax(FONT_MAX_SIZE - FONT_MIN_SIZE);
+ // Create an Intent to launch an Activity for the tab (to be reused)
+ intent = new Intent().setClass(this, BiDiTestBasicActivity.class);
- textSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- textView.setCurrentTextSize(FONT_MIN_SIZE + progress);
- }
+ // Initialize a TabSpec for each tab and add it to the TabHost
+ spec = tabHost.newTabSpec("basic").setIndicator("Basic").setContent(intent);
+ tabHost.addTab(spec);
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
+ // Do the same for the other tabs
+ intent = new Intent().setClass(this, BiDiTestCanvasActivity.class);
+ spec = tabHost.newTabSpec("canvas").setIndicator("Canvas").setContent(intent);
+ tabHost.addTab(spec);
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- }
+ intent = new Intent().setClass(this, BiDiTestLinearLayoutLtrActivity.class);
+ spec = tabHost.newTabSpec("layout-ltr").setIndicator("LinearLayout LTR").setContent(intent);
+ tabHost.addTab(spec);
- @Override
- protected void onResume() {
- super.onResume();
- }
+ intent = new Intent().setClass(this, BiDiTestLinearLayoutRtlActivity.class);
+ spec = tabHost.newTabSpec("layout-rtl").setIndicator("LinearLayout RTL").setContent(intent);
+ tabHost.addTab(spec);
- public void onButtonClick(View v) {
- Log.v(TAG, "onButtonClick");
+ tabHost.setCurrentTab(0);
}
}
\ No newline at end of file
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestBasicActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestBasicActivity.java
new file mode 100644
index 0000000..2a8de04
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestBasicActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bidi;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class BiDiTestBasicActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.basic);
+ }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvasActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvasActivity.java
new file mode 100644
index 0000000..3ab75d5
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvasActivity.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bidi;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.SeekBar;
+
+import static com.android.bidi.BiDiTestConstants.FONT_MAX_SIZE;
+import static com.android.bidi.BiDiTestConstants.FONT_MIN_SIZE;
+
+public class BiDiTestCanvasActivity extends Activity {
+
+ static final int INIT_TEXT_SIZE = (FONT_MAX_SIZE - FONT_MIN_SIZE) / 2;
+
+ private BiDiTestView testView;
+ private SeekBar textSizeSeekBar;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.canvas);
+
+ testView = (BiDiTestView) findViewById(R.id.testview);
+ testView.setCurrentTextSize(INIT_TEXT_SIZE);
+
+ textSizeSeekBar = (SeekBar) findViewById(R.id.seekbar);
+ textSizeSeekBar.setProgress(INIT_TEXT_SIZE);
+ textSizeSeekBar.setMax(FONT_MAX_SIZE - FONT_MIN_SIZE);
+
+ textSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ testView.setCurrentTextSize(FONT_MIN_SIZE + progress);
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutLtrActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutLtrActivity.java
new file mode 100644
index 0000000..6d8f11d
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutLtrActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bidi;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+
+public class BiDiTestLinearLayoutLtrActivity extends Activity {
+
+ private LinearLayout layout;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.linear_layout_ltr);
+
+ layout = (LinearLayout) findViewById(R.id.layouttest);
+ }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutRtlActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutRtlActivity.java
new file mode 100644
index 0000000..0130793
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutRtlActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bidi;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+
+public class BiDiTestLinearLayoutRtlActivity extends Activity {
+
+ private LinearLayout layout;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.linear_layout_rtl);
+
+ layout = (LinearLayout) findViewById(R.id.layouttest);
+ }
+}
diff --git a/tests/DumpRenderTree2/assets/run_apache2.py b/tests/DumpRenderTree2/assets/run_apache2.py
index b4a8685..3806599 100755
--- a/tests/DumpRenderTree2/assets/run_apache2.py
+++ b/tests/DumpRenderTree2/assets/run_apache2.py
@@ -79,8 +79,8 @@
# 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;
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 134f29f..bd6da64 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -491,7 +491,7 @@
mNetworkInfo.setIsAvailable(false);
mLinkProperties.clear();
mLastBssid = null;
- mLastNetworkId = -1;
+ mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
mLastSignalLevel = -1;
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
@@ -837,11 +837,12 @@
}
public void connectNetwork(WifiConfiguration wifiConfig) {
- /* arg1 is used to indicate netId, force a netId value of -1 when
- * we are passing a configuration since the default value of
- * 0 is a valid netId
+ /* arg1 is used to indicate netId, force a netId value of
+ * WifiConfiguration.INVALID_NETWORK_ID when we are passing
+ * a configuration since the default value of 0 is a valid netId
*/
- sendMessage(obtainMessage(CMD_CONNECT_NETWORK, -1, 0, wifiConfig));
+ sendMessage(obtainMessage(CMD_CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
+ 0, wifiConfig));
}
public void saveNetwork(WifiConfiguration wifiConfig) {
@@ -1445,7 +1446,7 @@
mWifiInfo.setInetAddress(null);
mWifiInfo.setBSSID(null);
mWifiInfo.setSSID(null);
- mWifiInfo.setNetworkId(-1);
+ mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
mWifiInfo.setRssi(MIN_RSSI);
mWifiInfo.setLinkSpeed(-1);
@@ -1457,7 +1458,7 @@
mLinkProperties.clear();
mLastBssid= null;
- mLastNetworkId = -1;
+ mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
}
@@ -2050,7 +2051,7 @@
mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
/* Initialize data structures */
mLastBssid = null;
- mLastNetworkId = -1;
+ mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
mLastSignalLevel = -1;
mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
@@ -2545,7 +2546,10 @@
// Network id is only valid when we start connecting
if (SupplicantState.isConnecting(state)) {
mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ } else {
+ mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
}
+
if (state == SupplicantState.ASSOCIATING) {
/* BSSID is valid only in ASSOCIATING state */
mWifiInfo.setBSSID(stateChangeResult.BSSID);