diff options
Diffstat (limited to 'core')
133 files changed, 2112 insertions, 897 deletions
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index adfda8e..d25de97 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -18,6 +18,7 @@ package android.animation; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.DecelerateInterpolator; @@ -70,8 +71,9 @@ import java.util.List; * moving as a result of the layout event) as well as the values that are changing (such as the * position and size of that object). The actual values that are pushed to each animation * depends on what properties are specified for the animation. For example, the default - * CHANGE_APPEARING animation animates <code>left</code>, <code>top</code>, <code>right</code>, - * and <code>bottom</code>. Values for these properties are updated with the pre- and post-layout + * CHANGE_APPEARING animation animates the <code>left</code>, <code>top</code>, <code>right</code>, + * <code>bottom</code>, <code>scrollX</code>, and <code>scrollY</code> properties. + * Values for these properties are updated with the pre- and post-layout * values when the transition begins. Custom animations will be similarly populated with * the target and values being animated, assuming they use ObjectAnimator objects with * property names that are known on the target object.</p> @@ -210,6 +212,14 @@ public class LayoutTransition { */ private ArrayList<TransitionListener> mListeners; + /** + * Controls whether changing animations automatically animate the parent hierarchy as well. + * This behavior prevents artifacts when wrap_content layouts snap to the end state as the + * transition begins, causing visual glitches and clipping. + * Default value is true. + */ + private boolean mAnimateParentHierarchy = true; + /** * Constructs a LayoutTransition object. By default, the object will listen to layout @@ -223,14 +233,17 @@ public class LayoutTransition { PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1); PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 1); PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1); + PropertyValuesHolder pvhScrollX = PropertyValuesHolder.ofInt("scrollX", 0, 1); + PropertyValuesHolder pvhScrollY = PropertyValuesHolder.ofInt("scrollY", 0, 1); defaultChangeIn = ObjectAnimator.ofPropertyValuesHolder(this, - pvhLeft, pvhTop, pvhRight, pvhBottom); + pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScrollX, pvhScrollY); defaultChangeIn.setDuration(DEFAULT_DURATION); defaultChangeIn.setStartDelay(mChangingAppearingDelay); defaultChangeIn.setInterpolator(mChangingAppearingInterpolator); defaultChangeOut = defaultChangeIn.clone(); defaultChangeOut.setStartDelay(mChangingDisappearingDelay); defaultChangeOut.setInterpolator(mChangingDisappearingInterpolator); + defaultFadeIn = ObjectAnimator.ofFloat(this, "alpha", 0f, 1f); defaultFadeIn.setDuration(DEFAULT_DURATION); defaultFadeIn.setStartDelay(mAppearingDelay); @@ -572,122 +585,24 @@ public class LayoutTransition { // only animate the views not being added or removed if (child != newView) { - - - // Make a copy of the appropriate animation - final Animator anim = baseAnimator.clone(); - - // Set the target object for the animation - anim.setTarget(child); - - // A ObjectAnimator (or AnimatorSet of them) can extract start values from - // its target object - anim.setupStartValues(); - - // If there's an animation running on this view already, cancel it - Animator currentAnimation = pendingAnimations.get(child); - if (currentAnimation != null) { - currentAnimation.cancel(); - pendingAnimations.remove(child); + setupChangeAnimation(parent, changeReason, baseAnimator, duration, child); + } + } + if (mAnimateParentHierarchy) { + ViewGroup tempParent = parent; + while (tempParent != null) { + ViewParent parentParent = tempParent.getParent(); + if (parentParent instanceof ViewGroup) { + setupChangeAnimation((ViewGroup)parentParent, changeReason, baseAnimator, + duration, tempParent); + tempParent = (ViewGroup) parentParent; + } else { + tempParent = null; } - // Cache the animation in case we need to cancel it later - pendingAnimations.put(child, anim); - - // For the animations which don't get started, we have to have a means of - // removing them from the cache, lest we leak them and their target objects. - // We run an animator for the default duration+100 (an arbitrary time, but one - // which should far surpass the delay between setting them up here and - // handling layout events which start them. - ValueAnimator pendingAnimRemover = ValueAnimator.ofFloat(0f, 1f). - setDuration(duration+100); - pendingAnimRemover.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - pendingAnimations.remove(child); - } - }); - pendingAnimRemover.start(); - - // Add a listener to track layout changes on this view. If we don't get a callback, - // then there's nothing to animate. - final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() { - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - - // Tell the animation to extract end values from the changed object - anim.setupEndValues(); - - long startDelay; - if (changeReason == APPEARING) { - startDelay = mChangingAppearingDelay + staggerDelay; - staggerDelay += mChangingAppearingStagger; - } else { - startDelay = mChangingDisappearingDelay + staggerDelay; - staggerDelay += mChangingDisappearingStagger; - } - anim.setStartDelay(startDelay); - anim.setDuration(duration); - - Animator prevAnimation = currentChangingAnimations.get(child); - if (prevAnimation != null) { - prevAnimation.cancel(); - } - Animator pendingAnimation = pendingAnimations.get(child); - if (pendingAnimation != null) { - pendingAnimations.remove(child); - } - // Cache the animation in case we need to cancel it later - currentChangingAnimations.put(child, anim); - - if (anim instanceof ObjectAnimator) { - ((ObjectAnimator) anim).setCurrentPlayTime(0); - } - anim.start(); - - // this only removes listeners whose views changed - must clear the - // other listeners later - child.removeOnLayoutChangeListener(this); - layoutChangeListenerMap.remove(child); - } - }; - // Remove the animation from the cache when it ends - anim.addListener(new AnimatorListenerAdapter() { - - @Override - public void onAnimationStart(Animator animator) { - if (mListeners != null) { - for (TransitionListener listener : mListeners) { - listener.startTransition(LayoutTransition.this, parent, child, - changeReason == APPEARING ? - CHANGE_APPEARING : CHANGE_DISAPPEARING); - } - } - } - @Override - public void onAnimationCancel(Animator animator) { - child.removeOnLayoutChangeListener(listener); - layoutChangeListenerMap.remove(child); - } - - @Override - public void onAnimationEnd(Animator animator) { - currentChangingAnimations.remove(child); - if (mListeners != null) { - for (TransitionListener listener : mListeners) { - listener.endTransition(LayoutTransition.this, parent, child, - changeReason == APPEARING ? - CHANGE_APPEARING : CHANGE_DISAPPEARING); - } - } - } - }); - - child.addOnLayoutChangeListener(listener); - // cache the listener for later removal - layoutChangeListenerMap.put(child, listener); } } + // This is the cleanup step. When we get this rendering event, we know that all of // the appropriate animations have been set up and run. Now we can clear out the // layout listeners. @@ -706,6 +621,175 @@ public class LayoutTransition { } /** + * This flag controls whether CHANGE_APPEARING or CHANGE_DISAPPEARING animations will + * cause the same changing animation to be run on the parent hierarchy as well. This allows + * containers of transitioning views to also transition, which may be necessary in situations + * where the containers bounds change between the before/after states and may clip their + * children during the transition animations. For example, layouts with wrap_content will + * adjust their bounds according to the dimensions of their children. + * + * @param animateParentHierarchy A boolean value indicating whether the parents of + * transitioning views should also be animated during the transition. Default value is true. + */ + public void setAnimateParentHierarchy(boolean animateParentHierarchy) { + mAnimateParentHierarchy = animateParentHierarchy; + } + + /** + * Utility function called by runChangingTransition for both the children and the parent + * hierarchy. + */ + private void setupChangeAnimation(final ViewGroup parent, final int changeReason, + Animator baseAnimator, final long duration, final View child) { + // Make a copy of the appropriate animation + final Animator anim = baseAnimator.clone(); + + // Set the target object for the animation + anim.setTarget(child); + + // A ObjectAnimator (or AnimatorSet of them) can extract start values from + // its target object + anim.setupStartValues(); + + // If there's an animation running on this view already, cancel it + Animator currentAnimation = pendingAnimations.get(child); + if (currentAnimation != null) { + currentAnimation.cancel(); + pendingAnimations.remove(child); + } + // Cache the animation in case we need to cancel it later + pendingAnimations.put(child, anim); + + // For the animations which don't get started, we have to have a means of + // removing them from the cache, lest we leak them and their target objects. + // We run an animator for the default duration+100 (an arbitrary time, but one + // which should far surpass the delay between setting them up here and + // handling layout events which start them. + ValueAnimator pendingAnimRemover = ValueAnimator.ofFloat(0f, 1f). + setDuration(duration + 100); + pendingAnimRemover.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + pendingAnimations.remove(child); + } + }); + pendingAnimRemover.start(); + + // Add a listener to track layout changes on this view. If we don't get a callback, + // then there's nothing to animate. + final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() { + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + + // Tell the animation to extract end values from the changed object + anim.setupEndValues(); + if (anim instanceof ValueAnimator) { + boolean valuesDiffer = false; + ValueAnimator valueAnim = (ValueAnimator)anim; + PropertyValuesHolder[] oldValues = valueAnim.getValues(); + for (int i = 0; i < oldValues.length; ++i) { + PropertyValuesHolder pvh = oldValues[i]; + KeyframeSet keyframeSet = pvh.mKeyframeSet; + if (keyframeSet.mFirstKeyframe == null || + keyframeSet.mLastKeyframe == null || + !keyframeSet.mFirstKeyframe.getValue().equals( + keyframeSet.mLastKeyframe.getValue())) { + valuesDiffer = true; + } + } + if (!valuesDiffer) { + return; + } + } + + long startDelay; + if (changeReason == APPEARING) { + startDelay = mChangingAppearingDelay + staggerDelay; + staggerDelay += mChangingAppearingStagger; + } else { + startDelay = mChangingDisappearingDelay + staggerDelay; + staggerDelay += mChangingDisappearingStagger; + } + anim.setStartDelay(startDelay); + anim.setDuration(duration); + + Animator prevAnimation = currentChangingAnimations.get(child); + if (prevAnimation != null) { + prevAnimation.cancel(); + } + Animator pendingAnimation = pendingAnimations.get(child); + if (pendingAnimation != null) { + pendingAnimations.remove(child); + } + // Cache the animation in case we need to cancel it later + currentChangingAnimations.put(child, anim); + + parent.requestTransitionStart(LayoutTransition.this); + + // this only removes listeners whose views changed - must clear the + // other listeners later + child.removeOnLayoutChangeListener(this); + layoutChangeListenerMap.remove(child); + } + }; + // Remove the animation from the cache when it ends + anim.addListener(new AnimatorListenerAdapter() { + + @Override + public void onAnimationStart(Animator animator) { + if (mListeners != null) { + for (TransitionListener listener : mListeners) { + listener.startTransition(LayoutTransition.this, parent, child, + changeReason == APPEARING ? + CHANGE_APPEARING : CHANGE_DISAPPEARING); + } + } + } + + @Override + public void onAnimationCancel(Animator animator) { + child.removeOnLayoutChangeListener(listener); + layoutChangeListenerMap.remove(child); + } + + @Override + public void onAnimationEnd(Animator animator) { + currentChangingAnimations.remove(child); + if (mListeners != null) { + for (TransitionListener listener : mListeners) { + listener.endTransition(LayoutTransition.this, parent, child, + changeReason == APPEARING ? + CHANGE_APPEARING : CHANGE_DISAPPEARING); + } + } + } + }); + + child.addOnLayoutChangeListener(listener); + // cache the listener for later removal + layoutChangeListenerMap.put(child, listener); + } + + /** + * Starts the animations set up for a CHANGING transition. We separate the setup of these + * animations from actually starting them, to avoid side-effects that starting the animations + * may have on the properties of the affected objects. After setup, we tell the affected parent + * that this transition should be started. The parent informs its ViewAncestor, which then + * starts the transition after the current layout/measurement phase, just prior to drawing + * the view hierarchy. + * + * @hide + */ + public void startChangingAnimations() { + for (Animator anim : currentChangingAnimations.values()) { + if (anim instanceof ObjectAnimator) { + ((ObjectAnimator) anim).setCurrentPlayTime(0); + } + anim.start(); + } + } + + /** * Returns true if animations are running which animate layout-related properties. This * essentially means that either CHANGE_APPEARING or CHANGE_DISAPPEARING animations * are running, since these animations operate on layout-related properties. diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 87369ab..3877bd0 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1398,6 +1398,10 @@ public class Activity extends ContextThemeWrapper public void onConfigurationChanged(Configuration newConfig) { mCalled = true; + if (mActionBar != null) { + mActionBar.onConfigurationChanged(newConfig); + } + mFragments.dispatchConfigurationChanged(newConfig); if (mWindow != null) { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index f2c9796..2a0d798 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1467,6 +1467,22 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case REGISTER_PROCESS_OBSERVER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IProcessObserver observer = IProcessObserver.Stub.asInterface( + data.readStrongBinder()); + registerProcessObserver(observer); + return true; + } + + case UNREGISTER_PROCESS_OBSERVER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IProcessObserver observer = IProcessObserver.Stub.asInterface( + data.readStrongBinder()); + unregisterProcessObserver(observer); + return true; + } + } return super.onTransact(code, data, reply, flags); @@ -3300,5 +3316,27 @@ class ActivityManagerProxy implements IActivityManager return result; } + public void registerProcessObserver(IProcessObserver observer) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(observer != null ? observer.asBinder() : null); + mRemote.transact(REGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(observer != null ? observer.asBinder() : null); + mRemote.transact(UNREGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 54c3422..1f53c0e 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -355,6 +355,9 @@ public interface IActivityManager extends IInterface { public boolean removeTask(int taskId, int flags) throws RemoteException; + public void registerProcessObserver(IProcessObserver observer) throws RemoteException; + public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -577,4 +580,6 @@ public interface IActivityManager extends IInterface { int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+127; int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+128; int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+129; + int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+130; + int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131; } diff --git a/core/java/android/app/IProcessObserver.aidl b/core/java/android/app/IProcessObserver.aidl new file mode 100644 index 0000000..2094294 --- /dev/null +++ b/core/java/android/app/IProcessObserver.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +/** {@hide} */ +oneway interface IProcessObserver { + + void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities); + void onProcessDied(int pid, int uid); + +} diff --git a/core/java/android/app/IThumbnailRetriever.aidl b/core/java/android/app/IThumbnailRetriever.aidl index 2a6737d..410cc20 100644 --- a/core/java/android/app/IThumbnailRetriever.aidl +++ b/core/java/android/app/IThumbnailRetriever.aidl @@ -18,6 +18,7 @@ import android.graphics.Bitmap; /** * System private API for retrieving thumbnails + * {@hide} */ interface IThumbnailRetriever { Bitmap getThumbnail(int index); diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 95451d6..71f6445 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -167,7 +167,8 @@ public class UiModeManager { * Return the current running mode type. May be one of * {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL}, * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, or - * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, + * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, or + * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_TV}. */ public int getCurrentModeType() { if (mService != null) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 4c7d87f..a660bd7 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1544,6 +1544,9 @@ public abstract class Context { */ public static final String NETWORKMANAGEMENT_SERVICE = "network_management"; + /** {@hide} */ + public static final String NETWORK_POLICY_SERVICE = "netpolicy"; + /** * Use with {@link #getSystemService} to retrieve a {@link * android.net.wifi.WifiManager} for handling management of diff --git a/core/java/android/content/IOnPrimaryClipChangedListener.aidl b/core/java/android/content/IOnPrimaryClipChangedListener.aidl index fb42a45..46d7f7c 100644 --- a/core/java/android/content/IOnPrimaryClipChangedListener.aidl +++ b/core/java/android/content/IOnPrimaryClipChangedListener.aidl @@ -16,6 +16,9 @@ package android.content; +/** + * {@hide} + */ oneway interface IOnPrimaryClipChangedListener { void dispatchPrimaryClipChanged(); } diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 12ec258..51a7115 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -226,6 +226,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration public static final int UI_MODE_TYPE_NORMAL = 0x01; public static final int UI_MODE_TYPE_DESK = 0x02; public static final int UI_MODE_TYPE_CAR = 0x03; + public static final int UI_MODE_TYPE_TELEVISION = 0x04; public static final int UI_MODE_NIGHT_MASK = 0x30; public static final int UI_MODE_NIGHT_UNDEFINED = 0x00; @@ -367,6 +368,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break; case UI_MODE_TYPE_DESK: sb.append(" desk"); break; case UI_MODE_TYPE_CAR: sb.append(" car"); break; + case UI_MODE_TYPE_TELEVISION: sb.append(" television"); break; default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break; } switch ((uiMode&UI_MODE_NIGHT_MASK)) { diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index fa6eae5..d9351ee 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -23,9 +23,6 @@ package android.net; */ interface INetworkPolicyManager { - void onForegroundActivitiesChanged(int uid, int pid, boolean foregroundActivities); - void onProcessDied(int uid, int pid); - void setUidPolicy(int uid, int policy); int getUidPolicy(int uid); diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 2312bd9..1913aa7 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -16,6 +16,7 @@ package android.net; +import android.content.Context; import android.os.RemoteException; /** @@ -43,6 +44,10 @@ public class NetworkPolicyManager { mService = service; } + public static NetworkPolicyManager getSystemService(Context context) { + return (NetworkPolicyManager) context.getSystemService(Context.NETWORK_POLICY_SERVICE); + } + /** * Set policy flags for specific UID. * diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 4430e00..0f207bc 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -36,6 +36,9 @@ public class NetworkStats implements Parcelable { /** {@link #uid} value when entry is summarized over all UIDs. */ public static final int UID_ALL = 0; + // NOTE: data should only be accounted for once in this structure; if data + // is broken out, the summarized version should not be included. + /** * {@link SystemClock#elapsedRealtime()} timestamp when this data was * generated. @@ -81,12 +84,13 @@ public class NetworkStats implements Parcelable { mTx = new long[size]; } - public void addEntry(String iface, int uid, long rx, long tx) { + public Builder addEntry(String iface, int uid, long rx, long tx) { mIface[mIndex] = iface; mUid[mIndex] = uid; mRx[mIndex] = rx; mTx[mIndex] = tx; mIndex++; + return this; } public NetworkStats build() { @@ -97,11 +101,17 @@ public class NetworkStats implements Parcelable { } } + public int length() { + // length is identical for all fields + return iface.length; + } + /** * Find first stats index that matches the requested parameters. */ public int findIndex(String iface, int uid) { - for (int i = 0; i < this.iface.length; i++) { + final int length = length(); + for (int i = 0; i < length; i++) { if (equal(iface, this.iface[i]) && uid == this.uid[i]) { return i; } @@ -109,13 +119,38 @@ public class NetworkStats implements Parcelable { return -1; } - private static boolean equal(Object a, Object b) { - return a == b || (a != null && a.equals(b)); + /** + * Subtract the given {@link NetworkStats}, effectively leaving the delta + * between two snapshots in time. Assumes that statistics rows collect over + * time, and that none of them have disappeared. + */ + public NetworkStats subtract(NetworkStats value) { + // result will have our rows, but no meaningful timestamp + final int length = length(); + final NetworkStats.Builder result = new NetworkStats.Builder(-1, length); + + for (int i = 0; i < length; i++) { + final String iface = this.iface[i]; + final int uid = this.uid[i]; + + // find remote row that matches, and subtract + final int j = value.findIndex(iface, uid); + if (j == -1) { + // newly appearing row, return entire value + result.addEntry(iface, uid, this.rx[i], this.tx[i]); + } else { + // existing row, subtract remote value + final long rx = this.rx[i] - value.rx[j]; + final long tx = this.tx[i] - value.tx[j]; + result.addEntry(iface, uid, rx, tx); + } + } + + return result.build(); } - /** {@inheritDoc} */ - public int describeContents() { - return 0; + private static boolean equal(Object a, Object b) { + return a == b || (a != null && a.equals(b)); } public void dump(String prefix, PrintWriter pw) { @@ -138,6 +173,11 @@ public class NetworkStats implements Parcelable { } /** {@inheritDoc} */ + public int describeContents() { + return 0; + } + + /** {@inheritDoc} */ public void writeToParcel(Parcel dest, int flags) { dest.writeLong(elapsedRealtime); dest.writeStringArray(iface); diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 7ee7a81..c0ff734 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -16,6 +16,12 @@ package android.net; +import android.content.Context; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.RemoteException; +import android.os.ServiceManager; + import dalvik.system.BlockGuard; import java.net.Socket; @@ -36,6 +42,17 @@ public class TrafficStats { public final static int UNSUPPORTED = -1; /** + * Snapshot of {@link NetworkStats} when the currently active profiling + * session started, or {@code null} if no session active. + * + * @see #startDataProfiling(Context) + * @see #stopDataProfiling(Context) + */ + private static NetworkStats sActiveProfilingStart; + + private static Object sProfilingLock = new Object(); + + /** * Set active tag to use when accounting {@link Socket} traffic originating * from the current thread. Only one active tag per thread is supported. * <p> @@ -93,6 +110,44 @@ public class TrafficStats { } /** + * Start profiling data usage for current UID. Only one profiling session + * can be active at a time. + * + * @hide + */ + public static void startDataProfiling(Context context) { + synchronized (sProfilingLock) { + if (sActiveProfilingStart != null) { + throw new IllegalStateException("already profiling data"); + } + + // take snapshot in time; we calculate delta later + sActiveProfilingStart = getNetworkStatsForUid(context); + } + } + + /** + * Stop profiling data usage for current UID. + * + * @return Detailed {@link NetworkStats} of data that occurred since last + * {@link #startDataProfiling(Context)} call. + * @hide + */ + public static NetworkStats stopDataProfiling(Context context) { + synchronized (sProfilingLock) { + if (sActiveProfilingStart == null) { + throw new IllegalStateException("not profiling data"); + } + + // subtract starting values and return delta + final NetworkStats profilingStop = getNetworkStatsForUid(context); + final NetworkStats profilingDelta = profilingStop.subtract(sActiveProfilingStart); + sActiveProfilingStart = null; + return profilingDelta; + } + } + + /** * Get the total number of packets transmitted through the mobile interface. * * @return number of packets. If the statistics are not supported by this device, @@ -350,4 +405,21 @@ public class TrafficStats { * {@link #UNSUPPORTED} will be returned. */ public static native long getUidUdpRxPackets(int uid); + + /** + * Return detailed {@link NetworkStats} for the current UID. Requires no + * special permission. + */ + private static NetworkStats getNetworkStatsForUid(Context context) { + final IBinder binder = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + final INetworkManagementService service = INetworkManagementService.Stub.asInterface( + binder); + + final int uid = android.os.Process.myUid(); + try { + return service.getNetworkStatsUidDetail(uid); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } } diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/INdefPushCallback.aidl new file mode 100644 index 0000000..80ba2ed --- /dev/null +++ b/core/java/android/nfc/INdefPushCallback.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc; + +import android.nfc.NdefMessage; + +/** + * @hide + */ +interface INdefPushCallback +{ + NdefMessage onConnect(); + void onMessagePushed(); +} diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index 870127c..d11fea0 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -25,6 +25,7 @@ import android.nfc.TechListParcel; import android.nfc.ILlcpSocket; import android.nfc.ILlcpServiceSocket; import android.nfc.ILlcpConnectionlessSocket; +import android.nfc.INdefPushCallback; import android.nfc.INfcTag; import android.nfc.IP2pTarget; import android.nfc.IP2pInitiator; @@ -51,6 +52,7 @@ interface INfcAdapter in IntentFilter[] filters, in TechListParcel techLists); void disableForegroundDispatch(in ComponentName activity); void enableForegroundNdefPush(in ComponentName activity, in NdefMessage msg); + void enableForegroundNdefPushWithCallback(in ComponentName activity, in INdefPushCallback callback); void disableForegroundNdefPush(in ComponentName activity); // Non-public methods diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 4689804..738e75f 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -124,7 +124,7 @@ public final class NfcAdapter { * Intent to start an activity when a tag is discovered. * * <p>This intent will not be started when a tag is discovered if any activities respond to - * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag. + * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"; @@ -235,6 +235,37 @@ public final class NfcAdapter { */ private static final int DISCOVERY_MODE_CARD_EMULATION = 2; + /** + * Callback passed into {@link #enableForegroundNdefPush(Activity,NdefPushCallback)}. This + */ + public interface NdefPushCallback { + /** + * Called when a P2P connection is created. + */ + NdefMessage createMessage(); + /** + * Called when the message is pushed. + */ + void onMessagePushed(); + } + + private static class NdefPushCallbackWrapper extends INdefPushCallback.Stub { + private NdefPushCallback mCallback; + + public NdefPushCallbackWrapper(NdefPushCallback callback) { + mCallback = callback; + } + + @Override + public NdefMessage onConnect() { + return mCallback.createMessage(); + } + + @Override + public void onMessagePushed() { + mCallback.onMessagePushed(); + } + } // Guarded by NfcAdapter.class private static boolean sIsInitialized = false; @@ -575,6 +606,44 @@ public final class NfcAdapter { } /** + * Enable NDEF message push over P2P while this Activity is in the foreground. + * + * <p>For this to function properly the other NFC device being scanned must + * support the "com.android.npp" NDEF push protocol. Support for this + * protocol is currently optional for Android NFC devices. + * + * <p>This method must be called from the main thread. + * + * <p class="note"><em>NOTE:</em> While foreground NDEF push is active standard tag dispatch is disabled. + * Only the foreground activity may receive tag discovered dispatches via + * {@link #enableForegroundDispatch}. + * + * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. + * + * @param activity the foreground Activity + * @param callback is called on when the P2P connection is established + * @throws IllegalStateException if the Activity is not currently in the foreground + * @throws OperationNotSupportedException if this Android device does not support NDEF push + */ + public void enableForegroundNdefPush(Activity activity, NdefPushCallback callback) { + if (activity == null || callback == null) { + throw new NullPointerException(); + } + if (!activity.isResumed()) { + throw new IllegalStateException("Foregorund NDEF push can only be enabled " + + "when your activity is resumed"); + } + try { + ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, + mForegroundNdefPushListener); + sService.enableForegroundNdefPushWithCallback(activity.getComponentName(), + new NdefPushCallbackWrapper(callback)); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + } + } + + /** * Disable NDEF message push over P2P. * * <p>After calling {@link #enableForegroundNdefPush}, an activity diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index ecc111b..f17a6f2 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -214,6 +214,12 @@ interface INetworkManagementService NetworkStats getNetworkStatsDetail(); /** + * Return detailed network statistics for the requested UID, + * including interface and tag details. + */ + NetworkStats getNetworkStatsUidDetail(int uid); + + /** * Configures bandwidth throttling on an interface. */ void setInterfaceThrottle(String iface, int rxKbps, int txKbps); diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 727fcca..3ea3f56 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -129,7 +129,46 @@ public class ParcelFileDescriptor implements Parcelable { } /** - * Create a new ParcelFileDescriptor from the specified Socket. + * Create a new ParcelFileDescriptor from a raw native fd. The new + * ParcelFileDescriptor holds a dup of the original fd passed in here, + * so you must still close that fd as well as the new ParcelFileDescriptor. + * + * @param fd The native fd that the ParcelFileDescriptor should dup. + * + * @return Returns a new ParcelFileDescriptor holding a FileDescriptor + * for a dup of the given fd. + */ + public static ParcelFileDescriptor fromFd(int fd) throws IOException { + FileDescriptor fdesc = getFileDescriptorFromFd(fd); + return new ParcelFileDescriptor(fdesc); + } + + // Extracts the file descriptor from the specified socket and returns it untouched + private static native FileDescriptor getFileDescriptorFromFd(int fd) throws IOException; + + /** + * Take ownership of a raw native fd in to a new ParcelFileDescriptor. + * The returned ParcelFileDescriptor now owns the given fd, and will be + * responsible for closing it. You must not close the fd yourself. + * + * @param fd The native fd that the ParcelFileDescriptor should adopt. + * + * @return Returns a new ParcelFileDescriptor holding a FileDescriptor + * for the given fd. + */ + public static ParcelFileDescriptor adoptFd(int fd) { + FileDescriptor fdesc = getFileDescriptorFromFdNoDup(fd); + return new ParcelFileDescriptor(fdesc); + } + + // Extracts the file descriptor from the specified socket and returns it untouched + private static native FileDescriptor getFileDescriptorFromFdNoDup(int fd); + + /** + * Create a new ParcelFileDescriptor from the specified Socket. The new + * ParcelFileDescriptor holds a dup of the original FileDescriptor in + * the Socket, so you must still close the Socket as well as the new + * ParcelFileDescriptor. * * @param socket The Socket whose FileDescriptor is used to create * a new ParcelFileDescriptor. @@ -163,17 +202,14 @@ public class ParcelFileDescriptor implements Parcelable { */ public static ParcelFileDescriptor[] createPipe() throws IOException { FileDescriptor[] fds = new FileDescriptor[2]; - int res = createPipeNative(fds); - if (res == 0) { - ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2]; - pfds[0] = new ParcelFileDescriptor(fds[0]); - pfds[1] = new ParcelFileDescriptor(fds[1]); - return pfds; - } - throw new IOException("Unable to create pipe: errno=" + -res); + createPipeNative(fds); + ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2]; + pfds[0] = new ParcelFileDescriptor(fds[0]); + pfds[1] = new ParcelFileDescriptor(fds[1]); + return pfds; } - private static native int createPipeNative(FileDescriptor[] outFds); + private static native void createPipeNative(FileDescriptor[] outFds) throws IOException; /** * @hide Please use createPipe() or ContentProvider.openPipeHelper(). diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index f85df6c..d475f36 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -634,6 +634,20 @@ public class Process { } /** + * Returns the parent process id for a currently running process. + * @param pid the process id + * @return the parent process id of the process, or -1 if the process is not running. + * @hide + */ + public static final int getParentPid(int pid) { + String[] procStatusLabels = { "PPid:" }; + long[] procStatusValues = new long[1]; + procStatusValues[0] = -1; + Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues); + return (int) procStatusValues[0]; + } + + /** * Set the priority of a thread, based on Linux priorities. * * @param tid The identifier of the thread/process to change. diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 1375a29..01c640a 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -1481,6 +1481,13 @@ public final class StrictMode { onVmPolicyViolation(message, originStack); } + /** + * @hide + */ + public static void onWebViewMethodCalledOnWrongThread(Throwable originStack) { + onVmPolicyViolation(null, originStack); + } + // Map from VM violation fingerprint to uptime millis. private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>(); diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index d68e6fb..bc6e993 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -36,6 +36,11 @@ public class StorageVolume implements Parcelable { private final int mMtpReserveSpace; private int mStorageId; + // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING, + // ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED, + // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts. + public static final String EXTRA_STORAGE_VOLUME = "storage_volume"; + public StorageVolume(String path, String description, boolean removable, boolean emulated, int mtpReserveSpace) { mPath = path; diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java index 4141879..20614dc 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -616,18 +616,6 @@ public final class Calendar { public static final String DTEND = "dtend"; /** - * The time the event starts with allDay events in a local tz - * <P>Type: INTEGER (long; millis since epoch)</P> - */ - public static final String DTSTART2 = "dtstart2"; - - /** - * The time the event ends with allDay events in a local tz - * <P>Type: INTEGER (long; millis since epoch)</P> - */ - public static final String DTEND2 = "dtend2"; - - /** * The duration of the event * <P>Type: TEXT (duration in RFC2445 format)</P> */ @@ -734,8 +722,16 @@ public final class Calendar { public static final String EXDATE = "exdate"; /** + * The _id of the original recurring event for which this event is an + * exception. + * <P>Type: TEXT</P> + */ + public static final String ORIGINAL_ID = "original_id"; + + /** * The _sync_id of the original recurring event for which this event is - * an exception. + * an exception. The provider should keep the original_id in sync when + * this is updated. * <P>Type: TEXT</P> */ public static final String ORIGINAL_SYNC_ID = "original_sync_id"; @@ -899,6 +895,7 @@ public final class Calendar { DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DTEND); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DURATION); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_TIMEZONE); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_END_TIMEZONE); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ALL_DAY); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, AVAILABILITY); @@ -910,6 +907,7 @@ public final class Calendar { DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXRULE); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXDATE); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_SYNC_ID); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_ID); DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, ORIGINAL_INSTANCE_TIME); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ORIGINAL_ALL_DAY); @@ -925,7 +923,7 @@ public final class Calendar { DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED); - DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1); + DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC1); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Events.SYNC_DATA1); diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 2c79385..60bee9a 100644..100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -1104,7 +1104,7 @@ public class BluetoothService extends IBluetooth.Stub { } /*package*/ synchronized boolean setBondState(String address, int state, int reason) { - mBondState.setBondState(address.toUpperCase(), state); + mBondState.setBondState(address.toUpperCase(), state, reason); return true; } diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java index 6f4c15b..d698b54 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java +++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java @@ -18,6 +18,7 @@ package android.speech.tts; import android.media.AudioFormat; import android.media.AudioTrack; import android.os.Bundle; +import android.os.Handler; import android.util.Log; /** @@ -49,16 +50,20 @@ class PlaybackSynthesisRequest extends SynthesisRequest { private final float mPan; private final Object mStateLock = new Object(); - private AudioTrack mAudioTrack = null; + private final Handler mAudioTrackHandler; + private volatile AudioTrack mAudioTrack = null; private boolean mStopped = false; private boolean mDone = false; + private volatile boolean mWriteErrorOccured; PlaybackSynthesisRequest(String text, Bundle params, - int streamType, float volume, float pan) { + int streamType, float volume, float pan, Handler audioTrackHandler) { super(text, params); mStreamType = streamType; mVolume = volume; mPan = pan; + mAudioTrackHandler = audioTrackHandler; + mWriteErrorOccured = false; } @Override @@ -70,14 +75,28 @@ class PlaybackSynthesisRequest extends SynthesisRequest { } } + // Always guarded by mStateLock. private void cleanUp() { if (DBG) Log.d(TAG, "cleanUp()"); - if (mAudioTrack != null) { - mAudioTrack.flush(); - mAudioTrack.stop(); - mAudioTrack.release(); - mAudioTrack = null; + if (mAudioTrack == null) { + return; } + + final AudioTrack audioTrack = mAudioTrack; + mAudioTrack = null; + + // Clean up on the audiotrack handler thread. + // + // NOTE: It isn't very clear whether AudioTrack is thread safe. + // If it is we can clean up on the current (synthesis) thread. + mAudioTrackHandler.post(new Runnable() { + @Override + public void run() { + audioTrack.flush(); + audioTrack.stop(); + audioTrack.release(); + } + }); } @Override @@ -146,10 +165,15 @@ class PlaybackSynthesisRequest extends SynthesisRequest { Log.d(TAG, "audioAvailable(byte[" + buffer.length + "]," + offset + "," + length + ")"); } - if (length > getMaxBufferSize()) { - throw new IllegalArgumentException("buffer is too large (" + length + " bytes)"); + if (length > getMaxBufferSize() || length <= 0) { + throw new IllegalArgumentException("buffer is too large or of zero length (" + + + length + " bytes)"); } synchronized (mStateLock) { + if (mWriteErrorOccured) { + if (DBG) Log.d(TAG, "Error writing to audio track, count < 0"); + return TextToSpeech.ERROR; + } if (mStopped) { if (DBG) Log.d(TAG, "Request has been aborted."); return TextToSpeech.ERROR; @@ -158,22 +182,33 @@ class PlaybackSynthesisRequest extends SynthesisRequest { Log.e(TAG, "audioAvailable(): Not started"); return TextToSpeech.ERROR; } - int playState = mAudioTrack.getPlayState(); - if (playState == AudioTrack.PLAYSTATE_STOPPED) { - if (DBG) Log.d(TAG, "AudioTrack stopped, restarting"); - mAudioTrack.play(); - } - // TODO: loop until all data is written? - if (DBG) Log.d(TAG, "AudioTrack.write()"); - int count = mAudioTrack.write(buffer, offset, length); - if (DBG) Log.d(TAG, "AudioTrack.write() returned " + count); - if (count < 0) { - Log.e(TAG, "Writing to AudioTrack failed: " + count); - cleanUp(); - return TextToSpeech.ERROR; - } else { - return TextToSpeech.SUCCESS; - } + final AudioTrack audioTrack = mAudioTrack; + // Sigh, another copy. + final byte[] bufferCopy = new byte[length]; + System.arraycopy(buffer, offset, bufferCopy, 0, length); + + mAudioTrackHandler.post(new Runnable() { + @Override + public void run() { + int playState = audioTrack.getPlayState(); + if (playState == AudioTrack.PLAYSTATE_STOPPED) { + if (DBG) Log.d(TAG, "AudioTrack stopped, restarting"); + audioTrack.play(); + } + // TODO: loop until all data is written? + if (DBG) Log.d(TAG, "AudioTrack.write()"); + int count = audioTrack.write(bufferCopy, 0, bufferCopy.length); + // The semantics of this change very slightly. Earlier, we would + // report an error immediately, Now we will return an error on + // the next API call, usually done( ) or another audioAvailable( ) + // call. + if (count < 0) { + mWriteErrorOccured = true; + } + } + }); + + return TextToSpeech.SUCCESS; } } @@ -181,6 +216,10 @@ class PlaybackSynthesisRequest extends SynthesisRequest { public int done() { if (DBG) Log.d(TAG, "done()"); synchronized (mStateLock) { + if (mWriteErrorOccured) { + if (DBG) Log.d(TAG, "Error writing to audio track, count < 0"); + return TextToSpeech.ERROR; + } if (mStopped) { if (DBG) Log.d(TAG, "Request has been aborted."); return TextToSpeech.ERROR; diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index f32474f..717dde8 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -51,6 +51,7 @@ public abstract class TextToSpeechService extends Service { private static final String SYNTH_THREAD_NAME = "SynthThread"; private SynthHandler mSynthHandler; + private Handler mAudioTrackHandler; private CallbackMap mCallbacks; @@ -63,6 +64,10 @@ public abstract class TextToSpeechService extends Service { synthThread.start(); mSynthHandler = new SynthHandler(synthThread.getLooper()); + HandlerThread audioTrackThread = new HandlerThread("TTS.audioTrackThread"); + audioTrackThread.start(); + mAudioTrackHandler = new Handler(audioTrackThread.getLooper()); + mCallbacks = new CallbackMap(); // Load default language @@ -75,6 +80,7 @@ public abstract class TextToSpeechService extends Service { // Tell the synthesizer to stop mSynthHandler.quit(); + mAudioTrackHandler.getLooper().quit(); // Unregister all callbacks. mCallbacks.kill(); @@ -444,7 +450,7 @@ public abstract class TextToSpeechService extends Service { protected SynthesisRequest createSynthesisRequest() { return new PlaybackSynthesisRequest(mText, mParams, - getStreamType(), getVolume(), getPan()); + getStreamType(), getVolume(), getPan(), mAudioTrackHandler); } private void setRequestParams(SynthesisRequest request) { diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java index d432dee..fe96565 100644 --- a/core/java/android/text/method/ArrowKeyMovementMethod.java +++ b/core/java/android/text/method/ArrowKeyMovementMethod.java @@ -234,7 +234,7 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme if (action == MotionEvent.ACTION_DOWN) { boolean cap = isSelecting(buffer); if (cap) { - int offset = widget.getOffset((int) event.getX(), (int) event.getY()); + int offset = widget.getOffsetForPosition(event.getX(), event.getY()); buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT); @@ -259,7 +259,7 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme // Update selection as we're moving the selection area. // Get the current touch position - int offset = widget.getOffset((int) event.getX(), (int) event.getY()); + int offset = widget.getOffsetForPosition(event.getX(), event.getY()); Selection.extendSelection(buffer, offset); return true; @@ -275,7 +275,7 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme return true; } - int offset = widget.getOffset((int) event.getX(), (int) event.getY()); + int offset = widget.getOffsetForPosition(event.getX(), event.getY()); if (isSelecting(buffer)) { buffer.removeSpan(LAST_TAP_DOWN); Selection.extendSelection(buffer, offset); diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 845fbc3..61a24a0 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -17,7 +17,6 @@ package android.view; -import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.SurfaceTexture; @@ -137,14 +136,14 @@ public abstract class HardwareRenderer { * * @param canvas The Canvas used to render the view. */ - void onHardwarePreDraw(Canvas canvas); + void onHardwarePreDraw(HardwareCanvas canvas); /** * Invoked after a view is drawn by a hardware renderer. * * @param canvas The Canvas used to render the view. */ - void onHardwarePostDraw(Canvas canvas); + void onHardwarePostDraw(HardwareCanvas canvas); } /** diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 82fd581..3436cd1 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -883,8 +883,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { * <p> * <ul> * <li>For a stylus, reports the distance of the stylus from the screen. - * The value is normalized to a range from 0.0 (direct contact) to 1.0 (furthest measurable - * distance). + * The value is nominally measured in millimeters where 0.0 indicates direct contact + * and larger values indicate increasing distance from the surface. * </ul> * </p> * diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index d5f573c..98d07c4 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6048,6 +6048,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Set the horizontal scrolled position of your view. This will cause a call to + * {@link #onScrollChanged(int, int, int, int)} and the view will be + * invalidated. + * @param value the x position to scroll to + */ + public void setScrollX(int value) { + scrollTo(value, mScrollY); + } + + /** + * Set the vertical scrolled position of your view. This will cause a call to + * {@link #onScrollChanged(int, int, int, int)} and the view will be + * invalidated. + * @param value the y position to scroll to + */ + public void setScrollY(int value) { + scrollTo(mScrollX, value); + } + + /** * Return the scrolled left position of this view. This is the left edge of * the displayed part of your view. You do not need to draw any pixels * farther left, since those are outside of the frame of your view on diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java index 8085ea8..bf04502 100644 --- a/core/java/android/view/ViewAncestor.java +++ b/core/java/android/view/ViewAncestor.java @@ -17,6 +17,7 @@ package android.view; import android.Manifest; +import android.animation.LayoutTransition; import android.app.ActivityManagerNative; import android.content.ClipDescription; import android.content.ComponentCallbacks; @@ -25,7 +26,6 @@ import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PixelFormat; @@ -45,9 +45,7 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; -import android.os.SystemProperties; import android.util.AndroidRuntimeException; import android.util.DisplayMetrics; import android.util.EventLog; @@ -230,10 +228,11 @@ public final class ViewAncestor extends Handler implements ViewParent, int mScrollY; int mCurScrollY; Scroller mScroller; - Bitmap mResizeBitmap; - long mResizeBitmapStartTime; - int mResizeBitmapDuration; + HardwareLayer mResizeBuffer; + long mResizeBufferStartTime; + int mResizeBufferDuration; static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator(); + private ArrayList<LayoutTransition> mPendingTransitions; final ViewConfiguration mViewConfiguration; @@ -672,6 +671,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (!mTraversalScheduled) { mTraversalScheduled = true; + //noinspection ConstantConditions if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) { final long now = System.nanoTime(); Log.d(TAG, "Latency: Scheduled traversal, it has been " @@ -694,10 +694,32 @@ public final class ViewAncestor extends Handler implements ViewParent, return mAppVisible ? mView.getVisibility() : View.GONE; } - void disposeResizeBitmap() { - if (mResizeBitmap != null) { - mResizeBitmap.recycle(); - mResizeBitmap = null; + void disposeResizeBuffer() { + if (mResizeBuffer != null) { + mResizeBuffer.destroy(); + mResizeBuffer = null; + } + } + + /** + * Add LayoutTransition to the list of transitions to be started in the next traversal. + * This list will be cleared after the transitions on the list are start()'ed. These + * transitionsa re added by LayoutTransition itself when it sets up animations. The setup + * happens during the layout phase of traversal, which we want to complete before any of the + * animations are started (because those animations may side-effect properties that layout + * depends upon, like the bounding rectangles of the affected views). So we add the transition + * to the list and it is started just prior to starting the drawing phase of traversal. + * + * @param transition The LayoutTransition to be started on the next traversal. + * + * @hide + */ + public void requestTransitionStart(LayoutTransition transition) { + if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) { + if (mPendingTransitions == null) { + mPendingTransitions = new ArrayList<LayoutTransition>(); + } + mPendingTransitions.add(transition); } } @@ -819,15 +841,25 @@ public final class ViewAncestor extends Handler implements ViewParent, mAttachInfo.mHardwareRenderer.isEnabled() && lp != null && !PixelFormat.formatHasAlpha(lp.format)) { - disposeResizeBitmap(); + disposeResizeBuffer(); boolean completed = false; + HardwareCanvas canvas = null; try { - mResizeBitmap = Bitmap.createBitmap(mWidth, mHeight, - Bitmap.Config.ARGB_8888); - mResizeBitmap.setHasAlpha(false); - Canvas canvas = new Canvas(mResizeBitmap); + if (mResizeBuffer == null) { + mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer( + mWidth, mHeight, false); + } else if (mResizeBuffer.getWidth() != mWidth || + mResizeBuffer.getHeight() != mHeight) { + mResizeBuffer.resize(mWidth, mHeight); + } + canvas = mResizeBuffer.start(mAttachInfo.mHardwareCanvas); + canvas.setViewport(mWidth, mHeight); + canvas.onPreDraw(null); + final int restoreCount = canvas.save(); + canvas.drawColor(0xff000000, PorterDuff.Mode.SRC); + int yoff; final boolean scrolling = mScroller != null && mScroller.computeScrollOffset(); @@ -837,22 +869,32 @@ public final class ViewAncestor extends Handler implements ViewParent, } else { yoff = mScrollY; } + canvas.translate(0, -yoff); if (mTranslator != null) { mTranslator.translateCanvas(canvas); } - canvas.setScreenDensity(mAttachInfo.mScalingRequired - ? DisplayMetrics.DENSITY_DEVICE : 0); + mView.draw(canvas); - mResizeBitmapStartTime = SystemClock.uptimeMillis(); - mResizeBitmapDuration = mView.getResources().getInteger( + + mResizeBufferStartTime = SystemClock.uptimeMillis(); + mResizeBufferDuration = mView.getResources().getInteger( com.android.internal.R.integer.config_mediumAnimTime); completed = true; + + canvas.restoreToCount(restoreCount); } catch (OutOfMemoryError e) { Log.w(TAG, "Not enough memory for content change anim buffer", e); } finally { - if (!completed) { - mResizeBitmap = null; + if (canvas != null) { + canvas.onPostDraw(); + } + if (mResizeBuffer != null) { + mResizeBuffer.end(mAttachInfo.mHardwareCanvas); + if (!completed) { + mResizeBuffer.destroy(); + mResizeBuffer = null; + } } } } @@ -1114,7 +1156,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (mScroller != null) { mScroller.abortAnimation(); } - disposeResizeBitmap(); + disposeResizeBuffer(); } else if (surfaceGenerationId != mSurface.getGenerationId() && mSurfaceHolder == null && mAttachInfo.mHardwareRenderer != null) { fullRedrawNeeded = true; @@ -1396,6 +1438,12 @@ public final class ViewAncestor extends Handler implements ViewParent, boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw(); if (!cancelDraw && !newSurface) { + if (mPendingTransitions != null && mPendingTransitions.size() > 0) { + for (int i = 0; i < mPendingTransitions.size(); ++i) { + mPendingTransitions.get(i).startChangingAnimations(); + } + mPendingTransitions.clear(); + } mFullRedrawNeeded = false; final long drawStartTime; @@ -1495,15 +1543,14 @@ public final class ViewAncestor extends Handler implements ViewParent, int mResizeAlpha; final Paint mResizePaint = new Paint(); - public void onHardwarePreDraw(Canvas canvas) { + public void onHardwarePreDraw(HardwareCanvas canvas) { canvas.translate(0, -mHardwareYOffset); } - public void onHardwarePostDraw(Canvas canvas) { - if (mResizeBitmap != null) { - canvas.translate(0, mHardwareYOffset); + public void onHardwarePostDraw(HardwareCanvas canvas) { + if (mResizeBuffer != null) { mResizePaint.setAlpha(mResizeAlpha); - canvas.drawBitmap(mResizeBitmap, 0, 0, mResizePaint); + canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint); } } @@ -1559,15 +1606,15 @@ public final class ViewAncestor extends Handler implements ViewParent, boolean scalingRequired = mAttachInfo.mScalingRequired; int resizeAlpha = 0; - if (mResizeBitmap != null) { - long deltaTime = SystemClock.uptimeMillis() - mResizeBitmapStartTime; - if (deltaTime < mResizeBitmapDuration) { - float amt = deltaTime/(float)mResizeBitmapDuration; + if (mResizeBuffer != null) { + long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime; + if (deltaTime < mResizeBufferDuration) { + float amt = deltaTime/(float) mResizeBufferDuration; amt = mResizeInterpolator.getInterpolation(amt); animating = true; resizeAlpha = 255 - (int)(amt*255); } else { - disposeResizeBitmap(); + disposeResizeBuffer(); } } @@ -1579,7 +1626,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (mScroller != null) { mScroller.abortAnimation(); } - disposeResizeBitmap(); + disposeResizeBuffer(); } return; } @@ -1863,7 +1910,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (scrollY != mScrollY) { if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old=" + mScrollY + " , new=" + scrollY); - if (!immediate && mResizeBitmap == null) { + if (!immediate && mResizeBuffer == null) { if (mScroller == null) { mScroller = new Scroller(mView.getContext()); } @@ -1909,20 +1956,22 @@ public final class ViewAncestor extends Handler implements ViewParent, public void focusableViewAvailable(View v) { checkThread(); - if (mView != null && !mView.hasFocus()) { - v.requestFocus(); - } else { - // the one case where will transfer focus away from the current one - // is if the current view is a view group that prefers to give focus - // to its children first AND the view is a descendant of it. - mFocusedView = mView.findFocus(); - boolean descendantsHaveDibsOnFocus = - (mFocusedView instanceof ViewGroup) && - (((ViewGroup) mFocusedView).getDescendantFocusability() == - ViewGroup.FOCUS_AFTER_DESCENDANTS); - if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) { - // If a view gets the focus, the listener will be invoked from requestChildFocus() + if (mView != null) { + if (!mView.hasFocus()) { v.requestFocus(); + } else { + // the one case where will transfer focus away from the current one + // is if the current view is a view group that prefers to give focus + // to its children first AND the view is a descendant of it. + mFocusedView = mView.findFocus(); + boolean descendantsHaveDibsOnFocus = + (mFocusedView instanceof ViewGroup) && + (((ViewGroup) mFocusedView).getDescendantFocusability() == + ViewGroup.FOCUS_AFTER_DESCENDANTS); + if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) { + // If a view gets the focus, the listener will be invoked from requestChildFocus() + v.requestFocus(); + } } } } @@ -1986,9 +2035,7 @@ public final class ViewAncestor extends Handler implements ViewParent, // At this point the resources have been updated to // have the most recent config, whatever that is. Use // the on in them which may be newer. - if (mView != null) { - config = mView.getResources().getConfiguration(); - } + config = mView.getResources().getConfiguration(); if (force || mLastConfiguration.diff(config) != 0) { mLastConfiguration.setTo(config); mView.dispatchConfigurationChanged(config); @@ -2207,6 +2254,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) { // The IME is trying to say this event is from the // system! Bad bad bad! + //noinspection UnusedAssignment event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM); } deliverKeyEventPostIme((KeyEvent)msg.obj, false); @@ -2240,7 +2288,7 @@ public final class ViewAncestor extends Handler implements ViewParent, } } - private void startInputEvent(InputEvent event, InputQueue.FinishedCallback finishedCallback) { + private void startInputEvent(InputQueue.FinishedCallback finishedCallback) { if (mFinishedCallback != null) { Slog.w(TAG, "Received a new input event from the input queue but there is " + "already an unfinished input event in progress."); @@ -2454,9 +2502,6 @@ public final class ViewAncestor extends Handler implements ViewParent, if (isDown) { ensureTouchMode(true); } - if(false) { - captureMotionLog("captureDispatchPointer", event); - } // Offset the scroll position. if (mCurScrollY != 0) { @@ -2534,6 +2579,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (sendDone) { finishInputEvent(event, handled); } + //noinspection ConstantConditions if (LOCAL_LOGV || WATCH_POINTER) { if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { Log.i(TAG, "Done dispatching!"); @@ -2859,52 +2905,6 @@ public final class ViewAncestor extends Handler implements ViewParent, return false; } - /** - * log motion events - */ - private static void captureMotionLog(String subTag, MotionEvent ev) { - //check dynamic switch - if (ev == null || - SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) { - return; - } - - StringBuilder sb = new StringBuilder(subTag + ": "); - sb.append(ev.getDownTime()).append(','); - sb.append(ev.getEventTime()).append(','); - sb.append(ev.getAction()).append(','); - sb.append(ev.getX()).append(','); - sb.append(ev.getY()).append(','); - sb.append(ev.getPressure()).append(','); - sb.append(ev.getSize()).append(','); - sb.append(ev.getMetaState()).append(','); - sb.append(ev.getXPrecision()).append(','); - sb.append(ev.getYPrecision()).append(','); - sb.append(ev.getDeviceId()).append(','); - sb.append(ev.getEdgeFlags()); - Log.d(TAG, sb.toString()); - } - /** - * log motion events - */ - private static void captureKeyLog(String subTag, KeyEvent ev) { - //check dynamic switch - if (ev == null || - SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) { - return; - } - StringBuilder sb = new StringBuilder(subTag + ": "); - sb.append(ev.getDownTime()).append(','); - sb.append(ev.getEventTime()).append(','); - sb.append(ev.getAction()).append(','); - sb.append(ev.getKeyCode()).append(','); - sb.append(ev.getRepeatCount()).append(','); - sb.append(ev.getMetaState()).append(','); - sb.append(ev.getDeviceId()).append(','); - sb.append(ev.getScanCode()); - Log.d(TAG, sb.toString()); - } - int enqueuePendingEvent(Object event, boolean sendDone) { int seq = mPendingEventSeq+1; if (seq < 0) seq = 0; @@ -2993,10 +2993,6 @@ public final class ViewAncestor extends Handler implements ViewParent, return; } - if (false) { - captureKeyLog("captureDispatchKeyEvent", event); - } - // Make sure the fallback event policy sees all keys that will be delivered to the // view hierarchy. mFallbackEventHandler.preDispatchKeyEvent(event); @@ -3392,12 +3388,12 @@ public final class ViewAncestor extends Handler implements ViewParent, private final InputHandler mInputHandler = new InputHandler() { public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) { - startInputEvent(event, finishedCallback); + startInputEvent(finishedCallback); dispatchKey(event, true); } public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) { - startInputEvent(event, finishedCallback); + startInputEvent(finishedCallback); dispatchMotion(event, true); } }; @@ -3429,10 +3425,6 @@ public final class ViewAncestor extends Handler implements ViewParent, sendMessageAtTime(msg, event.getEventTime()); } - public void dispatchMotion(MotionEvent event) { - dispatchMotion(event, false); - } - private void dispatchMotion(MotionEvent event, boolean sendDone) { int source = event.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { @@ -3444,10 +3436,6 @@ public final class ViewAncestor extends Handler implements ViewParent, } } - public void dispatchPointer(MotionEvent event) { - dispatchPointer(event, false); - } - private void dispatchPointer(MotionEvent event, boolean sendDone) { Message msg = obtainMessage(DISPATCH_POINTER); msg.obj = event; @@ -3455,10 +3443,6 @@ public final class ViewAncestor extends Handler implements ViewParent, sendMessageAtTime(msg, event.getEventTime()); } - public void dispatchTrackball(MotionEvent event) { - dispatchTrackball(event, false); - } - private void dispatchTrackball(MotionEvent event, boolean sendDone) { Message msg = obtainMessage(DISPATCH_TRACKBALL); msg.obj = event; diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 4aa8727..f014070 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -92,12 +92,6 @@ public class ViewDebug { public static final boolean TRACE_RECYCLER = false; /** - * The system property of dynamic switch for capturing event information - * when it is set, we log key events, touch/motion and trackball events - */ - static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent"; - - /** * Profiles drawing times in the events log. * * @hide diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 6937573..f504b90 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4838,6 +4838,20 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * This method is called by LayoutTransition when there are 'changing' animations that need + * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who + * starts all pending transitions prior to the drawing phase in the current traversal. + * + * @param transition The LayoutTransition to be started on the next traversal. + * + * @hide + */ + public void requestTransitionStart(LayoutTransition transition) { + ViewAncestor viewAncestor = getViewAncestor(); + viewAncestor.requestTransitionStart(transition); + } + + /** * Return true if the pressed state should be delayed for children or descendants of this * ViewGroup. Generally, this should be done for containers that can scroll, such as a List. * This prevents the pressed state from appearing when the user is actually trying to scroll diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index 1d56e9d..9eddf23 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -67,6 +67,19 @@ public class ViewPropertyAnimator { private boolean mDurationSet = false; /** + * The startDelay of the underlying Animator object. By default, we don't set the startDelay + * on the Animator and just use its default startDelay. If the startDelay is ever set on this + * Animator, then we use the startDelay that it was set to. + */ + private long mStartDelay = 0; + + /** + * A flag indicating whether the startDelay has been set on this object. If not, we don't set + * the startDelay on the underlying Animator, but instead just use its default startDelay. + */ + private boolean mStartDelaySet = false; + + /** * The interpolator of the underlying Animator object. By default, we don't set the interpolator * on the Animator and just use its default interpolator. If the interpolator is ever set on * this Animator, then we use the interpolator that it was set to. @@ -233,6 +246,60 @@ public class ViewPropertyAnimator { } /** + * Returns the current duration of property animations. If the duration was set on this + * object, that value is returned. Otherwise, the default value of the underlying Animator + * is returned. + * + * @see #setDuration(long) + * @return The duration of animations, in milliseconds. + */ + public long getDuration() { + if (mStartDelaySet) { + return mStartDelay; + } else { + // Just return the default from ValueAnimator, since that's what we'd get if + // the value has not been set otherwise + return new ValueAnimator().getDuration(); + } + } + + /** + * Returns the current startDelay of property animations. If the startDelay was set on this + * object, that value is returned. Otherwise, the default value of the underlying Animator + * is returned. + * + * @see #setStartDelay(long) + * @return The startDelay of animations, in milliseconds. + */ + public long getStartDelay() { + if (mStartDelaySet) { + return mStartDelay; + } else { + // Just return the default from ValueAnimator (0), since that's what we'd get if + // the value has not been set otherwise + return 0; + } + } + + /** + * Sets the startDelay for the underlying animator that animates the requested properties. + * By default, the animator uses the default value for ValueAnimator. Calling this method + * will cause the declared value to be used instead. + * @param startDelay The delay of ensuing property animations, in milliseconds. The value + * cannot be negative. + * @return This object, allowing calls to methods in this class to be chained. + */ + public ViewPropertyAnimator setStartDelay(long startDelay) { + if (startDelay < 0) { + throw new IllegalArgumentException("Animators cannot have negative duration: " + + startDelay); + } + mStartDelaySet = true; + mStartDelay = startDelay; + return this; + } + + /** * Sets the interpolator for the underlying animator that animates the requested properties. * By default, the animator uses the default interpolator for ValueAnimator. Calling this method * will cause the declared object to be used instead. @@ -259,6 +326,33 @@ public class ViewPropertyAnimator { } /** + * Starts the currently pending property animations immediately. Calling <code>start()</code> + * is optional because all animations start automatically at the next opportunity. However, + * if the animations are needed to start immediately and synchronously (not at the time when + * the next event is processed by the hierarchy, which is when the animations would begin + * otherwise), then this method can be used. + */ + public void start() { + startAnimation(); + } + + /** + * Cancels all property animations that are currently running or pending. + */ + public void cancel() { + if (mAnimatorMap.size() > 0) { + HashMap<Animator, PropertyBundle> mAnimatorMapCopy = + (HashMap<Animator, PropertyBundle>)mAnimatorMap.clone(); + Set<Animator> animatorSet = mAnimatorMapCopy.keySet(); + for (Animator runningAnim : animatorSet) { + runningAnim.cancel(); + } + } + mPendingAnimations.clear(); + mView.getHandler().removeCallbacks(mAnimationStarter); + } + + /** * This method will cause the View's <code>x</code> property to be animated to the * specified value. Animations already running on the property will be canceled. * @@ -598,7 +692,7 @@ public class ViewPropertyAnimator { // on a property will cancel a previous animation on that property, so // there can only ever be one such animation running. if (bundle.mPropertyMask == NONE) { - // the animation is not longer changing anything - cancel it + // the animation is no longer changing anything - cancel it animatorToCancel = runningAnim; break; } diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java index 976e786..12391df 100644 --- a/core/java/android/webkit/JWebCoreJavaBridge.java +++ b/core/java/android/webkit/JWebCoreJavaBridge.java @@ -39,9 +39,6 @@ final class JWebCoreJavaBridge extends Handler { // immediately. private boolean mHasInstantTimer; - // Reference count the pause/resume of timers - private int mPauseTimerRefCount; - private boolean mTimerPaused; private boolean mHasDeferredTimers; @@ -136,7 +133,7 @@ final class JWebCoreJavaBridge extends Handler { * Pause all timers. */ public void pause() { - if (--mPauseTimerRefCount == 0) { + if (!mTimerPaused) { mTimerPaused = true; mHasDeferredTimers = false; } @@ -146,7 +143,7 @@ final class JWebCoreJavaBridge extends Handler { * Resume all timers. */ public void resume() { - if (++mPauseTimerRefCount == 1) { + if (mTimerPaused) { mTimerPaused = false; if (mHasDeferredTimers) { mHasDeferredTimers = false; diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java index f59d7d0..5b4fb1d 100644 --- a/core/java/android/webkit/L10nUtils.java +++ b/core/java/android/webkit/L10nUtils.java @@ -69,7 +69,8 @@ public class L10nUtils { com.android.internal.R.string.autofill_card_number_re, // IDS_AUTOFILL_CARD_NUMBER_RE com.android.internal.R.string.autofill_expiration_month_re, // IDS_AUTOFILL_EXPIRATION_MONTH_RE com.android.internal.R.string.autofill_expiration_date_re, // IDS_AUTOFILL_EXPIRATION_DATE_RE - com.android.internal.R.string.autofill_card_ignored_re // IDS_AUTOFILL_CARD_IGNORED_RE + com.android.internal.R.string.autofill_card_ignored_re, // IDS_AUTOFILL_CARD_IGNORED_RE + com.android.internal.R.string.autofill_fax_re // IDS_AUTOFILL_FAX_RE }; private static Context mApplicationContext; diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 8ffbda2..66fcc27 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -183,7 +183,6 @@ public class WebSettings { private boolean mJavaScriptCanOpenWindowsAutomatically = false; private boolean mUseDoubleTree = false; private boolean mUseWideViewport = false; - private boolean mUseFixedViewport = false; private boolean mSupportMultipleWindows = false; private boolean mShrinksStandaloneImagesToFit = false; private long mMaximumDecodedImageSize = 0; // 0 means default @@ -361,10 +360,9 @@ public class WebSettings { } // User agent strings. - private static final String DESKTOP_USERAGENT = - "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)" - + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0" - + " Safari/530.17"; + private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (X11; " + + "Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) " + + "Chrome/11.0.696.34 Safari/534.24"; private static final String IPHONE_USERAGENT = "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)" + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0" @@ -383,13 +381,6 @@ public class WebSettings { mDefaultTextEncoding = context.getString(com.android.internal. R.string.default_text_encoding); - // Detect tablet device for fixed viewport mode. - final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - final int landscapeWidth = Math.max(metrics.widthPixels, metrics.heightPixels); - final int minTabletWidth = context.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.min_xlarge_screen_width); - mUseFixedViewport = (metrics.density == 1.0f && landscapeWidth >= minTabletWidth); - if (sLockForLocaleSettings == null) { sLockForLocaleSettings = new Object(); sLocale = Locale.getDefault(); @@ -1652,11 +1643,11 @@ public class WebSettings { } /** - * Returns whether to use fixed viewport. Fixed viewport should operate only - * when wide viewport is on. + * Returns whether to use fixed viewport. Use fixed viewport + * whenever wide viewport is on. */ /* package */ boolean getUseFixedViewport() { - return getUseWideViewPort() && mUseFixedViewport; + return getUseWideViewPort(); } /** diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index f774803..61a69ca 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -54,7 +54,9 @@ import android.net.http.SslCertificate; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.os.Message; +import android.os.StrictMode; import android.provider.Settings; import android.speech.tts.TextToSpeech; import android.text.Selection; @@ -8933,15 +8935,14 @@ public class WebView extends AbsoluteLayout } private static void checkThread() { - if (!"main".equals(Thread.currentThread().getName())) { - try { - throw new RuntimeException("A WebView method was called on thread '" + - Thread.currentThread().getName() + "'. " + - "All WebView methods must be called on the UI thread. " + - "Future versions of WebView may not support use on other threads."); - } catch (RuntimeException e) { - Log.e(LOGTAG, Log.getStackTraceString(e)); - } + if (Looper.myLooper() != Looper.getMainLooper()) { + RuntimeException exception = new RuntimeException( + "A WebView method was called on thread '" + + Thread.currentThread().getName() + "'. " + + "All WebView methods must be called on the UI thread. " + + "Future versions of WebView may not support use on other threads."); + Log.e(LOGTAG, Log.getStackTraceString(exception)); + StrictMode.onWebViewMethodCalledOnWrongThread(exception); } } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index e8083eb..db5ff54 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -1865,7 +1865,7 @@ public final class WebViewCore { Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); return; } - int width = calculateWindowWidth(w, textwrapWidth); + int width = calculateWindowWidth(w); int height = h; if (width != w) { float heightWidthRatio = data.mHeightWidthRatio; @@ -1891,41 +1891,18 @@ public final class WebViewCore { } // Calculate width to be used in webkit window. - private int calculateWindowWidth(int viewWidth, int textwrapWidth) { + private int calculateWindowWidth(int viewWidth) { int width = viewWidth; if (mSettings.getUseWideViewPort()) { if (mViewportWidth == -1) { - if (mSettings.getLayoutAlgorithm() == - WebSettings.LayoutAlgorithm.NORMAL || mSettings.getUseFixedViewport()) { - width = WebView.DEFAULT_VIEWPORT_WIDTH; - } else { - /* - * if a page's minimum preferred width is wider than the - * given "w", use it instead to get better layout result. If - * we start a page with MAX_ZOOM_WIDTH, "w" will be always - * wider. If we start a page with screen width, due to the - * delay between {@link #didFirstLayout} and - * {@link #viewSizeChanged}, - * {@link #nativeGetContentMinPrefWidth} will return a more - * accurate value than initial 0 to result a better layout. - * In the worse case, the native width will be adjusted when - * next zoom or screen orientation change happens. - */ - width = Math.min(WebView.sMaxViewportWidth, Math.max(viewWidth, - Math.max(WebView.DEFAULT_VIEWPORT_WIDTH, - nativeGetContentMinPrefWidth()))); - } + // Fixed viewport width. + width = WebView.DEFAULT_VIEWPORT_WIDTH; } else if (mViewportWidth > 0) { - if (mSettings.getUseFixedViewport()) { - // Use website specified or desired fixed viewport width. - width = mViewportWidth; - } else { - width = Math.max(viewWidth, mViewportWidth); - } - } else if (mSettings.getUseFixedViewport()) { - width = mWebView.getViewWidth(); + // Use website specified or desired fixed viewport width. + width = mViewportWidth; } else { - width = textwrapWidth; + // For mobile web site. + width = viewWidth; } } return width; @@ -2025,7 +2002,8 @@ public final class WebViewCore { if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start"); draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mContentSize); if (draw.mBaseLayer == 0) { - if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort"); + if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message"); + mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); return; } webkitDraw(draw); @@ -2439,8 +2417,7 @@ public final class WebViewCore { // in zoom overview mode. tentativeScale = mInitialViewState.mTextWrapScale; int tentativeViewWidth = Math.round(webViewWidth / tentativeScale); - int windowWidth = calculateWindowWidth(tentativeViewWidth, - tentativeViewWidth); + int windowWidth = calculateWindowWidth(tentativeViewWidth); // In viewport setup time, since no content width is known, we assume // the windowWidth will be the content width, to get a more likely // zoom overview scale. @@ -2449,8 +2426,7 @@ public final class WebViewCore { // If user choose non-overview mode. data.mScale = Math.max(data.mScale, tentativeScale); } - if (mSettings.isNarrowColumnLayout() && - mSettings.getUseFixedViewport()) { + if (mSettings.isNarrowColumnLayout()) { // In case of automatic text reflow in fixed view port mode. mInitialViewState.mTextWrapScale = ZoomManager.computeReadingLevelScale(data.mScale); diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index f2a1ec3..e330737 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -1019,19 +1019,11 @@ class ZoomManager { WebSettings settings = mWebView.getSettings(); int newZoomOverviewWidth = mZoomOverviewWidth; if (settings.getUseWideViewPort()) { - if (!settings.getUseFixedViewport()) { - // limit mZoomOverviewWidth upper bound to - // sMaxViewportWidth so that if the page doesn't behave - // well, the WebView won't go insane. limit the lower - // bound to match the default scale for mobile sites. - newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth, - Math.max((int) (viewWidth * mInvDefaultScale), - Math.max(drawData.mMinPrefWidth, drawData.mViewSize.x))); - } else if (drawData.mContentSize.x > 0) { + if (drawData.mContentSize.x > 0) { // The webkitDraw for layers will not populate contentSize, and it'll be // ignored for zoom overview width update. - final int contentWidth = Math.max(drawData.mContentSize.x, drawData.mMinPrefWidth); - newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth, contentWidth); + newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth, + drawData.mContentSize.x); } } else { // If not use wide viewport, use view width as the zoom overview width. diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 86fefaf..6f30452 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -696,7 +696,8 @@ public class LinearLayout extends ViewGroup { mTotalLength += mDividerHeight; } - if (useLargestChild && heightMode == MeasureSpec.AT_MOST) { + if (useLargestChild && + (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) { mTotalLength = 0; for (int i = 0; i < count; ++i) { @@ -809,6 +810,31 @@ public class LinearLayout extends ViewGroup { } else { alternativeMaxWidth = Math.max(alternativeMaxWidth, weightedMaxWidth); + + + // We have no limit, so make all weighted views as tall as the largest child. + // Children will have already been measured once. + if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) { + for (int i = 0; i < count; i++) { + final View child = getVirtualChildAt(i); + + if (child == null || child.getVisibility() == View.GONE) { + continue; + } + + final LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) child.getLayoutParams(); + + float childExtra = lp.weight; + if (childExtra > 0) { + child.measure( + MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(), + MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(largestChildHeight, + MeasureSpec.EXACTLY)); + } + } + } } if (!allFillParent && widthMode != MeasureSpec.EXACTLY) { @@ -1044,7 +1070,8 @@ public class LinearLayout extends ViewGroup { maxHeight = Math.max(maxHeight, ascent + descent); } - if (useLargestChild && widthMode == MeasureSpec.AT_MOST) { + if (useLargestChild && + (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) { mTotalLength = 0; for (int i = 0; i < count; ++i) { @@ -1200,6 +1227,29 @@ public class LinearLayout extends ViewGroup { } } else { alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight); + + // We have no limit, so make all weighted views as wide as the largest child. + // Children will have already been measured once. + if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) { + for (int i = 0; i < count; i++) { + final View child = getVirtualChildAt(i); + + if (child == null || child.getVisibility() == View.GONE) { + continue; + } + + final LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) child.getLayoutParams(); + + float childExtra = lp.weight; + if (childExtra > 0) { + child.measure( + MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), + MeasureSpec.EXACTLY)); + } + } + } } if (!allFillParent && heightMode != MeasureSpec.EXACTLY) { @@ -1331,7 +1381,7 @@ public class LinearLayout extends ViewGroup { void layoutVertical() { final int paddingLeft = mPaddingLeft; - int childTop = mPaddingTop; + int childTop; int childLeft; // Where right end of child should go @@ -1346,19 +1396,21 @@ public class LinearLayout extends ViewGroup { final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; final int minorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK; - if (majorGravity != Gravity.TOP) { - switch (majorGravity) { - case Gravity.BOTTOM: - // mTotalLength contains the padding already, we add the top - // padding to compensate - childTop = mBottom - mTop + mPaddingTop - mTotalLength; - break; - - case Gravity.CENTER_VERTICAL: - childTop += ((mBottom - mTop) - mTotalLength) / 2; - break; - } - + switch (majorGravity) { + case Gravity.BOTTOM: + // mTotalLength contains the padding already + childTop = mPaddingTop + mBottom - mTop - mTotalLength; + break; + + // mTotalLength contains the padding already + case Gravity.CENTER_VERTICAL: + childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2; + break; + + case Gravity.TOP: + default: + childTop = mPaddingTop; + break; } for (int i = 0; i < count; i++) { @@ -1376,12 +1428,8 @@ public class LinearLayout extends ViewGroup { if (gravity < 0) { gravity = minorGravity; } - - switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.LEFT: - childLeft = paddingLeft + lp.leftMargin; - break; + switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: childLeft = paddingLeft + ((childSpace - childWidth) / 2) + lp.leftMargin - lp.rightMargin; @@ -1390,11 +1438,13 @@ public class LinearLayout extends ViewGroup { case Gravity.RIGHT: childLeft = childRight - childWidth - lp.rightMargin; break; + + case Gravity.LEFT: default: - childLeft = paddingLeft; + childLeft = paddingLeft + lp.leftMargin; break; } - + if (hasDividerBeforeChildAt(i)) { childTop += mDividerHeight; } @@ -1418,10 +1468,11 @@ public class LinearLayout extends ViewGroup { * @see #onLayout(boolean, int, int, int, int) */ void layoutHorizontal() { + final boolean isLayoutRtl = isLayoutRtl(); final int paddingTop = mPaddingTop; int childTop; - int childLeft = mPaddingLeft; + int childLeft; // Where bottom of child should go final int height = mBottom - mTop; @@ -1440,25 +1491,37 @@ public class LinearLayout extends ViewGroup { final int[] maxAscent = mMaxAscent; final int[] maxDescent = mMaxDescent; - if (majorGravity != Gravity.LEFT) { - switch (majorGravity) { - case Gravity.RIGHT: - // mTotalLength contains the padding already, we add the left - // padding to compensate - childLeft = mRight - mLeft + mPaddingLeft - mTotalLength; - break; - - case Gravity.CENTER_HORIZONTAL: - childLeft += ((mRight - mLeft) - mTotalLength) / 2; - break; - } + switch (majorGravity) { + case Gravity.RIGHT: + // mTotalLength contains the padding already + childLeft = mPaddingLeft + mRight - mLeft - mTotalLength; + break; + + case Gravity.CENTER_HORIZONTAL: + // mTotalLength contains the padding already + childLeft = mPaddingLeft + (mRight - mLeft - mTotalLength) / 2; + break; + + case Gravity.LEFT: + default: + childLeft = mPaddingLeft; + break; + } + + int start = 0; + int dir = 1; + //In case of RTL, start drawing from the last child. + if (isLayoutRtl) { + start = count - 1; + dir = -1; } for (int i = 0; i < count; i++) { - final View child = getVirtualChildAt(i); + int childIndex = start + dir * i; + final View child = getVirtualChildAt(childIndex); if (child == null) { - childLeft += measureNullChild(i); + childLeft += measureNullChild(childIndex); } else if (child.getVisibility() != GONE) { final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); @@ -1512,7 +1575,7 @@ public class LinearLayout extends ViewGroup { break; } - if (hasDividerBeforeChildAt(i)) { + if (hasDividerBeforeChildAt(childIndex)) { childLeft += mDividerWidth; } @@ -1522,7 +1585,7 @@ public class LinearLayout extends ViewGroup { childLeft += childWidth + lp.rightMargin + getNextLocationOffset(child); - i += getChildrenSkipCount(child, i); + i += getChildrenSkipCount(child, childIndex); } } } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 1e1a043..563fc26 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1171,8 +1171,7 @@ public class PopupWindow { int bottomEdge = displayFrame.bottom; if (ignoreBottomDecorations) { Resources res = anchor.getContext().getResources(); - bottomEdge = res.getDisplayMetrics().heightPixels - - (int) res.getDimension(com.android.internal.R.dimen.screen_margin_bottom); + bottomEdge = res.getDisplayMetrics().heightPixels; } final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset; final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset; diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java index 71c91e1..c4ba7c8 100644 --- a/core/java/android/widget/StackView.java +++ b/core/java/android/widget/StackView.java @@ -116,7 +116,7 @@ public class StackView extends AdapterViewAnimator { private static final int MIN_TIME_BETWEEN_INTERACTION_AND_AUTOADVANCE = 5000; - private static long MIN_TIME_BETWEEN_SCROLLS = 100; + private static final long MIN_TIME_BETWEEN_SCROLLS = 100; /** * These variables are all related to the current state of touch interaction @@ -213,8 +213,7 @@ public class StackView extends AdapterViewAnimator { * Animate the views between different relative indexes within the {@link AdapterViewAnimator} */ void transformViewForTransition(int fromIndex, int toIndex, final View view, boolean animate) { - ObjectAnimator alphaOa = null; - ObjectAnimator oldAlphaOa = null; + ObjectAnimator alphaOa; if (!animate) { ((StackFrame) view).cancelSliderAnimator(); @@ -1276,13 +1275,11 @@ public class StackView extends AdapterViewAnimator { boolean firstPass = true; parentRect.set(0, 0, 0, 0); - int depth = 0; while (p.getParent() != null && p.getParent() instanceof View && !parentRect.contains(globalInvalidateRect)) { if (!firstPass) { globalInvalidateRect.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY()); - depth++; } firstPass = false; p = (View) p.getParent(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index d58c72b..3875765 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -16,11 +16,6 @@ package android.widget; -import com.android.internal.util.FastMath; -import com.android.internal.widget.EditableInputConnection; - -import org.xmlpull.v1.XmlPullParserException; - import android.R; import android.content.ClipData; import android.content.ClipData.Item; @@ -129,6 +124,11 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.RemoteViews.RemoteView; +import com.android.internal.util.FastMath; +import com.android.internal.widget.EditableInputConnection; + +import org.xmlpull.v1.XmlPullParserException; + import java.io.IOException; import java.lang.ref.WeakReference; import java.text.BreakIterator; @@ -320,6 +320,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mTextEditSuggestionItemLayout; private SuggestionsPopupWindow mSuggestionsPopupWindow; private SuggestionRangeSpan mSuggestionRangeSpan; + private boolean mSuggestionsEnabled = true; private int mCursorDrawableRes; private final Drawable[] mCursorDrawable = new Drawable[2]; @@ -329,7 +330,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private Drawable mSelectHandleRight; private Drawable mSelectHandleCenter; - private int mLastDownPositionX, mLastDownPositionY; + private float mLastDownPositionX, mLastDownPositionY; private Callback mCustomSelectionActionModeCallback; private final int mSquaredTouchSlopDistance; @@ -806,6 +807,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextView_textIsSelectable: mTextIsSelectable = a.getBoolean(attr, false); break; + + case com.android.internal.R.styleable.TextView_suggestionsEnabled: + mSuggestionsEnabled = a.getBoolean(attr, true); + break; } } a.recycle(); @@ -2898,8 +2903,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setText(mCharWrapper, mBufferType, false, oldlen); } - private static class CharWrapper - implements CharSequence, GetChars, GraphicsOperations { + private static class CharWrapper implements CharSequence, GetChars, GraphicsOperations { private char[] mChars; private int mStart, mLength; @@ -7323,8 +7327,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (action == MotionEvent.ACTION_DOWN) { - mLastDownPositionX = (int) event.getX(); - mLastDownPositionY = (int) event.getY(); + mLastDownPositionX = event.getX(); + mLastDownPositionY = event.getY(); // Reset this state; it will be re-set if super.onTouchEvent // causes focus to move to the view. @@ -7758,16 +7762,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener hasPrimaryClip()); } - private boolean isWordCharacter(int c, int type) { - return (c == '\'' || c == '"' || - type == Character.UPPERCASE_LETTER || - type == Character.LOWERCASE_LETTER || - type == Character.TITLECASE_LETTER || - type == Character.MODIFIER_LETTER || - type == Character.OTHER_LETTER || // Should handle asian characters - type == Character.DECIMAL_DIGIT_NUMBER); - } - private static long packRangeInLong(int start, int end) { return (((long) start) << 32) | end; } @@ -8140,7 +8134,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Long press in empty space moves cursor and shows the Paste affordance if available. if (!isPositionOnText(mLastDownPositionX, mLastDownPositionY) && mInsertionControllerEnabled) { - final int offset = getOffset(mLastDownPositionX, mLastDownPositionY); + final int offset = getOffsetForPosition(mLastDownPositionX, mLastDownPositionY); stopSelectionActionMode(); Selection.setSelection((Spannable) mText, offset); getInsertionController().showWithPaste(); @@ -8208,7 +8202,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private final ViewGroup[] mSuggestionViews = new ViewGroup[2]; private final int[] mSuggestionViewLayouts = new int[] { mTextEditSuggestionsBottomWindowLayout, mTextEditSuggestionsTopWindowLayout}; - private WordIterator mWordIterator; + private WordIterator mSuggestionWordIterator; private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan[0]; public SuggestionsPopupWindow() { @@ -8344,26 +8338,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private long[] getWordLimits(CharSequence text) { - if (mWordIterator == null) mWordIterator = new WordIterator(); // TODO locale - mWordIterator.setCharSequence(text); + // TODO locale for mSuggestionWordIterator + if (mSuggestionWordIterator == null) mSuggestionWordIterator = new WordIterator(); + mSuggestionWordIterator.setCharSequence(text); // First pass will simply count the number of words to be able to create an array // Not too expensive since previous break positions are cached by the BreakIterator int nbWords = 0; - int position = mWordIterator.following(0); + int position = mSuggestionWordIterator.following(0); while (position != BreakIterator.DONE) { nbWords++; - position = mWordIterator.following(position); + position = mSuggestionWordIterator.following(position); } int index = 0; long[] result = new long[nbWords]; - position = mWordIterator.following(0); + position = mSuggestionWordIterator.following(0); while (position != BreakIterator.DONE) { - int wordStart = mWordIterator.getBeginning(position); + int wordStart = mSuggestionWordIterator.getBeginning(position); result[index++] = packRangeInLong(wordStart, position); - position = mWordIterator.following(position); + position = mSuggestionWordIterator.following(position); } return result; @@ -8601,6 +8596,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } void showSuggestions() { + if (!mSuggestionsEnabled || !isTextEditable()) return; + if (mSuggestionsPopupWindow == null) { mSuggestionsPopupWindow = new SuggestionsPopupWindow(); } @@ -8615,6 +8612,31 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Some parts of the text can have alternate suggestion text attached. This is typically done by + * the IME by adding {@link SuggestionSpan}s to the text. + * + * When suggestions are enabled (default), this list of suggestions will be displayed when the + * user double taps on these parts of the text. No suggestions are displayed when this value is + * false. Use {@link #setSuggestionsEnabled(boolean)} to change this value. + * + * @return true if the suggestions popup window is enabled. + * + * @attr ref android.R.styleable#TextView_suggestionsEnabled + */ + public boolean isSuggestionsEnabled() { + return mSuggestionsEnabled; + } + + /** + * Enables or disables the suggestion popup. See {@link #isSuggestionsEnabled()}. + * + * @param enabled Whether or not suggestions are enabled. + */ + public void setSuggestionsEnabled(boolean enabled) { + mSuggestionsEnabled = enabled; + } + + /** * If provided, this ActionMode.Callback will be used to create the ActionMode when text * selection is initiated in this View. * @@ -9115,7 +9137,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public abstract void updateOffset(int offset); - public abstract void updatePosition(int x, int y); + public abstract void updatePosition(float x, float y); protected void positionAtCursorOffset(int offset) { addPositionToTouchUpFilter(offset); @@ -9215,7 +9237,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX; final float newPosY = rawY - mTouchToWindowOffsetY + mTouchOffsetY; - updatePosition(Math.round(newPosX), Math.round(newPosY)); + updatePosition(newPosX, newPosY); break; } @@ -9366,8 +9388,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public void updatePosition(int x, int y) { - updateOffset(getOffset(x, y)); + public void updatePosition(float x, float y) { + updateOffset(getOffsetForPosition(x, y)); } void showPastePopupWindow() { @@ -9421,11 +9443,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public void updatePosition(int x, int y) { + public void updatePosition(float x, float y) { final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); - int offset = getOffset(x, y); + int offset = getOffsetForPosition(x, y); // No need to redraw when the offset is unchanged if (offset == selectionStart) return; @@ -9458,11 +9480,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public void updatePosition(int x, int y) { + public void updatePosition(float x, float y) { final int selectionStart = getSelectionStart(); final int selectionEnd = getSelectionEnd(); - int offset = getOffset(x, y); + int offset = getOffsetForPosition(x, y); // No need to redraw when the offset is unchanged if (offset == selectionEnd) return; @@ -9560,7 +9582,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Double tap detection private long mPreviousTapUpTime = 0; - private int mPreviousTapPositionX, mPreviousTapPositionY; + private float mPreviousTapPositionX, mPreviousTapPositionY; SelectionModifierCursorController() { resetTouchOffsets(); @@ -9593,19 +9615,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (isTextEditable() || mTextIsSelectable) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: - final int x = (int) event.getX(); - final int y = (int) event.getY(); + final float x = event.getX(); + final float y = event.getY(); // Remember finger down position, to be able to start selection from there - mMinTouchOffset = mMaxTouchOffset = getOffset(x, y); + mMinTouchOffset = mMaxTouchOffset = getOffsetForPosition(x, y); // Double tap detection long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime; if (duration <= ViewConfiguration.getDoubleTapTimeout() && isPositionOnText(x, y)) { - final int deltaX = x - mPreviousTapPositionX; - final int deltaY = y - mPreviousTapPositionY; - final int distanceSquared = deltaX * deltaX + deltaY * deltaY; + final float deltaX = x - mPreviousTapPositionX; + final float deltaY = y - mPreviousTapPositionY; + final float distanceSquared = deltaX * deltaX + deltaY * deltaY; if (distanceSquared < mSquaredTouchSlopDistance) { showSuggestions(); mDiscardNextActionUp = true; @@ -9641,9 +9663,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void updateMinAndMaxOffsets(MotionEvent event) { int pointerCount = event.getPointerCount(); for (int index = 0; index < pointerCount; index++) { - final int x = (int) event.getX(index); - final int y = (int) event.getY(index); - int offset = getOffset(x, y); + int offset = getOffsetForPosition(event.getX(index), event.getY(index)); if (offset < mMinTouchOffset) mMinTouchOffset = offset; if (offset > mMaxTouchOffset) mMaxTouchOffset = offset; } @@ -9701,41 +9721,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Get the offset character closest to the specified absolute position. + * Get the character offset closest to the specified absolute position. A typical use case is to + * pass the result of {@link MotionEvent#getX()} and {@link MotionEvent#getY()} to this method. * * @param x The horizontal absolute position of a point on screen * @param y The vertical absolute position of a point on screen * @return the character offset for the character whose position is closest to the specified * position. Returns -1 if there is no layout. - * - * @hide */ - public int getOffset(int x, int y) { + public int getOffsetForPosition(float x, float y) { if (getLayout() == null) return -1; final int line = getLineAtCoordinate(y); final int offset = getOffsetAtCoordinate(line, x); return offset; } - private int convertToLocalHorizontalCoordinate(int x) { + private float convertToLocalHorizontalCoordinate(float x) { x -= getTotalPaddingLeft(); // Clamp the position to inside of the view. - x = Math.max(0, x); + x = Math.max(0.0f, x); x = Math.min(getWidth() - getTotalPaddingRight() - 1, x); x += getScrollX(); return x; } - private int getLineAtCoordinate(int y) { + private int getLineAtCoordinate(float y) { y -= getTotalPaddingTop(); // Clamp the position to inside of the view. - y = Math.max(0, y); + y = Math.max(0.0f, y); y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y); y += getScrollY(); - return getLayout().getLineForVertical(y); + return getLayout().getLineForVertical((int) y); } - private int getOffsetAtCoordinate(int line, int x) { + private int getOffsetAtCoordinate(int line, float x) { x = convertToLocalHorizontalCoordinate(x); return getLayout().getOffsetForHorizontal(line, x); } @@ -9743,7 +9762,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** Returns true if the screen coordinates position (x,y) corresponds to a character displayed * in the view. Returns false when the position is in the empty space of left/right of text. */ - private boolean isPositionOnText(int x, int y) { + private boolean isPositionOnText(float x, float y) { if (getLayout() == null) return false; final int line = getLineAtCoordinate(y); @@ -9765,7 +9784,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; case DragEvent.ACTION_DRAG_LOCATION: - final int offset = getOffset((int) event.getX(), (int) event.getY()); + final int offset = getOffsetForPosition(event.getX(), event.getY()); Selection.setSelection((Spannable)mText, offset); return true; @@ -9789,7 +9808,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener content.append(item.coerceToText(TextView.this.mContext)); } - final int offset = getOffset((int) event.getX(), (int) event.getY()); + final int offset = getOffsetForPosition(event.getX(), event.getY()); Object localState = event.getLocalState(); DragLocalState dragLocalState = null; diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 1e576ce..183cfbd 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -16,25 +16,27 @@ package com.android.internal.app; +import com.android.internal.R; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuPopupHelper; import com.android.internal.view.menu.SubMenuBuilder; -import com.android.internal.widget.AbsActionBarView; import com.android.internal.widget.ActionBarContainer; import com.android.internal.widget.ActionBarContextView; import com.android.internal.widget.ActionBarView; +import com.android.internal.widget.ScrollingTabContainerView; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.animation.TimeInterpolator; import android.app.ActionBar; import android.app.Activity; import android.app.Dialog; import android.app.FragmentTransaction; import android.content.Context; +import android.content.res.Configuration; +import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Handler; import android.view.ActionMode; @@ -43,10 +45,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.view.Window; -import android.view.animation.DecelerateInterpolator; -import android.widget.HorizontalScrollView; import android.widget.SpinnerAdapter; import java.lang.ref.WeakReference; @@ -71,7 +70,7 @@ public class ActionBarImpl extends ActionBar { private ActionBarContextView mContextView; private ActionBarContainer mSplitView; private View mContentView; - private ViewGroup mExternalTabView; + private ScrollingTabContainerView mTabScrollView; private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>(); @@ -90,16 +89,17 @@ public class ActionBarImpl extends ActionBar { private static final int INVALID_POSITION = -1; private int mContextDisplayMode; + private boolean mHasEmbeddedTabs; + private int mContentHeight; final Handler mHandler = new Handler(); + Runnable mTabSelector; private Animator mCurrentShowAnim; private Animator mCurrentModeAnim; private boolean mShowHideAnimationEnabled; boolean mWasHiddenBeforeMode; - private static final TimeInterpolator sFadeOutInterpolator = new DecelerateInterpolator(); - final AnimatorListener mHideListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -150,21 +150,59 @@ public class ActionBarImpl extends ActionBar { "with a compatible window decor layout"); } + mHasEmbeddedTabs = mContext.getResources().getBoolean( + com.android.internal.R.bool.action_bar_embed_tabs); mActionView.setContextView(mContextView); mContextDisplayMode = mActionView.isSplitActionBar() ? CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL; - if (!mActionView.hasEmbeddedTabs()) { - HorizontalScrollView tabScroller = new HorizontalScrollView(mContext); - ViewGroup tabContainer = mActionView.createTabContainer(); - tabScroller.setHorizontalFadingEdgeEnabled(true); - tabScroller.addView(tabContainer); + TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar); + mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0); + a.recycle(); + } + + public void onConfigurationChanged(Configuration newConfig) { + mHasEmbeddedTabs = mContext.getResources().getBoolean( + com.android.internal.R.bool.action_bar_embed_tabs); + + // Switch tab layout configuration if needed + if (!mHasEmbeddedTabs) { + mActionView.setEmbeddedTabView(null); + mContainerView.setTabContainer(mTabScrollView); + } else { + mContainerView.setTabContainer(null); + if (mTabScrollView != null) { + mTabScrollView.setVisibility(View.VISIBLE); + } + mActionView.setEmbeddedTabView(mTabScrollView); + } + + TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar); + mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0); + a.recycle(); + + if (mTabScrollView != null) { + mTabScrollView.getLayoutParams().height = mContentHeight; + mTabScrollView.requestLayout(); + } + } + + private void ensureTabsExist() { + if (mTabScrollView != null) { + return; + } + + ScrollingTabContainerView tabScroller = mActionView.createTabContainer(); + + if (mHasEmbeddedTabs) { + tabScroller.setVisibility(View.VISIBLE); + mActionView.setEmbeddedTabView(tabScroller); + } else { tabScroller.setVisibility(getNavigationMode() == NAVIGATION_MODE_TABS ? View.VISIBLE : View.GONE); - mActionView.setExternalTabLayout(tabContainer); mContainerView.setTabContainer(tabScroller); - mExternalTabView = tabScroller; } + mTabScrollView = tabScroller; } /** @@ -269,7 +307,7 @@ public class ActionBarImpl extends ActionBar { selectTab(null); } mTabs.clear(); - mActionView.removeAllTabs(); + mTabScrollView.removeAllTabs(); mSavedTabPosition = INVALID_POSITION; } @@ -365,7 +403,8 @@ public class ActionBarImpl extends ActionBar { @Override public void addTab(Tab tab, boolean setSelected) { - mActionView.addTab(tab, setSelected); + ensureTabsExist(); + mTabScrollView.addTab(tab, setSelected); configureTab(tab, mTabs.size()); if (setSelected) { selectTab(tab); @@ -374,7 +413,8 @@ public class ActionBarImpl extends ActionBar { @Override public void addTab(Tab tab, int position, boolean setSelected) { - mActionView.addTab(tab, position, setSelected); + ensureTabsExist(); + mTabScrollView.addTab(tab, position, setSelected); configureTab(tab, position); if (setSelected) { selectTab(tab); @@ -393,9 +433,14 @@ public class ActionBarImpl extends ActionBar { @Override public void removeTabAt(int position) { + if (mTabScrollView == null) { + // No tabs around to remove + return; + } + int selectedTabPosition = mSelectedTab != null ? mSelectedTab.getPosition() : mSavedTabPosition; - mActionView.removeTabAt(position); + mTabScrollView.removeTabAt(position); TabImpl removedTab = mTabs.remove(position); if (removedTab != null) { removedTab.setPosition(-1); @@ -424,9 +469,10 @@ public class ActionBarImpl extends ActionBar { if (mSelectedTab == tab) { if (mSelectedTab != null) { mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans); + mTabScrollView.animateToTab(tab.getPosition()); } } else { - mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION); + mTabScrollView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION); if (mSelectedTab != null) { mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans); } @@ -705,7 +751,9 @@ public class ActionBarImpl extends ActionBar { @Override public Tab setCustomView(View view) { mCustomView = view; - if (mPosition >= 0) mActionView.updateTab(mPosition); + if (mPosition >= 0) { + mTabScrollView.updateTab(mPosition); + } return this; } @@ -736,7 +784,9 @@ public class ActionBarImpl extends ActionBar { @Override public Tab setIcon(Drawable icon) { mIcon = icon; - if (mPosition >= 0) mActionView.updateTab(mPosition); + if (mPosition >= 0) { + mTabScrollView.updateTab(mPosition); + } return this; } @@ -748,7 +798,9 @@ public class ActionBarImpl extends ActionBar { @Override public Tab setText(CharSequence text) { mText = text; - if (mPosition >= 0) mActionView.updateTab(mPosition); + if (mPosition >= 0) { + mTabScrollView.updateTab(mPosition); + } return this; } @@ -818,15 +870,16 @@ public class ActionBarImpl extends ActionBar { mSavedTabPosition = getSelectedNavigationIndex(); selectTab(null); if (!mActionView.hasEmbeddedTabs()) { - mExternalTabView.setVisibility(View.GONE); + mTabScrollView.setVisibility(View.GONE); } break; } mActionView.setNavigationMode(mode); switch (mode) { case NAVIGATION_MODE_TABS: + ensureTabsExist(); if (!mActionView.hasEmbeddedTabs()) { - mExternalTabView.setVisibility(View.VISIBLE); + mTabScrollView.setVisibility(View.VISIBLE); } if (mSavedTabPosition != INVALID_POSITION) { setSelectedNavigationItem(mSavedTabPosition); diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 0f086f6..5e9cd23 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -32,7 +32,6 @@ import dalvik.system.VMRuntime; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.LogManager; import java.util.TimeZone; @@ -45,6 +44,7 @@ import org.apache.harmony.luni.internal.util.TimezoneGetter; */ public class RuntimeInit { private final static String TAG = "AndroidRuntime"; + private final static boolean DEBUG = false; /** true if commonInit() has been called */ private static boolean initialized; @@ -89,14 +89,14 @@ public class RuntimeInit { } private static final void commonInit() { - if (false) Slog.d(TAG, "Entered RuntimeInit!"); + if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!"); /* set default handler; this applies to all threads in the VM */ Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); int hasQwerty = getQwertyKeyboard(); - if (false) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty); + if (DEBUG) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty); if (hasQwerty == 1) { System.setProperty("qwerty", "1"); } @@ -183,11 +183,6 @@ public class RuntimeInit { */ private static void invokeStaticMain(String className, String[] argv) throws ZygoteInit.MethodAndArgsCaller { - - // We want to be fairly aggressive about heap utilization, to avoid - // holding on to a lot of memory that isn't needed. - VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); - Class<?> cl; try { @@ -225,6 +220,13 @@ public class RuntimeInit { } public static final void main(String[] argv) { + if (argv.length == 2 && argv[1].equals("application")) { + if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application"); + redirectLogStreams(); + } else { + if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool"); + } + commonInit(); /* @@ -233,7 +235,7 @@ public class RuntimeInit { */ finishInit(); - if (false) Slog.d(TAG, "Leaving RuntimeInit!"); + if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!"); } public static final native void finishInit(); @@ -245,7 +247,6 @@ public class RuntimeInit { * * Current recognized args: * <ul> - * <li> --nice-name=<i>nice name to appear in ps</i> * <li> <code> [--] <start class name> <args> * </ul> * @@ -253,45 +254,60 @@ public class RuntimeInit { */ public static final void zygoteInit(String[] argv) throws ZygoteInit.MethodAndArgsCaller { - // TODO: Doing this here works, but it seems kind of arbitrary. Find - // a better place. The goal is to set it up for applications, but not - // tools like am. - System.out.close(); - System.setOut(new AndroidPrintStream(Log.INFO, "System.out")); - System.err.close(); - System.setErr(new AndroidPrintStream(Log.WARN, "System.err")); + if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote"); + + redirectLogStreams(); commonInit(); zygoteInitNative(); - int curArg = 0; - for ( /* curArg */ ; curArg < argv.length; curArg++) { - String arg = argv[curArg]; - - if (arg.equals("--")) { - curArg++; - break; - } else if (!arg.startsWith("--")) { - break; - } else if (arg.startsWith("--nice-name=")) { - String niceName = arg.substring(arg.indexOf('=') + 1); - Process.setArgV0(niceName); - } - } + applicationInit(argv); + } - if (curArg == argv.length) { - Slog.e(TAG, "Missing classname argument to RuntimeInit!"); + /** + * The main function called when an application is started through a + * wrapper process. + * + * When the wrapper starts, the runtime starts {@link RuntimeInit#main} + * which calls {@link WrapperInit#main} which then calls this method. + * So we don't need to call commonInit() here. + * + * @param argv arg strings + */ + public static void wrapperInit(String[] argv) + throws ZygoteInit.MethodAndArgsCaller { + if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper"); + + applicationInit(argv); + } + + private static void applicationInit(String[] argv) + throws ZygoteInit.MethodAndArgsCaller { + // We want to be fairly aggressive about heap utilization, to avoid + // holding on to a lot of memory that isn't needed. + VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); + + final Arguments args; + try { + args = new Arguments(argv); + } catch (IllegalArgumentException ex) { + Slog.e(TAG, ex.getMessage()); // let the process exit return; } // Remaining arguments are passed to the start class's static main + invokeStaticMain(args.startClass, args.startArgs); + } - String startClass = argv[curArg++]; - String[] startArgs = new String[argv.length - curArg]; - - System.arraycopy(argv, curArg, startArgs, 0, startArgs.length); - invokeStaticMain(startClass, startArgs); + /** + * Redirect System.out and System.err to the Android log. + */ + public static void redirectLogStreams() { + System.out.close(); + System.setOut(new AndroidPrintStream(Log.INFO, "System.out")); + System.err.close(); + System.setErr(new AndroidPrintStream(Log.WARN, "System.err")); } public static final native void zygoteInitNative(); @@ -351,4 +367,55 @@ public class RuntimeInit { // Register handlers for DDM messages. android.ddm.DdmRegister.registerHandlers(); } + + /** + * Handles argument parsing for args related to the runtime. + * + * Current recognized args: + * <ul> + * <li> <code> [--] <start class name> <args> + * </ul> + */ + static class Arguments { + /** first non-option argument */ + String startClass; + + /** all following arguments */ + String[] startArgs; + + /** + * Constructs instance and parses args + * @param args runtime command-line args + * @throws IllegalArgumentException + */ + Arguments(String args[]) throws IllegalArgumentException { + parseArgs(args); + } + + /** + * Parses the commandline arguments intended for the Runtime. + */ + private void parseArgs(String args[]) + throws IllegalArgumentException { + int curArg = 0; + for (; curArg < args.length; curArg++) { + String arg = args[curArg]; + + if (arg.equals("--")) { + curArg++; + break; + } else if (!arg.startsWith("--")) { + break; + } + } + + if (curArg == args.length) { + throw new IllegalArgumentException("Missing classname argument to RuntimeInit!"); + } + + startClass = args[curArg++]; + startArgs = new String[args.length - curArg]; + System.arraycopy(args, curArg, startArgs, 0, startArgs.length); + } + } } diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java new file mode 100644 index 0000000..18d6caa --- /dev/null +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.os.Process; +import android.util.Slog; + +import java.io.DataOutputStream; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; + +import libcore.io.IoUtils; +import libcore.io.Libcore; + +import dalvik.system.Zygote; + +/** + * Startup class for the wrapper process. + * @hide + */ +public class WrapperInit { + private final static String TAG = "AndroidRuntime"; + + /** + * Class not instantiable. + */ + private WrapperInit() { + } + + /** + * The main function called when starting a runtime application through a + * wrapper process instead of by forking Zygote. + * + * The first argument specifies the file descriptor for a pipe that should receive + * the pid of this process, or 0 if none. The remaining arguments are passed to + * the runtime. + * + * @param args The command-line arguments. + */ + public static void main(String[] args) { + try { + int fdNum = Integer.parseInt(args[0], 10); + if (fdNum != 0) { + try { + FileDescriptor fd = ZygoteInit.createFileDescriptor(fdNum); + DataOutputStream os = new DataOutputStream(new FileOutputStream(fd)); + os.writeInt(Process.myPid()); + os.close(); + IoUtils.closeQuietly(fd); + } catch (IOException ex) { + Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex); + } + } + + String[] runtimeArgs = new String[args.length - 1]; + System.arraycopy(args, 1, runtimeArgs, 0, runtimeArgs.length); + RuntimeInit.wrapperInit(runtimeArgs); + } catch (ZygoteInit.MethodAndArgsCaller caller) { + caller.run(); + } + } + + /** + * Executes a runtime application with a wrapper command. + * This method never returns. + * + * @param invokeWith The wrapper command. + * @param niceName The nice name for the application, or null if none. + * @param pipeFd The pipe to which the application's pid should be written, or null if none. + * @param args Arguments for {@link RuntimeInit.main}. + */ + public static void execApplication(String invokeWith, String niceName, + FileDescriptor pipeFd, String[] args) { + StringBuilder command = new StringBuilder(invokeWith); + command.append(" /system/bin/app_process /system/bin --application"); + if (niceName != null) { + command.append(" '--nice-name=").append(niceName).append("'"); + } + command.append(" com.android.internal.os.WrapperInit "); + command.append(pipeFd != null ? pipeFd.getInt$() : 0); + Zygote.appendQuotedShellArgs(command, args); + Zygote.execShell(command.toString()); + } + + /** + * Executes a standalone application with a wrapper command. + * This method never returns. + * + * @param invokeWith The wrapper command. + * @param classPath The class path. + * @param className The class name to invoke. + * @param args Arguments for the main() method of the specified class. + */ + public static void execStandalone(String invokeWith, String classPath, String className, + String[] args) { + StringBuilder command = new StringBuilder(invokeWith); + command.append(" /system/bin/dalvikvm -classpath '").append(classPath); + command.append("' ").append(className); + Zygote.appendQuotedShellArgs(command, args); + Zygote.execShell(command.toString()); + } +} diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index c473fd2..b872e22 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -26,14 +26,20 @@ import dalvik.system.PathClassLoader; import dalvik.system.Zygote; import java.io.BufferedReader; +import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileDescriptor; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.util.ArrayList; +import libcore.io.ErrnoException; +import libcore.io.IoUtils; +import libcore.io.Libcore; + /** * A connection that can make spawn requests. */ @@ -193,15 +199,20 @@ class ZygoteConnection { new FileOutputStream(descriptors[2])); } - int pid; + int pid = -1; + FileDescriptor childPipeFd = null; + FileDescriptor serverPipeFd = null; try { parsedArgs = new Arguments(args); applyUidSecurityPolicy(parsedArgs, peer); - applyDebuggerSecurityPolicy(parsedArgs); applyRlimitSecurityPolicy(parsedArgs, peer); applyCapabilitiesSecurityPolicy(parsedArgs, peer); + applyInvokeWithSecurityPolicy(parsedArgs, peer); + + applyDebuggerSystemProperty(parsedArgs); + applyInvokeWithSystemProperty(parsedArgs); int[][] rlimits = null; @@ -209,25 +220,45 @@ class ZygoteConnection { rlimits = parsedArgs.rlimits.toArray(intArray2d); } + if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) { + FileDescriptor[] pipeFds = Libcore.os.pipe(); + childPipeFd = pipeFds[1]; + serverPipeFd = pipeFds[0]; + ZygoteInit.setCloseOnExec(serverPipeFd, true); + } + pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits); + } catch (IOException ex) { + logAndPrintError(newStderr, "Exception creating pipe", ex); + } catch (ErrnoException ex) { + logAndPrintError(newStderr, "Exception creating pipe", ex); } catch (IllegalArgumentException ex) { - logAndPrintError (newStderr, "Invalid zygote arguments", ex); - pid = -1; + logAndPrintError(newStderr, "Invalid zygote arguments", ex); } catch (ZygoteSecurityException ex) { logAndPrintError(newStderr, "Zygote security policy prevents request: ", ex); - pid = -1; } - if (pid == 0) { - // in child - handleChildProc(parsedArgs, descriptors, newStderr); - // should never happen - return true; - } else { /* pid != 0 */ - // in parent...pid of < 0 means failure - return handleParentProc(pid, descriptors, parsedArgs); + try { + if (pid == 0) { + // in child + IoUtils.closeQuietly(serverPipeFd); + serverPipeFd = null; + handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); + + // should never get here, the child is expected to either + // throw ZygoteInit.MethodAndArgsCaller or exec(). + return true; + } else { + // in parent...pid of < 0 means failure + IoUtils.closeQuietly(childPipeFd); + childPipeFd = null; + return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); + } + } finally { + IoUtils.closeQuietly(childPipeFd); + IoUtils.closeQuietly(serverPipeFd); } } @@ -244,8 +275,8 @@ class ZygoteConnection { } /** - * Handles argument parsing for args related to the zygote spawner.<p> - + * Handles argument parsing for args related to the zygote spawner. + * * Current recognized args: * <ul> * <li> --setuid=<i>uid of child process, defaults to 0</i> @@ -274,6 +305,7 @@ class ZygoteConnection { * be handed off to com.android.internal.os.RuntimeInit, rather than * processed directly * Android runtime startup (eg, Binder initialization) is also eschewed. + * <li> --nice-name=<i>nice name to appear in ps</i> * <li> If <code>--runtime-init</code> is present: * [--] <args for RuntimeInit > * <li> If <code>--runtime-init</code> is absent: @@ -307,6 +339,9 @@ class ZygoteConnection { /** from --runtime-init */ boolean runtimeInit; + /** from --nice-name */ + String niceName; + /** from --capabilities */ boolean capabilitiesSpecified; long permittedCapabilities; @@ -315,6 +350,9 @@ class ZygoteConnection { /** from all --rlimit=r,c,m */ ArrayList<int[]> rlimits; + /** from --invoke-with */ + String invokeWith; + /** * Any args after and including the first non-option arg * (or after a '--') @@ -438,6 +476,23 @@ class ZygoteConnection { for (int i = params.length - 1; i >= 0 ; i--) { gids[i] = Integer.parseInt(params[i]); } + } else if (arg.equals("--invoke-with")) { + if (invokeWith != null) { + throw new IllegalArgumentException( + "Duplicate arg specified"); + } + try { + invokeWith = args[++curArg]; + } catch (IndexOutOfBoundsException ex) { + throw new IllegalArgumentException( + "--invoke-with requires argument"); + } + } else if (arg.startsWith("--nice-name=")) { + if (niceName != null) { + throw new IllegalArgumentException( + "Duplicate arg specified"); + } + niceName = arg.substring(arg.indexOf('=') + 1); } else { break; } @@ -567,14 +622,15 @@ class ZygoteConnection { /** - * Applies debugger security policy. + * Applies debugger system properties to the zygote arguments. + * * If "ro.debuggable" is "1", all apps are debuggable. Otherwise, * the debugger state is specified via the "--enable-debugger" flag * in the spawn request. * * @param args non-null; zygote spawner args */ - private static void applyDebuggerSecurityPolicy(Arguments args) { + public static void applyDebuggerSystemProperty(Arguments args) { if ("1".equals(SystemProperties.get("ro.debuggable"))) { args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; } @@ -664,12 +720,56 @@ class ZygoteConnection { } /** + * Applies zygote security policy. + * Based on the credentials of the process issuing a zygote command: + * <ol> + * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a + * wrapper command. + * <li> Any other uid may not specify any invoke-with argument. + * </ul> + * + * @param args non-null; zygote spawner arguments + * @param peer non-null; peer credentials + * @throws ZygoteSecurityException + */ + private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer) + throws ZygoteSecurityException { + int peerUid = peer.getUid(); + + if (args.invokeWith != null && peerUid != 0) { + throw new ZygoteSecurityException("Peer is not permitted to specify " + + "an explicit invoke-with wrapper command"); + } + } + + /** + * Applies invoke-with system properties to the zygote arguments. + * + * @param parsedArgs non-null; zygote args + */ + public static void applyInvokeWithSystemProperty(Arguments args) { + if (args.invokeWith == null && args.niceName != null) { + if (args.niceName != null) { + String property = "wrap." + args.niceName; + if (property.length() > 31) { + property = property.substring(0, 31); + } + args.invokeWith = SystemProperties.get(property); + if (args.invokeWith != null && args.invokeWith.length() == 0) { + args.invokeWith = null; + } + } + } + } + + /** * Handles post-fork setup of child proc, closing sockets as appropriate, * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller * if successful or returning if failed. * * @param parsedArgs non-null; zygote args * @param descriptors null-ok; new file descriptors for stdio if available. + * @param pipeFd null-ok; pipe for communication back to Zygote. * @param newStderr null-ok; stream to use for stderr until stdio * is reopened. * @@ -677,7 +777,7 @@ class ZygoteConnection { * trampoline to code that invokes static main. */ private void handleChildProc(Arguments parsedArgs, - FileDescriptor[] descriptors, PrintStream newStderr) + FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) throws ZygoteInit.MethodAndArgsCaller { /* @@ -704,7 +804,7 @@ class ZygoteConnection { descriptors[1], descriptors[2]); for (FileDescriptor fd: descriptors) { - ZygoteInit.closeDescriptor(fd); + IoUtils.closeQuietly(fd); } newStderr = System.err; } catch (IOException ex) { @@ -712,37 +812,48 @@ class ZygoteConnection { } } - if (parsedArgs.runtimeInit) { - RuntimeInit.zygoteInit(parsedArgs.remainingArgs); - } else { - ClassLoader cloader; + if (parsedArgs.niceName != null) { + Process.setArgV0(parsedArgs.niceName); + } - if (parsedArgs.classpath != null) { - cloader - = new PathClassLoader(parsedArgs.classpath, - ClassLoader.getSystemClassLoader()); + if (parsedArgs.runtimeInit) { + if (parsedArgs.invokeWith != null) { + WrapperInit.execApplication(parsedArgs.invokeWith, + parsedArgs.niceName, pipeFd, parsedArgs.remainingArgs); } else { - cloader = ClassLoader.getSystemClassLoader(); + RuntimeInit.zygoteInit(parsedArgs.remainingArgs); } - + } else { String className; try { className = parsedArgs.remainingArgs[0]; } catch (ArrayIndexOutOfBoundsException ex) { - logAndPrintError (newStderr, + logAndPrintError(newStderr, "Missing required class name argument", null); return; } - String[] mainArgs - = new String[parsedArgs.remainingArgs.length - 1]; + String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1]; System.arraycopy(parsedArgs.remainingArgs, 1, mainArgs, 0, mainArgs.length); - try { - ZygoteInit.invokeStaticMain(cloader, className, mainArgs); - } catch (RuntimeException ex) { - logAndPrintError (newStderr, "Error starting. ", ex); + if (parsedArgs.invokeWith != null) { + WrapperInit.execStandalone(parsedArgs.invokeWith, + parsedArgs.classpath, className, mainArgs); + } else { + ClassLoader cloader; + if (parsedArgs.classpath != null) { + cloader = new PathClassLoader(parsedArgs.classpath, + ClassLoader.getSystemClassLoader()); + } else { + cloader = ClassLoader.getSystemClassLoader(); + } + + try { + ZygoteInit.invokeStaticMain(cloader, className, mainArgs); + } catch (RuntimeException ex) { + logAndPrintError(newStderr, "Error starting.", ex); + } } } } @@ -754,36 +865,54 @@ class ZygoteConnection { * if < 0; * @param descriptors null-ok; file descriptors for child's new stdio if * specified. + * @param pipeFd null-ok; pipe for communication with child. * @param parsedArgs non-null; zygote args * @return true for "exit command loop" and false for "continue command * loop" */ private boolean handleParentProc(int pid, - FileDescriptor[] descriptors, Arguments parsedArgs) { + FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) { + + if (pid > 0) { + setChildPgid(pid); + } + + if (descriptors != null) { + for (FileDescriptor fd: descriptors) { + IoUtils.closeQuietly(fd); + } + } - if(pid > 0) { - // Try to move the new child into the peer's process group. + if (pipeFd != null && pid > 0) { + DataInputStream is = new DataInputStream(new FileInputStream(pipeFd)); + int innerPid = -1; try { - ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid())); + innerPid = is.readInt(); } catch (IOException ex) { - // This exception is expected in the case where - // the peer is not in our session - // TODO get rid of this log message in the case where - // getsid(0) != getsid(peer.getPid()) - Log.i(TAG, "Zygote: setpgid failed. This is " - + "normal if peer is not in our session"); + Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex); + } finally { + try { + is.close(); + } catch (IOException ex) { + } } - } - try { - if (descriptors != null) { - for (FileDescriptor fd: descriptors) { - ZygoteInit.closeDescriptor(fd); + // Ensure that the pid reported by the wrapped process is either the + // child process that we forked, or a descendant of it. + if (innerPid > 0) { + int parentPid = innerPid; + while (parentPid > 0 && parentPid != pid) { + parentPid = Process.getParentPid(parentPid); + } + if (parentPid > 0) { + Log.i(TAG, "Wrapped process has pid " + innerPid); + pid = innerPid; + } else { + Log.w(TAG, "Wrapped process reported a pid that is not a child of " + + "the process that we forked: childPid=" + pid + + " innerPid=" + innerPid); } } - } catch (IOException ex) { - Log.e(TAG, "Error closing passed descriptors in " - + "parent process", ex); } try { @@ -808,6 +937,20 @@ class ZygoteConnection { return false; } + private void setChildPgid(int pid) { + // Try to move the new child into the peer's process group. + try { + ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid())); + } catch (IOException ex) { + // This exception is expected in the case where + // the peer is not in our session + // TODO get rid of this log message in the case where + // getsid(0) != getsid(peer.getPid()) + Log.i(TAG, "Zygote: setpgid failed. This is " + + "normal if peer is not in our session"); + } + } + /** * Logs an error message and prints it to the specified stream, if * provided diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index fbe66e5..157c0bf 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable; import android.net.LocalServerSocket; import android.os.Debug; import android.os.FileUtils; +import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; import android.util.EventLog; @@ -68,7 +69,7 @@ public class ZygoteInit { private static final int PRELOAD_GC_THRESHOLD = 50000; public static final String USAGE_STRING = - " <\"true\"|\"false\" for startSystemServer>"; + " <\"start-system-server\"|\"\" for startSystemServer>"; private static LocalServerSocket sServerSocket; @@ -441,11 +442,20 @@ public class ZygoteInit { // set umask to 0077 so new files and directories will default to owner-only permissions. FileUtils.setUMask(FileUtils.S_IRWXG | FileUtils.S_IRWXO); - /* - * Pass the remaining arguments to SystemServer. - * "--nice-name=system_server com.android.server.SystemServer" - */ - RuntimeInit.zygoteInit(parsedArgs.remainingArgs); + if (parsedArgs.niceName != null) { + Process.setArgV0(parsedArgs.niceName); + } + + if (parsedArgs.invokeWith != null) { + WrapperInit.execApplication(parsedArgs.invokeWith, + parsedArgs.niceName, null, parsedArgs.remainingArgs); + } else { + /* + * Pass the remaining arguments to SystemServer. + */ + RuntimeInit.zygoteInit(parsedArgs.remainingArgs); + } + /* should never reach here */ } @@ -470,20 +480,13 @@ public class ZygoteInit { try { parsedArgs = new ZygoteConnection.Arguments(args); - - /* - * Enable debugging of the system process if *either* the command line flags - * indicate it should be debuggable or the ro.debuggable system property - * is set to "1" - */ - int debugFlags = parsedArgs.debugFlags; - if ("1".equals(SystemProperties.get("ro.debuggable"))) - debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; + ZygoteConnection.applyDebuggerSystemProperty(parsedArgs); + ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs); /* Request to fork the system server process */ pid = Zygote.forkSystemServer( parsedArgs.uid, parsedArgs.gid, - parsedArgs.gids, debugFlags, null, + parsedArgs.gids, parsedArgs.debugFlags, null, parsedArgs.permittedCapabilities, parsedArgs.effectiveCapabilities); } catch (IllegalArgumentException ex) { @@ -522,9 +525,9 @@ public class ZygoteInit { throw new RuntimeException(argv[0] + USAGE_STRING); } - if (argv[1].equals("true")) { + if (argv[1].equals("start-system-server")) { startSystemServer(); - } else if (!argv[1].equals("false")) { + } else if (!argv[1].equals("")) { throw new RuntimeException(argv[0] + USAGE_STRING); } @@ -696,15 +699,6 @@ public class ZygoteInit { FileDescriptor out, FileDescriptor err) throws IOException; /** - * Calls close() on a file descriptor - * - * @param fd descriptor to close - * @throws IOException - */ - static native void closeDescriptor(FileDescriptor fd) - throws IOException; - - /** * Toggles the close-on-exec flag for the specified file descriptor. * * @param fd non-null; file descriptor diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java index 290bf08..7b4f216 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuView.java @@ -89,7 +89,6 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo final int childCount = getChildCount(); final int midVertical = (top + bottom) / 2; final int dividerWidth = getDividerWidth(); - boolean hasOverflow = false; int overflowWidth = 0; int nonOverflowWidth = 0; int nonOverflowCount = 0; @@ -102,7 +101,6 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo LayoutParams p = (LayoutParams) v.getLayoutParams(); if (p.isOverflowButton) { - hasOverflow = true; overflowWidth = v.getMeasuredWidth(); if (hasDividerBeforeChildAt(i)) { overflowWidth += dividerWidth; @@ -125,15 +123,12 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } } - // Try to center non-overflow items with uniformly spaced padding, including on the edges. - // Overflow will always pin to the right edge. If there isn't enough room for that, - // center in the remaining space. + // Fill action items from the left. Overflow will always pin to the right edge. if (nonOverflowWidth <= widthRemaining - overflowWidth) { widthRemaining -= overflowWidth; } - final int spacing = (widthRemaining - nonOverflowWidth) / (nonOverflowCount + 1); - int startLeft = getPaddingLeft() + overflowWidth + spacing; + int startLeft = getPaddingLeft(); for (int i = 0; i < childCount; i++) { final View v = getChildAt(i); final LayoutParams lp = (LayoutParams) v.getLayoutParams(); @@ -146,7 +141,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo int height = v.getMeasuredHeight(); int t = midVertical - (height / 2); v.layout(startLeft, t, startLeft + width, t + height); - startLeft += width + lp.rightMargin + spacing; + startLeft += width + lp.rightMargin; } } diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java index ad773ee..834041f 100644 --- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java +++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java @@ -76,6 +76,12 @@ public class SubMenuBuilder extends MenuBuilder implements SubMenu { return mParentMenu; } + @Override + boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) { + return super.dispatchMenuItemSelected(menu, item) || + mParentMenu.dispatchMenuItemSelected(menu, item); + } + public SubMenu setIcon(Drawable icon) { mItem.setIcon(icon); return this; diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index f1887eb..ff04735 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -78,7 +78,7 @@ public class ActionBarView extends AbsActionBarView { private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.LEFT | Gravity.CENTER_VERTICAL; - private final int mContentHeight; + private int mContentHeight; private int mNavigationMode; private int mDisplayOptions = ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP; @@ -95,8 +95,7 @@ public class ActionBarView extends AbsActionBarView { private TextView mSubtitleView; private Spinner mSpinner; private LinearLayout mListNavLayout; - private HorizontalScrollView mTabScrollView; - private ViewGroup mTabLayout; + private ScrollingTabContainerView mTabScrollView; private View mCustomNavView; private ProgressBar mProgressView; private ProgressBar mIndeterminateProgressView; @@ -122,6 +121,8 @@ public class ActionBarView extends AbsActionBarView { private SpinnerAdapter mSpinnerAdapter; private OnNavigationListener mCallback; + private Runnable mTabSelector; + private final AdapterView.OnItemSelectedListener mNavItemSelectedListener = new AdapterView.OnItemSelectedListener() { public void onItemSelected(AdapterView parent, View view, int position, long id) { @@ -199,8 +200,6 @@ public class ActionBarView extends AbsActionBarView { mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0); mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0); - mIncludeTabs = a.getBoolean(R.styleable.ActionBar_embeddedTabs, true); - setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT)); final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0); @@ -229,6 +228,12 @@ public class ActionBarView extends AbsActionBarView { } @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + removeCallbacks(mTabSelector); + } + + @Override public boolean shouldDelayChildPressedState() { return false; } @@ -247,6 +252,11 @@ public class ActionBarView extends AbsActionBarView { addView(mIndeterminateProgressView); } + public void setContentHeight(int height) { + mContentHeight = height; + requestLayout(); + } + public void setSplitActionBar(boolean splitActionBar) { if (mSplitActionBar != splitActionBar) { if (mMenuView != null) { @@ -271,8 +281,9 @@ public class ActionBarView extends AbsActionBarView { return mIncludeTabs; } - public void setExternalTabLayout(ViewGroup tabLayout) { - mTabLayout = tabLayout; + public void setEmbeddedTabView(ScrollingTabContainerView tabs) { + mTabScrollView = tabs; + mIncludeTabs = tabs != null; } public void setCallback(OnNavigationListener callback) { @@ -489,7 +500,7 @@ public class ActionBarView extends AbsActionBarView { } break; case ActionBar.NAVIGATION_MODE_TABS: - if (mTabScrollView != null) { + if (mTabScrollView != null && mIncludeTabs) { removeView(mTabScrollView); } } @@ -513,8 +524,7 @@ public class ActionBarView extends AbsActionBarView { addView(mListNavLayout); break; case ActionBar.NAVIGATION_MODE_TABS: - ensureTabsExist(); - if (mTabScrollView != null) { + if (mTabScrollView != null && mIncludeTabs) { addView(mTabScrollView); } break; @@ -523,24 +533,17 @@ public class ActionBarView extends AbsActionBarView { requestLayout(); } } - - private void ensureTabsExist() { - if (!mIncludeTabs) return; - - if (mTabScrollView == null) { - mTabScrollView = new HorizontalScrollView(getContext()); - mTabScrollView.setHorizontalFadingEdgeEnabled(true); - mTabLayout = createTabContainer(); - mTabScrollView.addView(mTabLayout); - } - } - public ViewGroup createTabContainer() { - ViewGroup result = new LinearLayout(getContext(), null, + public ScrollingTabContainerView createTabContainer() { + final LinearLayout tabLayout = new LinearLayout(getContext(), null, com.android.internal.R.attr.actionBarTabBarStyle); - result.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, - mContentHeight)); - return result; + tabLayout.setMeasureWithLargestChildEnabled(true); + tabLayout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, mContentHeight)); + + final ScrollingTabContainerView scroller = new ScrollingTabContainerView(mContext); + scroller.setTabLayout(tabLayout); + return scroller; } public void setDropdownAdapter(SpinnerAdapter adapter) { @@ -574,51 +577,6 @@ public class ActionBarView extends AbsActionBarView { return mDisplayOptions; } - private TabView createTabView(ActionBar.Tab tab) { - final TabView tabView = new TabView(getContext(), tab); - tabView.setFocusable(true); - - if (mTabClickListener == null) { - mTabClickListener = new TabClickListener(); - } - tabView.setOnClickListener(mTabClickListener); - return tabView; - } - - public void addTab(ActionBar.Tab tab, boolean setSelected) { - ensureTabsExist(); - View tabView = createTabView(tab); - mTabLayout.addView(tabView); - if (setSelected) { - tabView.setSelected(true); - } - } - - public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { - ensureTabsExist(); - final TabView tabView = createTabView(tab); - mTabLayout.addView(tabView, position); - if (setSelected) { - tabView.setSelected(true); - } - } - - public void updateTab(int position) { - ((TabView) mTabLayout.getChildAt(position)).update(); - } - - public void removeTabAt(int position) { - if (mTabLayout != null) { - mTabLayout.removeViewAt(position); - } - } - - public void removeAllTabs() { - if (mTabLayout != null) { - mTabLayout.removeAllViews(); - } - } - @Override protected LayoutParams generateDefaultLayoutParams() { // Used by custom nav views if they don't supply layout params. Everything else @@ -667,15 +625,6 @@ public class ActionBarView extends AbsActionBarView { addView(mTitleLayout); } - public void setTabSelected(int position) { - ensureTabsExist(); - final int tabCount = mTabLayout.getChildCount(); - for (int i = 0; i < tabCount; i++) { - final View child = mTabLayout.getChildAt(i); - child.setSelected(i == position); - } - } - public void setContextView(ActionBarContextView view) { mContextView = view; } @@ -948,97 +897,6 @@ public class ActionBarView extends AbsActionBarView { } } - private static class TabView extends LinearLayout { - private ActionBar.Tab mTab; - private TextView mTextView; - private ImageView mIconView; - private View mCustomView; - - public TabView(Context context, ActionBar.Tab tab) { - super(context, null, com.android.internal.R.attr.actionBarTabStyle); - mTab = tab; - - update(); - - setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.MATCH_PARENT, 1)); - } - - public void update() { - final ActionBar.Tab tab = mTab; - final View custom = tab.getCustomView(); - if (custom != null) { - addView(custom); - mCustomView = custom; - if (mTextView != null) mTextView.setVisibility(GONE); - if (mIconView != null) { - mIconView.setVisibility(GONE); - mIconView.setImageDrawable(null); - } - } else { - if (mCustomView != null) { - removeView(mCustomView); - mCustomView = null; - } - - final Drawable icon = tab.getIcon(); - final CharSequence text = tab.getText(); - - if (icon != null) { - if (mIconView == null) { - ImageView iconView = new ImageView(getContext()); - LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER_VERTICAL; - iconView.setLayoutParams(lp); - addView(iconView, 0); - mIconView = iconView; - } - mIconView.setImageDrawable(icon); - mIconView.setVisibility(VISIBLE); - } else if (mIconView != null) { - mIconView.setVisibility(GONE); - mIconView.setImageDrawable(null); - } - - if (text != null) { - if (mTextView == null) { - TextView textView = new TextView(getContext(), null, - com.android.internal.R.attr.actionBarTabTextStyle); - textView.setSingleLine(); - textView.setEllipsize(TruncateAt.END); - LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER_VERTICAL; - textView.setLayoutParams(lp); - addView(textView); - mTextView = textView; - } - mTextView.setText(text); - mTextView.setVisibility(VISIBLE); - } else { - mTextView.setVisibility(GONE); - } - } - } - - public ActionBar.Tab getTab() { - return mTab; - } - } - - private class TabClickListener implements OnClickListener { - public void onClick(View view) { - TabView tabView = (TabView) view; - tabView.getTab().select(); - final int tabCount = mTabLayout.getChildCount(); - for (int i = 0; i < tabCount; i++) { - final View child = mTabLayout.getChildAt(i); - child.setSelected(child == view); - } - } - } - private static class HomeView extends FrameLayout { private View mUpView; private View mIconView; diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index d789584..bf1c637 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -385,6 +385,7 @@ public class PointerLocationView extends View { .append(" ToolMinor=").append(coords.toolMinor, 3) .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1) .append("deg") + .append(" Distance=").append(coords.getAxisValue(MotionEvent.AXIS_DISTANCE), 1) .append(" VScroll=").append(coords.getAxisValue(MotionEvent.AXIS_VSCROLL), 1) .append(" HScroll=").append(coords.getAxisValue(MotionEvent.AXIS_HSCROLL), 1) .append(" ToolType=").append(MotionEvent.toolTypeToString(toolType)) diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java new file mode 100644 index 0000000..c7d37f2 --- /dev/null +++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget; + +import android.app.ActionBar; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.TextUtils.TruncateAt; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class ScrollingTabContainerView extends HorizontalScrollView { + Runnable mTabSelector; + private TabClickListener mTabClickListener; + + private LinearLayout mTabLayout; + + int mMaxTabWidth; + + public ScrollingTabContainerView(Context context) { + super(context); + setHorizontalScrollBarEnabled(false); + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + setFillViewport(widthMode == MeasureSpec.EXACTLY); + + final int childCount = getChildCount(); + if (childCount > 1 && + (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) { + if (childCount > 2) { + mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f); + } else { + mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2; + } + } else { + mMaxTabWidth = -1; + } + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + public void setTabSelected(int position) { + if (mTabLayout == null) { + return; + } + + final int tabCount = mTabLayout.getChildCount(); + for (int i = 0; i < tabCount; i++) { + final View child = mTabLayout.getChildAt(i); + final boolean isSelected = i == position; + child.setSelected(isSelected); + if (isSelected) { + animateToTab(position); + } + } + } + + public void animateToTab(int position) { + final View tabView = mTabLayout.getChildAt(position); + if (mTabSelector != null) { + removeCallbacks(mTabSelector); + } + mTabSelector = new Runnable() { + public void run() { + final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2; + smoothScrollTo(scrollPos, 0); + mTabSelector = null; + } + }; + post(mTabSelector); + } + + public void setTabLayout(LinearLayout tabLayout) { + if (mTabLayout != tabLayout) { + if (mTabLayout != null) { + ((ViewGroup) mTabLayout.getParent()).removeView(mTabLayout); + } + if (tabLayout != null) { + addView(tabLayout); + } + mTabLayout = tabLayout; + } + } + + public LinearLayout getTabLayout() { + return mTabLayout; + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mTabSelector != null) { + removeCallbacks(mTabSelector); + } + } + + private TabView createTabView(ActionBar.Tab tab) { + final TabView tabView = new TabView(getContext(), tab); + tabView.setFocusable(true); + + if (mTabClickListener == null) { + mTabClickListener = new TabClickListener(); + } + tabView.setOnClickListener(mTabClickListener); + return tabView; + } + + public void addTab(ActionBar.Tab tab, boolean setSelected) { + View tabView = createTabView(tab); + mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, + LayoutParams.MATCH_PARENT, 1)); + if (setSelected) { + tabView.setSelected(true); + } + } + + public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { + final TabView tabView = createTabView(tab); + mTabLayout.addView(tabView, position, new LinearLayout.LayoutParams( + 0, LayoutParams.MATCH_PARENT, 1)); + if (setSelected) { + tabView.setSelected(true); + } + } + + public void updateTab(int position) { + ((TabView) mTabLayout.getChildAt(position)).update(); + } + + public void removeTabAt(int position) { + if (mTabLayout != null) { + mTabLayout.removeViewAt(position); + } + } + + public void removeAllTabs() { + if (mTabLayout != null) { + mTabLayout.removeAllViews(); + } + } + + private class TabView extends LinearLayout { + private ActionBar.Tab mTab; + private TextView mTextView; + private ImageView mIconView; + private View mCustomView; + + public TabView(Context context, ActionBar.Tab tab) { + super(context, null, com.android.internal.R.attr.actionBarTabStyle); + mTab = tab; + + update(); + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + // Re-measure if we went beyond our maximum size. + if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) { + super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY), + heightMeasureSpec); + } + } + + public void update() { + final ActionBar.Tab tab = mTab; + final View custom = tab.getCustomView(); + if (custom != null) { + addView(custom); + mCustomView = custom; + if (mTextView != null) mTextView.setVisibility(GONE); + if (mIconView != null) { + mIconView.setVisibility(GONE); + mIconView.setImageDrawable(null); + } + } else { + if (mCustomView != null) { + removeView(mCustomView); + mCustomView = null; + } + + final Drawable icon = tab.getIcon(); + final CharSequence text = tab.getText(); + + if (icon != null) { + if (mIconView == null) { + ImageView iconView = new ImageView(getContext()); + LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER_VERTICAL; + iconView.setLayoutParams(lp); + addView(iconView, 0); + mIconView = iconView; + } + mIconView.setImageDrawable(icon); + mIconView.setVisibility(VISIBLE); + } else if (mIconView != null) { + mIconView.setVisibility(GONE); + mIconView.setImageDrawable(null); + } + + if (text != null) { + if (mTextView == null) { + TextView textView = new TextView(getContext(), null, + com.android.internal.R.attr.actionBarTabTextStyle); + textView.setSingleLine(); + textView.setEllipsize(TruncateAt.END); + LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER_VERTICAL; + textView.setLayoutParams(lp); + addView(textView); + mTextView = textView; + } + mTextView.setText(text); + mTextView.setVisibility(VISIBLE); + } else { + mTextView.setVisibility(GONE); + } + } + } + + public ActionBar.Tab getTab() { + return mTab; + } + } + + private class TabClickListener implements OnClickListener { + public void onClick(View view) { + TabView tabView = (TabView) view; + tabView.getTab().select(); + final int tabCount = mTabLayout.getChildCount(); + for (int i = 0; i < tabCount; i++) { + final View child = mTabLayout.getChildAt(i); + child.setSelected(child == view); + } + } + } +} diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index b787e9f..e610640 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -808,8 +808,11 @@ char* AndroidRuntime::toSlashClassName(const char* className) * Start the Android runtime. This involves starting the virtual machine * and calling the "static void main(String[] args)" method in the class * named by "className". + * + * Passes the main function two arguments, the class name and the specified + * options string. */ -void AndroidRuntime::start(const char* className, const bool startSystemServer) +void AndroidRuntime::start(const char* className, const char* options) { LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n", className != NULL ? className : "(unknown)"); @@ -820,7 +823,7 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) * 'startSystemServer == true' means runtime is obsolete and not run from * init.rc anymore, so we print out the boot start event here. */ - if (startSystemServer) { + if (strcmp(options, "start-system-server") == 0) { /* track our progress through the boot sequence */ const int LOG_BOOT_PROGRESS_START = 3000; LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, @@ -857,13 +860,13 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) /* * We want to call main() with a String array with arguments in it. - * At present we only have one argument, the class name. Create an - * array to hold it. + * At present we have two arguments, the class name and an option string. + * Create an array to hold them. */ jclass stringClass; jobjectArray strArray; jstring classNameStr; - jstring startSystemServerStr; + jstring optionsStr; stringClass = env->FindClass("java/lang/String"); assert(stringClass != NULL); @@ -872,9 +875,8 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) classNameStr = env->NewStringUTF(className); assert(classNameStr != NULL); env->SetObjectArrayElement(strArray, 0, classNameStr); - startSystemServerStr = env->NewStringUTF(startSystemServer ? - "true" : "false"); - env->SetObjectArrayElement(strArray, 1, startSystemServerStr); + optionsStr = env->NewStringUTF(options); + env->SetObjectArrayElement(strArray, 1, optionsStr); /* * Start VM. This thread becomes the main thread of the VM, and will @@ -909,12 +911,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) LOGW("Warning: VM did not shut down cleanly\n"); } -void AndroidRuntime::start() -{ - start("com.android.internal.os.RuntimeInit", - false /* Don't start the system server */); -} - void AndroidRuntime::onExit(int code) { LOGV("AndroidRuntime onExit calling exit(%d)", code); diff --git a/core/jni/android_os_ParcelFileDescriptor.cpp b/core/jni/android_os_ParcelFileDescriptor.cpp index 4ec131c..99a2d04 100644 --- a/core/jni/android_os_ParcelFileDescriptor.cpp +++ b/core/jni/android_os_ParcelFileDescriptor.cpp @@ -34,20 +34,37 @@ static struct parcel_file_descriptor_offsets_t jfieldID mFileDescriptor; } gParcelFileDescriptorOffsets; -static int android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env, +static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromFd(JNIEnv* env, + jobject clazz, jint origfd) +{ + int fd = dup(origfd); + if (fd < 0) { + jniThrowException(env, "java/io/IOException", strerror(errno)); + return NULL; + } + return jniCreateFileDescriptor(env, fd); +} + +static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromFdNoDup(JNIEnv* env, + jobject clazz, jint fd) +{ + return jniCreateFileDescriptor(env, fd); +} + +static void android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env, jobject clazz, jobjectArray outFds) { int fds[2]; if (pipe(fds) < 0) { - return -errno; + int therr = errno; + jniThrowException(env, "java/io/IOException", strerror(therr)); + return; } for (int i=0; i<2; i++) { jobject fdObj = jniCreateFileDescriptor(env, fds[i]); env->SetObjectArrayElement(outFds, i, fdObj); } - - return 0; } static jint getFd(JNIEnv* env, jobject clazz) @@ -102,7 +119,11 @@ static jlong android_os_ParcelFileDescriptor_getFdNative(JNIEnv* env, jobject cl } static const JNINativeMethod gParcelFileDescriptorMethods[] = { - {"createPipeNative", "([Ljava/io/FileDescriptor;)I", + {"getFileDescriptorFromFd", "(I)Ljava/io/FileDescriptor;", + (void*)android_os_ParcelFileDescriptor_getFileDescriptorFromFd}, + {"getFileDescriptorFromFdNoDup", "(I)Ljava/io/FileDescriptor;", + (void*)android_os_ParcelFileDescriptor_getFileDescriptorFromFdNoDup}, + {"createPipeNative", "([Ljava/io/FileDescriptor;)V", (void*)android_os_ParcelFileDescriptor_createPipeNative}, {"getStatSize", "()J", (void*)android_os_ParcelFileDescriptor_getStatSize}, diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index dced1a5..59b97c2 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -351,7 +351,7 @@ static int register_agent(native_data_t *nat, { DBusMessage *msg, *reply; DBusError err; - bool oob = TRUE; + dbus_bool_t oob = TRUE; if (!dbus_connection_register_object_path(nat->conn, agent_path, &agent_vtable, nat)) { diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp index e627e4a..86fd9cb 100644 --- a/core/jni/com_android_internal_os_ZygoteInit.cpp +++ b/core/jni/com_android_internal_os_ZygoteInit.cpp @@ -131,28 +131,6 @@ static void com_android_internal_os_ZygoteInit_reopenStdio(JNIEnv* env, } while (err < 0 && errno == EINTR); } -static void com_android_internal_os_ZygoteInit_closeDescriptor(JNIEnv* env, - jobject clazz, jobject descriptor) -{ - int fd; - int err; - - fd = jniGetFDFromFileDescriptor(env, descriptor); - - if (env->ExceptionOccurred() != NULL) { - return; - } - - do { - err = close(fd); - } while (err < 0 && errno == EINTR); - - if (err < 0) { - jniThrowIOException(env, errno); - return; - } -} - static void com_android_internal_os_ZygoteInit_setCloseOnExec (JNIEnv *env, jobject clazz, jobject descriptor, jboolean flag) { @@ -332,8 +310,6 @@ static JNINativeMethod gMethods[] = { "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;" "Ljava/io/FileDescriptor;)V", (void *) com_android_internal_os_ZygoteInit_reopenStdio}, - { "closeDescriptor", "(Ljava/io/FileDescriptor;)V", - (void *) com_android_internal_os_ZygoteInit_closeDescriptor}, { "setCloseOnExec", "(Ljava/io/FileDescriptor;Z)V", (void *) com_android_internal_os_ZygoteInit_setCloseOnExec}, { "setCapabilities", "(JJ)V", diff --git a/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png Binary files differdeleted file mode 100644 index 61db22c..0000000 --- a/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png +++ /dev/null diff --git a/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png Binary files differdeleted file mode 100644 index d6478fb..0000000 --- a/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/btn_code_lock_default.png b/core/res/res/drawable-mdpi/btn_code_lock_default.png Binary files differindex f524317..45cc20d 100755..100644 --- a/core/res/res/drawable-mdpi/btn_code_lock_default.png +++ b/core/res/res/drawable-mdpi/btn_code_lock_default.png diff --git a/core/res/res/drawable-mdpi/btn_code_lock_touched.png b/core/res/res/drawable-mdpi/btn_code_lock_touched.png Binary files differindex 5cd436c..45cc20d 100755..100644 --- a/core/res/res/drawable-mdpi/btn_code_lock_touched.png +++ b/core/res/res/drawable-mdpi/btn_code_lock_touched.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png Binary files differindex 20f3d50..0fbdbfa 100644 --- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png Binary files differindex d09ce53..ae97453 100644 --- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png Binary files differindex a9e008c..4127d1e 100644 --- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png Binary files differindex 1ed3065..525ab8a 100644 --- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png Binary files differindex 5710ebf..eb05820 100644 --- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png Binary files differindex dd7d89e..416b2c7 100644 --- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png +++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png Binary files differindex d29c6c3..97ac023 100644 --- a/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png +++ b/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_charging.png b/core/res/res/drawable-mdpi/ic_lock_idle_charging.png Binary files differindex 20d6320..4210db2 100755..100644 --- a/core/res/res/drawable-mdpi/ic_lock_idle_charging.png +++ b/core/res/res/drawable-mdpi/ic_lock_idle_charging.png diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_lock.png b/core/res/res/drawable-mdpi/ic_lock_idle_lock.png Binary files differindex 0206aee..1060f5a 100755..100644 --- a/core/res/res/drawable-mdpi/ic_lock_idle_lock.png +++ b/core/res/res/drawable-mdpi/ic_lock_idle_lock.png diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png b/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png Binary files differindex bb96782..72e4afa 100755..100644 --- a/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png +++ b/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png Binary files differindex 7ddeba5..0bc86c3 100644 --- a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png +++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png Binary files differindex 7201e58..2ab4547 100644 --- a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png +++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png Binary files differindex 8546c5f..fe72d00 100755..100644 --- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png +++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png Binary files differindex a98a29a..be666c6 100755..100644 --- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png +++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png Binary files differindex 6d579cb..9627197 100755..100644 --- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png +++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png diff --git a/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png Binary files differdeleted file mode 100644 index 6e703af..0000000 --- a/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_delete_holo.png b/core/res/res/drawable-mdpi/sym_keyboard_delete_holo.png Binary files differindex 1555791..1555791 100644 --- a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_delete_holo.png +++ b/core/res/res/drawable-mdpi/sym_keyboard_delete_holo.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_shift.png b/core/res/res/drawable-mdpi/sym_keyboard_shift.png Binary files differindex 0566e5a..91d6e32 100644 --- a/core/res/res/drawable-mdpi/sym_keyboard_shift.png +++ b/core/res/res/drawable-mdpi/sym_keyboard_shift.png diff --git a/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png b/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png Binary files differindex ccaf05d..2bd0536 100755..100644 --- a/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png +++ b/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_activated_holo_dark.9.png Binary files differindex a233b0d..a233b0d 100644 --- a/core/res/res/drawable-xlarge-mdpi/textfield_bg_activated_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_bg_activated_holo_dark.9.png diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_default_holo_dark.9.png Binary files differindex 403f502..403f502 100644 --- a/core/res/res/drawable-xlarge-mdpi/textfield_bg_default_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_bg_default_holo_dark.9.png diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_disabled_focused_holo_dark.9.png Binary files differindex 0ded801..0ded801 100644 --- a/core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_focused_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_bg_disabled_focused_holo_dark.9.png diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_disabled_holo_dark.9.png Binary files differindex 27237b8..27237b8 100644 --- a/core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_bg_disabled_holo_dark.9.png diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_focused_holo_dark.9.png Binary files differindex 0e451f1..0e451f1 100644 --- a/core/res/res/drawable-xlarge-mdpi/textfield_bg_focused_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/textfield_bg_focused_holo_dark.9.png diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_default.png b/core/res/res/drawable-mdpi/unlock_default.png Binary files differindex 0a441c0..0a441c0 100644 --- a/core/res/res/drawable-xlarge-mdpi/unlock_default.png +++ b/core/res/res/drawable-mdpi/unlock_default.png diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_halo.png b/core/res/res/drawable-mdpi/unlock_halo.png Binary files differindex 09b0526..09b0526 100644 --- a/core/res/res/drawable-xlarge-mdpi/unlock_halo.png +++ b/core/res/res/drawable-mdpi/unlock_halo.png diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_ring.png b/core/res/res/drawable-mdpi/unlock_ring.png Binary files differindex 0363a8b..0363a8b 100644 --- a/core/res/res/drawable-xlarge-mdpi/unlock_ring.png +++ b/core/res/res/drawable-mdpi/unlock_ring.png diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_wave.png b/core/res/res/drawable-mdpi/unlock_wave.png Binary files differindex 21bfa24..21bfa24 100644 --- a/core/res/res/drawable-xlarge-mdpi/unlock_wave.png +++ b/core/res/res/drawable-mdpi/unlock_wave.png diff --git a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png b/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png Binary files differdeleted file mode 100644 index 45cc20d..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png b/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png Binary files differdeleted file mode 100644 index 45cc20d..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal.9.png Binary files differdeleted file mode 100644 index 0fbdbfa..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png Binary files differdeleted file mode 100644 index ae97453..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png Binary files differdeleted file mode 100644 index 4127d1e..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed.9.png Binary files differdeleted file mode 100644 index 525ab8a..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png Binary files differdeleted file mode 100644 index eb05820..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png Binary files differdeleted file mode 100644 index 416b2c7..0000000 --- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png Binary files differdeleted file mode 100644 index 97ac023..0000000 --- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png Binary files differdeleted file mode 100644 index 4210db2..0000000 --- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png Binary files differdeleted file mode 100644 index 1060f5a..0000000 --- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png Binary files differdeleted file mode 100644 index 72e4afa..0000000 --- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png Binary files differdeleted file mode 100644 index 0bc86c3..0000000 --- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png Binary files differdeleted file mode 100644 index 2ab4547..0000000 --- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png Binary files differdeleted file mode 100644 index fe72d00..0000000 --- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png Binary files differdeleted file mode 100644 index be666c6..0000000 --- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png Binary files differdeleted file mode 100644 index 9627197..0000000 --- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift.png b/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift.png Binary files differdeleted file mode 100644 index 91d6e32..0000000 --- a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift.png +++ /dev/null diff --git a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift_locked.png b/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift_locked.png Binary files differdeleted file mode 100644 index 2bd0536..0000000 --- a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift_locked.png +++ /dev/null diff --git a/core/res/res/drawable/extract_edit_text.xml b/core/res/res/drawable/extract_edit_text.xml deleted file mode 100644 index c7f66f6..0000000 --- a/core/res/res/drawable/extract_edit_text.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2007 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_focused="true" android:drawable="@drawable/keyboard_textfield_selected" /> - <item android:drawable="@drawable/textfield_disabled" /> -</selector> - diff --git a/core/res/res/layout-xlarge/keyguard.xml b/core/res/res/layout-sw600dp/keyguard.xml index ca629f8..ca629f8 100644 --- a/core/res/res/layout-xlarge/keyguard.xml +++ b/core/res/res/layout-sw600dp/keyguard.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_glogin_unlock.xml index 9779074..9779074 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_glogin_unlock.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_lock.xml b/core/res/res/layout-sw600dp/keyguard_screen_lock.xml index c7aa654..c7aa654 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_lock.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_lock.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml index 1783088..1783088 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml index 63241dd..63241dd 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml index b8cbe51..b8cbe51 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml index 009148f..009148f 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_status_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml index 0a485e2..0a485e2 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_status_land.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_status_port.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml index 346b21e..346b21e 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_status_port.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml index c9c1692..c9c1692 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml index fbb9983..fbb9983 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml index 8acb656..e3d7a3f 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml @@ -47,13 +47,12 @@ <RelativeLayout android:layout_weight="1" android:layout_width="0dip" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:gravity="center_vertical|center_horizontal"> <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern" android:layout_width="354dip" android:layout_height="354dip" - android:layout_marginLeft="143dip" - android:layout_marginTop="201dip" android:layout_gravity="center_vertical" /> diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml index f35897e..f35897e 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml diff --git a/core/res/res/layout/input_method_extract_view.xml b/core/res/res/layout/input_method_extract_view.xml index 689ba7b..7d59d02 100644 --- a/core/res/res/layout/input_method_extract_view.xml +++ b/core/res/res/layout/input_method_extract_view.xml @@ -31,7 +31,6 @@ android:gravity="top" android:minLines="1" android:inputType="text" - android:background="@android:drawable/extract_edit_text" > </android.inputmethodservice.ExtractEditText> diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-sw600dp/config.xml index 4c8bbe6..49ace34 100644 --- a/core/res/res/values-xlarge/config.xml +++ b/core/res/res/values-sw600dp/config.xml @@ -27,6 +27,7 @@ <!-- Show sliding tab before lockscreen --> <bool name="config_enableSlidingTabFirst">false</bool> + <!-- Enable lockscreen rotation --> <bool name="config_enableLockScreenRotation">true</bool> diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml index e058442..b906e1a 100644 --- a/core/res/res/values-xlarge/dimens.xml +++ b/core/res/res/values-xlarge/dimens.xml @@ -25,10 +25,6 @@ <!-- Size of the giant number (unread count) in the notifications --> <dimen name="status_bar_content_number_size">48sp</dimen> - <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. --> - <!-- Margin for permanent screen decorations at the bottom. --> - <dimen name="screen_margin_bottom">48dip</dimen> - <!-- Default height of a key in the password keyboard for alpha --> <dimen name="password_keyboard_key_height_alpha">75dip</dimen> <!-- Default height of a key in the password keyboard for numeric --> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 6c18089..c1e81c3 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -823,6 +823,10 @@ Default value is false. EditText content is always selectable. --> <attr name="textIsSelectable" format="boolean" /> + <!-- When true, IME suggestions will be displayed when the user double taps on editable text. + The default value is true. --> + <attr name="suggestionsEnabled" format="boolean" /> + <!-- Where to ellipsize text. --> <attr name="ellipsize"> <enum name="none" value="0" /> @@ -2877,6 +2881,8 @@ <!-- Indicates that the content of a non-editable text can be selected. --> <attr name="textIsSelectable" /> + <!-- Suggestions will be displayed when the user double taps on editable text. --> + <attr name="suggestionsEnabled" /> </declare-styleable> <!-- An <code>input-extras</code> is a container for extra data to supply to an input method. Contains @@ -4907,9 +4913,6 @@ <!-- Specifies padding that should be applied to the left and right sides of system-provided items in the bar. --> <attr name="itemPadding" format="dimension" /> - <!-- Specifies whether tabs should be embedded within the bar itself (true) - or displayed elsewhere (false). --> - <attr name="embeddedTabs" format="boolean" /> </declare-styleable> <declare-styleable name="ActionMode"> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 39d2329..6529fe1 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -77,6 +77,7 @@ <drawable name="dialog_holo_light_frame">@drawable/dialog_full_holo_light</drawable> <drawable name="input_method_fullscreen_background">#fff9f9f9</drawable> + <drawable name="input_method_fullscreen_background_holo">@drawable/screen_background_holo_dark</drawable> <!-- For date picker widget --> <drawable name="selected_day_background">#ff0092f4</drawable> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 8ad8f67..1957b2a 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1670,5 +1670,6 @@ <public type="attr" name="horizontalDirection" /> <public type="attr" name="fullBackupAgent" /> + <public type="attr" name="suggestionsEnabled" /> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 158d524..816546b 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1852,7 +1852,7 @@ <!-- Do not translate. WebView User Agent string --> <string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>) - AppleWebKit/534.16 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.16</string> + AppleWebKit/534.20 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.20</string> <!-- Do not translate. WebView User Agent targeted content --> <string name="web_user_agent_target_content" translatable="false">"Mobile "</string> @@ -1998,6 +1998,9 @@ <!-- Do not translate. Regex used by AutoFill. --> <string name="autofill_card_ignored_re">^card</string> + <!-- Do not translate. Regex used by AutoFill. --> + <string name="autofill_fax_re">fax<!-- fr-FR -->|télécopie|telecopie<!-- ja-JP -->|ファックス<!-- ru -->|факс<!-- zh-CN -->|传真<!-- zh-TW -->|傳真</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string> @@ -2449,6 +2452,20 @@ <!-- See SMS_DIALOG. This is a button choice to disallow sending the SMSes.. --> <string name="sms_control_no">Cancel</string> + <!-- SIM swap and device reboot Dialog --> <skip /> + <!-- See SIM_REMOVED_DIALOG. This is the title of that dialog. --> + <string name="sim_removed_title">SIM card removed</string> + <!-- See SIM_REMOVED_DIALOG. This is the message of that dialog. --> + <string name="sim_removed_message">The mobile network will be unavailable until you replace the SIM card.</string> + <!-- See SIM_REMOVED_DIALOG. This is the button of that dialog. --> + <string name="sim_done_button">Done</string> + <!-- See SIM_ADDED_DIALOG. This is the title of that dialog. --> + <string name="sim_added_title">SIM card added</string> + <!-- See SIM_ADDED_DIALOG. This is the message of that dialog. --> + <string name="sim_added_message">You must restart your device to access the mobile network.</string> + <!-- See SIM_ADDED_DIALOG. This is the button of that dialog. --> + <string name="sim_restart_button">Restart</string> + <!-- Date/Time picker dialogs strings --> <!-- The title of the time picker dialog. [CHAR LIMIT=NONE] --> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 26b5d95..e95094f 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1078,7 +1078,6 @@ <item name="android:progressBarStyle">@android:style/Widget.ProgressBar.Horizontal</item> <item name="android:indeterminateProgressStyle">@android:style/Widget.ProgressBar.Small</item> <item name="android:homeLayout">@android:layout/action_bar_home</item> - <item name="android:embeddedTabs">@android:bool/action_bar_embed_tabs</item> </style> <style name="Widget.ActionMode"> @@ -1122,6 +1121,7 @@ </style> <style name="Widget.ActionBarView_TabView"> + <item name="android:gravity">center_horizontal</item> <item name="android:background">@drawable/minitab_lt</item> <item name="android:paddingLeft">4dip</item> <item name="android:paddingRight">4dip</item> @@ -1795,6 +1795,9 @@ </style> <style name="Widget.Holo.ActionBarView_TabBar" parent="Widget.ActionBarView_TabBar"> + <item name="android:divider">?android:attr/dividerVertical</item> + <item name="android:showDividers">middle</item> + <item name="android:dividerPadding">8dip</item> </style> <style name="Widget.Holo.ActionBarView_TabText" parent="Widget.ActionBarView_TabText"> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index aa9ddff..0a614b2 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -681,7 +681,7 @@ and a few custom attributes. --> <style name="Theme.Holo.InputMethod" parent="Theme.Holo.Panel"> <item name="android:windowAnimationStyle">@android:style/Animation.InputMethod</item> - <item name="android:imeFullscreenBackground">@android:drawable/input_method_fullscreen_background</item> + <item name="android:imeFullscreenBackground">@android:drawable/input_method_fullscreen_background_holo</item> <item name="android:imeExtractEnterAnimation">@android:anim/input_method_extract_enter</item> <item name="android:imeExtractExitAnimation">@android:anim/input_method_extract_exit</item> </style> diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java new file mode 100644 index 0000000..45719c2 --- /dev/null +++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.os.SystemClock; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +@SmallTest +public class NetworkStatsTest extends TestCase { + + private static final String TEST_IFACE = "test0"; + + public void testFindIndex() throws Exception { + final NetworkStats stats = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3) + .addEntry(TEST_IFACE, 100, 1024, 0) + .addEntry(TEST_IFACE, 101, 0, 1024) + .addEntry(TEST_IFACE, 102, 1024, 1024).build(); + + assertEquals(2, stats.findIndex(TEST_IFACE, 102)); + assertEquals(2, stats.findIndex(TEST_IFACE, 102)); + assertEquals(0, stats.findIndex(TEST_IFACE, 100)); + assertEquals(-1, stats.findIndex(TEST_IFACE, 6)); + } + + public void testSubtractIdenticalData() throws Exception { + final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + .addEntry(TEST_IFACE, 100, 1024, 0) + .addEntry(TEST_IFACE, 101, 0, 1024).build(); + + final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + .addEntry(TEST_IFACE, 100, 1024, 0) + .addEntry(TEST_IFACE, 101, 0, 1024).build(); + + final NetworkStats result = after.subtract(before); + + assertEquals(0, result.rx[0]); + assertEquals(0, result.tx[0]); + assertEquals(0, result.rx[1]); + assertEquals(0, result.tx[1]); + } + + public void testSubtractIdenticalRows() throws Exception { + final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + .addEntry(TEST_IFACE, 100, 1024, 0) + .addEntry(TEST_IFACE, 101, 0, 1024).build(); + + final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + .addEntry(TEST_IFACE, 100, 1025, 2) + .addEntry(TEST_IFACE, 101, 3, 1028).build(); + + final NetworkStats result = after.subtract(before); + + // expect delta between measurements + assertEquals(1, result.rx[0]); + assertEquals(2, result.tx[0]); + assertEquals(3, result.rx[1]); + assertEquals(4, result.tx[1]); + } + + public void testSubtractNewRows() throws Exception { + final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + .addEntry(TEST_IFACE, 100, 1024, 0) + .addEntry(TEST_IFACE, 101, 0, 1024).build(); + + final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3) + .addEntry(TEST_IFACE, 100, 1024, 0) + .addEntry(TEST_IFACE, 101, 0, 1024) + .addEntry(TEST_IFACE, 102, 1024, 1024).build(); + + final NetworkStats result = after.subtract(before); + + // its okay to have new rows + assertEquals(0, result.rx[0]); + assertEquals(0, result.tx[0]); + assertEquals(0, result.rx[1]); + assertEquals(0, result.tx[1]); + assertEquals(1024, result.rx[2]); + assertEquals(1024, result.tx[2]); + } + +} |
