Merge change Iecf85b40 into eclair-mr2

* changes:
  Rename several files so that readers would not be confused.
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index 7855493..b5a0a55 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
@@ -30,6 +31,12 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.TranslateAnimation;
+import android.view.animation.Animation.AnimationListener;
 import android.widget.ImageView;
 import android.widget.TextView;
 import android.widget.ImageView.ScaleType;
@@ -45,17 +52,18 @@
  *
  */
 public class SlidingTab extends ViewGroup {
-    private static final int ANIMATION_DURATION = 250; // animation transition duration (in ms)
     private static final String LOG_TAG = "SlidingTab";
     private static final boolean DBG = false;
     private static final int HORIZONTAL = 0; // as defined in attrs.xml
     private static final int VERTICAL = 1;
-    private static final int MSG_ANIMATE = 100;
 
     // TODO: Make these configurable
     private static final float THRESHOLD = 2.0f / 3.0f;
     private static final long VIBRATE_SHORT = 30;
     private static final long VIBRATE_LONG = 40;
+    private static final int TRACKING_MARGIN = 50;
+    private static final int ANIM_DURATION = 250; // Time for most animations (in ms)
+    private static final int ANIM_TARGET_TIME = 500; // Time to show targets (in ms)
 
     private OnTriggerListener mOnTriggerListener;
     private int mGrabbedState = OnTriggerListener.NO_HANDLE;
@@ -63,8 +71,6 @@
     private Vibrator mVibrator;
     private float mDensity; // used to scale dimensions for bitmaps.
 
-    private final SlidingTabHandler mHandler = new SlidingTabHandler();
-
     /**
      * Either {@link #HORIZONTAL} or {@link #VERTICAL}.
      */
@@ -77,6 +83,7 @@
     private float mThreshold;
     private Slider mOtherSlider;
     private boolean mAnimating;
+    private Rect mTmpRect;
 
     /**
      * Interface definition for a callback to be invoked when a tab is triggered
@@ -121,6 +128,12 @@
         void onGrabbedStateChange(View v, int grabbedState);
     }
 
+    // TODO: For debugging; remove after glitches debugged.
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+    }
+
     /**
      * Simple container class for all things pertinent to a slider.
      * A slider consists of 3 Views:
@@ -138,6 +151,7 @@
         public static final int ALIGN_RIGHT = 1;
         public static final int ALIGN_TOP = 2;
         public static final int ALIGN_BOTTOM = 3;
+        public static final int ALIGN_UNKNOWN = 4;
 
         /**
          * States for the view.
@@ -150,6 +164,8 @@
         private final TextView text;
         private final ImageView target;
         private int currentState = STATE_NORMAL;
+        private int alignment = ALIGN_UNKNOWN;
+        private int alignment_value;
 
         /**
          * Constructor
@@ -205,12 +221,36 @@
         }
 
         void hide() {
-            // TODO: Animate off the screen
-            text.setVisibility(View.INVISIBLE);
-            tab.setVisibility(View.INVISIBLE);
+            boolean horiz = alignment == ALIGN_LEFT || alignment == ALIGN_RIGHT;
+            int dx = horiz ? (alignment == ALIGN_LEFT ? alignment_value - tab.getRight()
+                    : alignment_value - tab.getLeft()) : 0;
+            int dy = horiz ? 0 : (alignment == ALIGN_TOP ? alignment_value - tab.getBottom()
+                    : alignment_value - tab.getTop());
+
+            Animation trans = new TranslateAnimation(0, dx, 0, dy);
+            trans.setDuration(ANIM_DURATION);
+            trans.setFillAfter(true);
+            tab.startAnimation(trans);
+            text.startAnimation(trans);
             target.setVisibility(View.INVISIBLE);
         }
 
+        void show(boolean animate) {
+            text.setVisibility(View.VISIBLE);
+            tab.setVisibility(View.VISIBLE);
+            //target.setVisibility(View.INVISIBLE);
+            if (animate) {
+                boolean horiz = alignment == ALIGN_LEFT || alignment == ALIGN_RIGHT;
+                int dx = horiz ? (alignment == ALIGN_LEFT ? tab.getWidth() : -tab.getWidth()) : 0;
+                int dy = horiz ? 0: (alignment == ALIGN_TOP ? tab.getHeight() : -tab.getHeight());
+
+                Animation trans = new TranslateAnimation(-dx, 0, -dy, 0);
+                trans.setDuration(ANIM_DURATION);
+                tab.startAnimation(trans);
+                text.startAnimation(trans);
+            }
+        }
+
         void setState(int state) {
             text.setPressed(state == STATE_PRESSED);
             tab.setPressed(state == STATE_PRESSED);
@@ -230,15 +270,39 @@
         }
 
         void showTarget() {
+            AlphaAnimation alphaAnim = new AlphaAnimation(0.0f, 1.0f);
+            alphaAnim.setDuration(ANIM_TARGET_TIME);
+            target.startAnimation(alphaAnim);
             target.setVisibility(View.VISIBLE);
+            target.startAnimation(alphaAnim);
         }
 
-        void reset() {
+        void reset(boolean animate) {
             setState(STATE_NORMAL);
             text.setVisibility(View.VISIBLE);
             text.setTextAppearance(text.getContext(), R.style.TextAppearance_SlidingTabNormal);
             tab.setVisibility(View.VISIBLE);
             target.setVisibility(View.INVISIBLE);
+            final boolean horiz = alignment == ALIGN_LEFT || alignment == ALIGN_RIGHT;
+            int dx = horiz ? (alignment == ALIGN_LEFT ?  alignment_value - tab.getLeft()
+                    : alignment_value - tab.getRight()) : 0;
+            int dy = horiz ? 0 : (alignment == ALIGN_TOP ? alignment_value - tab.getTop()
+                    : alignment_value - tab.getBottom());
+            if (animate) {
+                TranslateAnimation trans = new TranslateAnimation(0, dx, 0, dy);
+                trans.setDuration(ANIM_DURATION);
+                trans.setFillAfter(false);
+                text.startAnimation(trans);
+                tab.startAnimation(trans);
+            } else {
+                if (horiz) {
+                    text.offsetLeftAndRight(dx);
+                    tab.offsetLeftAndRight(dx);
+                } else {
+                    text.offsetTopAndBottom(dy);
+                    tab.offsetTopAndBottom(dy);
+                }
+            }
         }
 
         void setTarget(int targetId) {
@@ -255,6 +319,7 @@
          * @param alignment which side to align the widget to
          */
         void layout(int l, int t, int r, int b, int alignment) {
+            this.alignment = alignment;
             final Drawable tabBackground = tab.getBackground();
             final int handleWidth = tabBackground.getIntrinsicWidth();
             final int handleHeight = tabBackground.getIntrinsicHeight();
@@ -280,11 +345,13 @@
                     text.layout(0 - parentWidth, top, 0, bottom);
                     text.setGravity(Gravity.RIGHT);
                     target.layout(leftTarget, targetTop, leftTarget + targetWidth, targetBottom);
+                    alignment_value = l;
                 } else {
                     tab.layout(parentWidth - handleWidth, top, parentWidth, bottom);
                     text.layout(parentWidth, top, parentWidth + parentWidth, bottom);
                     target.layout(rightTarget, targetTop, rightTarget + targetWidth, targetBottom);
                     text.setGravity(Gravity.TOP);
+                    alignment_value = r;
                 }
             } else {
                 // vertical
@@ -296,10 +363,12 @@
                     tab.layout(left, 0, right, handleHeight);
                     text.layout(left, 0 - parentHeight, right, 0);
                     target.layout(targetLeft, top, targetRight, top + targetHeight);
+                    alignment_value = t;
                 } else {
                     tab.layout(left, parentHeight - handleHeight, right, parentHeight);
                     text.layout(left, parentHeight, right, parentHeight + parentHeight);
                     target.layout(targetLeft, bottom, targetRight, bottom + targetHeight);
+                    alignment_value = b;
                 }
             }
         }
@@ -333,6 +402,12 @@
         public int getTabHeight() {
             return tab.getMeasuredHeight();
         }
+
+        public void startAnimation(Animation animation) {
+            tab.startAnimation(animation);
+            text.startAnimation(animation);
+            target.setVisibility(View.GONE);
+        }
     }
 
     public SlidingTab(Context context) {
@@ -345,6 +420,9 @@
     public SlidingTab(Context context, AttributeSet attrs) {
         super(context, attrs);
 
+        // Allocate a temporary once that can be used everywhere.
+        mTmpRect = new Rect();
+
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlidingTab);
         mOrientation = a.getInt(R.styleable.SlidingTab_orientation, HORIZONTAL);
         a.recycle();
@@ -401,19 +479,17 @@
         final float x = event.getX();
         final float y = event.getY();
 
-        final Rect frame = new Rect();
-
         if (mAnimating) {
             return false;
         }
 
         View leftHandle = mLeftSlider.tab;
-        leftHandle.getHitRect(frame);
-        boolean leftHit = frame.contains((int) x, (int) y);
+        leftHandle.getHitRect(mTmpRect);
+        boolean leftHit = mTmpRect.contains((int) x, (int) y);
 
         View rightHandle = mRightSlider.tab;
-        rightHandle.getHitRect(frame);
-        boolean rightHit = frame.contains((int)x, (int) y);
+        rightHandle.getHitRect(mTmpRect);
+        boolean rightHit = mTmpRect.contains((int)x, (int) y);
 
         if (!mTracking && !(leftHit || rightHit)) {
             return false;
@@ -451,40 +527,31 @@
             final int action = event.getAction();
             final float x = event.getX();
             final float y = event.getY();
-            final View handle = mCurrentSlider.tab;
+
             switch (action) {
                 case MotionEvent.ACTION_MOVE:
-                    moveHandle(x, y);
-                    float position = isHorizontal() ? x : y;
-                    float target = mThreshold * (isHorizontal() ? getWidth() : getHeight());
-                    boolean thresholdReached;
-                    if (isHorizontal()) {
-                        thresholdReached = mCurrentSlider == mLeftSlider ?
-                                position > target : position < target;
-                    } else {
-                        thresholdReached = mCurrentSlider == mLeftSlider ?
-                                position < target : position > target;
-                    }
-                    if (!mTriggered && thresholdReached) {
-                        mTriggered = true;
-                        mTracking = false;
-                        mCurrentSlider.setState(Slider.STATE_ACTIVE);
-                        dispatchTriggerEvent(mCurrentSlider == mLeftSlider ?
-                            OnTriggerListener.LEFT_HANDLE : OnTriggerListener.RIGHT_HANDLE);
+                    if (withinView(x, y, this) ) {
+                        moveHandle(x, y);
+                        float position = isHorizontal() ? x : y;
+                        float target = mThreshold * (isHorizontal() ? getWidth() : getHeight());
+                        boolean thresholdReached;
+                        if (isHorizontal()) {
+                            thresholdReached = mCurrentSlider == mLeftSlider ?
+                                    position > target : position < target;
+                        } else {
+                            thresholdReached = mCurrentSlider == mLeftSlider ?
+                                    position < target : position > target;
+                        }
+                        if (!mTriggered && thresholdReached) {
+                            mTriggered = true;
+                            mTracking = false;
+                            mCurrentSlider.setState(Slider.STATE_ACTIVE);
+                            dispatchTriggerEvent(mCurrentSlider == mLeftSlider ?
+                                OnTriggerListener.LEFT_HANDLE : OnTriggerListener.RIGHT_HANDLE);
 
-                        // TODO: This is a place holder for the real animation. It just holds
-                        // the screen for the duration of the animation for now.
-                        mAnimating = true;
-                        mHandler.postDelayed(new Runnable() {
-                            public void run() {
-                                resetView();
-                                mAnimating = false;
-                            }
-                        }, ANIMATION_DURATION);
-                    }
-
-                    if (isHorizontal() && (y <= handle.getBottom() && y >= handle.getTop()) ||
-                            !isHorizontal() && (x >= handle.getLeft() && x <= handle.getRight()) ) {
+                            startAnimating();
+                            setGrabbedState(OnTriggerListener.NO_HANDLE);
+                        }
                         break;
                     }
                     // Intentionally fall through - we're outside tracking rectangle
@@ -493,7 +560,10 @@
                 case MotionEvent.ACTION_CANCEL:
                     mTracking = false;
                     mTriggered = false;
-                    resetView();
+                    mOtherSlider.show(true);
+                    mCurrentSlider.reset(false);
+                    mCurrentSlider = null;
+                    mOtherSlider = null;
                     setGrabbedState(OnTriggerListener.NO_HANDLE);
                     break;
             }
@@ -502,14 +572,68 @@
         return mTracking || super.onTouchEvent(event);
     }
 
+    void startAnimating() {
+        mAnimating = true;
+        final Animation appear = new AlphaAnimation(0.5f, 1.0f); appear.setDuration(ANIM_DURATION);
+        final Animation trans;
+        Slider slider = mCurrentSlider;
+        int dx;
+        int dy;
+        if (isHorizontal()) {
+            int right = slider.tab.getRight();
+            int width = slider.tab.getWidth();
+            int left = slider.tab.getLeft();
+            int viewWidth = getWidth();
+            dx =  slider == mRightSlider ? - (right + viewWidth - width)
+                    : (viewWidth - left) + viewWidth - width;
+            dy = 0;
+        } else {
+            int top = slider.tab.getTop();
+            int bottom = slider.tab.getBottom();
+            int height = slider.tab.getHeight();
+            int viewHeight = getHeight();
+            dx = 0;
+            dy =  slider == mRightSlider ? (top + viewHeight - height)
+                    : - ((viewHeight - bottom) + viewHeight - height);
+        }
+        trans = new TranslateAnimation(0, dx, 0, dy);
+        trans.setDuration(ANIM_DURATION);
+        trans.setInterpolator(new LinearInterpolator());
+
+        trans.setAnimationListener(new AnimationListener() {
+            public void onAnimationEnd(Animation animation) {
+                resetView();
+                mLeftSlider.startAnimation(appear);
+                mRightSlider.startAnimation(appear);
+                mAnimating = false;
+            }
+
+            public void onAnimationRepeat(Animation animation) {
+
+            }
+
+            public void onAnimationStart(Animation animation) {
+
+            }
+
+        });
+
+        slider.startAnimation(trans);
+    }
+
+    private boolean withinView(final float x, final float y, final View view) {
+        return isHorizontal() && y > - TRACKING_MARGIN && y < TRACKING_MARGIN + view.getHeight()
+            || !isHorizontal() && x > -TRACKING_MARGIN && x < TRACKING_MARGIN + view.getWidth();
+    }
+
     private boolean isHorizontal() {
         return mOrientation == HORIZONTAL;
     }
 
     private void resetView() {
-        mLeftSlider.reset();
-        mRightSlider.reset();
-        onLayout(true, getLeft(), getTop(), getLeft() + getWidth(), getTop() + getHeight());
+        mLeftSlider.reset(false);
+        mRightSlider.reset(false);
+        // onLayout(true, getLeft(), getTop(), getLeft() + getWidth(), getTop() + getHeight());
     }
 
     @Override
@@ -642,22 +766,6 @@
         }
     }
 
-    private class SlidingTabHandler extends Handler {
-        public void handleMessage(Message m) {
-            switch (m.what) {
-                case MSG_ANIMATE:
-                    doAnimation();
-                    break;
-            }
-        }
-    }
-
-    private void doAnimation() {
-        if (mAnimating) {
-
-        }
-    }
-
     private void log(String msg) {
         Log.d(LOG_TAG, msg);
     }
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml
index 1991e98..fdb9ad5 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock.xml
@@ -50,7 +50,7 @@
             android:layout_height="wrap_content"
             android:layout_below="@id/carrier"
             android:layout_marginBottom="8dip"
-            android:layout_marginTop="16dip"
+            android:layout_marginTop="60dip"
             android:layout_marginLeft="24dip"
             >
 
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index 2856794..bc7f9a9 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -39,18 +39,16 @@
             android:layout_height="wrap_content"
             android:layout_alignParentTop="true"
             android:layout_alignParentRight="true"
-            android:layout_marginTop="16dip"
-            android:layout_marginRight="16dip"
+            android:layout_marginRight="8dip"
             android:textAppearance="?android:attr/textAppearanceMedium"
             />
 
         <com.android.internal.widget.DigitalClock android:id="@+id/time"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_alignParentTop="true"
+            android:layout_below="@id/carrier"
             android:layout_alignParentLeft="true"
             android:layout_marginBottom="8dip"
-            android:layout_marginTop="16dip"
             android:layout_marginLeft="24dip"
             >
 
@@ -89,7 +87,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_below="@id/time"
-            android:layout_marginLeft="24dip"
+            android:layout_marginLeft="16dip"
             android:textAppearance="?android:attr/textAppearanceMedium"
             />
     
@@ -112,7 +110,7 @@
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="3dip"
-        android:layout_marginLeft="24dip"
+        android:layout_marginLeft="16dip"
         android:gravity="left"
         >
         <TextView
diff --git a/docs/html/guide/publishing/publishing.jd b/docs/html/guide/publishing/publishing.jd
index 3aea3cf..c027f4d 100644
--- a/docs/html/guide/publishing/publishing.jd
+++ b/docs/html/guide/publishing/publishing.jd
@@ -18,7 +18,7 @@
 <ol>
 <li><a href="#overview">Publishing on Android Market</a>
     <ol>
-    <li><a href="#marketupgrade">Publishing Upgrades on Android Market</a>
+    <li><a href="#marketupgrade">Publishing Updates on Android Market</a>
     <li><a href="#marketintent">Using Intents to Launch the Market Application</a></li>
     </ol></li>
 <!--
@@ -89,9 +89,11 @@
 <li>Your application must be signed with a cryptographic private key whose
 validity period ends after <span style="color:red">22 October 2033</span>. </li>
 <li>Your application must define both an <code>android:versionCode</code> and an
-<code>android:versionName</code> attribute in the <code>&lt;manifest&gt;</code>
+<code>android:versionName</code> attribute in the
+<a
+href="{@docRoot}guide/topics/manifest/manifest-element.html"><code>&lt;manifest&gt;</code></a>
 element of its manifest. The server uses the <code>android:versionCode</code> as
-the basis for identifying the application internally and handling upgrades, and
+the basis for identifying the application internally and handling updates, and
 it displays the <code>android:versionName</code> to users as the application's
 version.</li>
 <li>Your application must define both an <code>android:icon</code> and an
@@ -100,58 +102,24 @@
 </ol>
 </div>
 
-<h3 id="marketupgrade">Publishing Upgrades on Android Market</h3>
+<h3 id="marketupgrade">Publishing Updates on Android Market</h3>
 
-<p>The beta version of Android Market does not support notifying your users when
-you publish a new version of your application. This capability will be
-added soon, but currently the user must independently initiate download of an
-upgraded application. When you publish an upgrade, you can assist users by
-notifying them that the upgrade is available and giving them a way to download
-the upgraded application from Android Market.</p>
+<p>At any time after publishing an application on Android Market, you can upload
+and publish an update to the same application package. When you publish an
+update to an application, users who have already installed the
+application will automatically receive a notification that an update is
+available for the application. They can then choose to update the application
+to the latest version.</p>
 
-<p>Here is a suggested way of tracking installed application versions and
-notifying users that an upgrade is available:</p>
+<p>Before uploading the updated application, be sure that you have incremented
+the <code>android:versionCode</code> and <code>android:versionName</code>
+attributes in the <a
+href="{@docRoot}guide/topics/manifest/manifest-element.html"><code>&lt;manifest&gt;</code></a>
+element of the manifest file. Also, the package name must be the same and the
+.apk must be signed with the same private key. If the package name and signing
+certificate do <em>not</em> match those of the existing version, Market will
+consider it a new application and will not offer it to users as an update.</p>
 
-<ol>
-<li>Have your app occasionally check in with a web-service that you're
-running. This web service should return two values: the latest available
-version number for the application (corresponding to
-<code>android:versionCode</code>) and a URI string that your application
-can later send in an Intent, to launch Market and search for the
-upgraded application for the user. 
-
-<p>The URI that your web service returns should be properly formatted to 
-search Android Market for your upgraded application. See 
-<a href="#marketintent">Using Intents to Launch the Market Application</a> 
-for more information. The URI should specify the upgraded 
-application's package name as the query parameter, since the package name
-is guaranteed to be unique on Android Market. The URI format for the 
-package name search is: </p>
-
-<p><code>http://market.android.com/search?q=pname:&lt;package&gt;</code> or 
-<br><code>market://search?q=pname:&lt;package&gt;</code></p>
-
-</li>
-<li>Your application can then compare its own version number against
-that retrieved.  If the retrieved value is greater, your application can
-show a dialog informing the user that a new version is available. The
-dialog can offer buttons to begin the download or cancel. </li> <li>If
-the user clicks the button to begin the download, your application can
-call startActivity() using the ACTION_VIEW Intent, passing the URI
-received from your web service. The Intent launches the Market
-application on the device and initiates an immediate search on the
-Android Market site, based on the query parameters in the URI. When the
-result is displayed, the user can view the details of the upgraded
-application and begin the download. 
-
-<p>Note that, because the URI string is received from your web 
-service and not hard-coded into your application, you can easily change
-the Market launch behaviors whenever needed, without 
-having to change your application. </p></li></ol>
-
-<p>For more information about URIs you can pass to the Market application at
-launch, see <a href="#marketintent">Using Intents to Launch the Market
-Application</a>, below.</p>
 
 <h3 id="marketintent">Using Intents to Launch the Market Application on 
 a Device</h3>
@@ -163,7 +131,7 @@
 
 <p>You can launch the Market application from another Android
 application by sending an Intent to the system. You might want to do
-this, for example, to help the user locate and download an upgrade to an
+this, for example, to help the user locate and download an update to an
 installed application, or to let the user know about related
 applications that are available for download. </p>
 
diff --git a/preloaded-classes b/preloaded-classes
index c4b1c66..fc7b1e3 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -1142,8 +1142,6 @@
 com.google.common.io.android.AndroidPersistentStore
 com.google.common.io.android.GoogleHttpClient
 com.google.common.io.android.J2SeTcpConnectionFactory
-com.google.common.io.protocol.ProtoBuf
-com.google.common.io.protocol.ProtoBufType
 com.google.common.lang.ThreadFactory
 com.google.common.task.AbstractTask
 com.google.common.task.Task
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index e1425d4..bde03f3 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -185,6 +185,7 @@
     private UnsynchronizedWakeLock mStayOnWhilePluggedInScreenDimLock;
     private UnsynchronizedWakeLock mStayOnWhilePluggedInPartialLock;
     private UnsynchronizedWakeLock mPreventScreenOnPartialLock;
+    private UnsynchronizedWakeLock mProximityPartialLock;
     private HandlerThread mHandlerThread;
     private Handler mHandler;
     private TimeoutTask mTimeoutTask = new TimeoutTask();
@@ -283,6 +284,7 @@
         IBinder mToken;
         int mCount = 0;
         boolean mRefCounted;
+        boolean mHeld;
 
         UnsynchronizedWakeLock(int flags, String tag, boolean refCounted) {
             mFlags = flags;
@@ -297,6 +299,7 @@
                 try {
                     PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
                             MY_UID, mTag);
+                    mHeld = true;
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -306,15 +309,21 @@
         public void release() {
             if (!mRefCounted || --mCount == 0) {
                 PowerManagerService.this.releaseWakeLockLocked(mToken, false);
+                mHeld = false;
             }
             if (mCount < 0) {
                 throw new RuntimeException("WakeLock under-locked " + mTag);
             }
         }
 
+        public boolean isHeld()
+        {
+            return mHeld;
+        }
+
         public String toString() {
             return "UnsynchronizedWakeLock(mFlags=0x" + Integer.toHexString(mFlags)
-                    + " mCount=" + mCount + ")";
+                    + " mCount=" + mCount + " mHeld=" + mHeld + ")";
         }
     }
 
@@ -443,6 +452,8 @@
                                 PowerManager.PARTIAL_WAKE_LOCK, "StayOnWhilePluggedIn Partial", false);
         mPreventScreenOnPartialLock = new UnsynchronizedWakeLock(
                                 PowerManager.PARTIAL_WAKE_LOCK, "PreventScreenOn Partial", false);
+        mProximityPartialLock = new UnsynchronizedWakeLock(
+                                PowerManager.PARTIAL_WAKE_LOCK, "Proximity Partial", false);
 
         mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
         mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -907,6 +918,7 @@
         pw.println("  mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock);
         pw.println("  mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock);
         pw.println("  mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock);
+        pw.println("  mProximityPartialLock=" + mProximityPartialLock);
         pw.println("  mProximityWakeLockCount=" + mProximityWakeLockCount);
         pw.println("  mProximitySensorEnabled=" + mProximitySensorEnabled);
         pw.println("  mProximitySensorActive=" + mProximitySensorActive);
@@ -1254,7 +1266,7 @@
                 // Forcibly turn on the screen if it's supposed to be on.  (This
                 // handles the case where the screen is currently off because of
                 // a prior preventScreenOn(true) call.)
-                if ((mPowerState & SCREEN_ON_BIT) != 0) {
+                if (!mProximitySensorActive && (mPowerState & SCREEN_ON_BIT) != 0) {
                     if (mSpew) {
                         Log.d(TAG,
                               "preventScreenOn: turning on after a prior preventScreenOn(true)!");
@@ -1835,8 +1847,10 @@
     }
 
     private void forceUserActivityLocked() {
-        // cancel animation so userActivity will succeed
-        mScreenBrightness.animating = false;
+        if (isScreenTurningOffLocked()) {
+            // cancel animation so userActivity will succeed
+            mScreenBrightness.animating = false;
+        }
         boolean savedActivityAllowed = mUserActivityAllowed;
         mUserActivityAllowed = true;
         userActivity(SystemClock.uptimeMillis(), false);
@@ -1951,6 +1965,9 @@
                     proximityChangedLocked(mProximityPendingValue == 1);
                     mProximityPendingValue = -1;
                 }
+                if (mProximityPartialLock.isHeld()) {
+                    mProximityPartialLock.release();
+                }
             }
         }
     };
@@ -2412,6 +2429,9 @@
             try {
                 mSensorManager.unregisterListener(mProximityListener);
                 mHandler.removeCallbacks(mProximityTask);
+                if (mProximityPartialLock.isHeld()) {
+                    mProximityPartialLock.release();
+                }
                 mProximitySensorEnabled = false;
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -2478,6 +2498,7 @@
                 long timeSinceLastEvent = milliseconds - mLastProximityEventTime;
                 mLastProximityEventTime = milliseconds;
                 mHandler.removeCallbacks(mProximityTask);
+                boolean proximityTaskQueued = false;
 
                 // compare against getMaximumRange to support sensors that only return 0 or 1
                 boolean active = (distance >= 0.0 && distance < PROXIMITY_THRESHOLD &&
@@ -2490,11 +2511,21 @@
                     // enforce delaying atleast PROXIMITY_SENSOR_DELAY before processing
                     mProximityPendingValue = (active ? 1 : 0);
                     mHandler.postDelayed(mProximityTask, PROXIMITY_SENSOR_DELAY - timeSinceLastEvent);
+                    proximityTaskQueued = true;
                 } else {
                     // process the value immediately
                     mProximityPendingValue = -1;
                     proximityChangedLocked(active);
                 }
+
+                // update mProximityPartialLock state
+                boolean held = mProximityPartialLock.isHeld();
+                if (!held && proximityTaskQueued) {
+                    // hold wakelock until mProximityTask runs
+                    mProximityPartialLock.acquire();
+                } else if (held && !proximityTaskQueued) {
+                    mProximityPartialLock.release();
+                }
             }
         }