Merge change 24998 into eclair

* changes:
  Add Panasonic KX-TH112 to blacklist.
diff --git a/camera/libcameraservice/CameraHardwareStub.cpp b/camera/libcameraservice/CameraHardwareStub.cpp
index 24496bb..35f4846 100644
--- a/camera/libcameraservice/CameraHardwareStub.cpp
+++ b/camera/libcameraservice/CameraHardwareStub.cpp
@@ -265,6 +265,11 @@
     return NO_ERROR;
 }
 
+status_t CameraHardwareStub::cancelAutoFocus()
+{
+    return NO_ERROR;
+}
+
 /*static*/ int CameraHardwareStub::beginPictureThread(void *cookie)
 {
     CameraHardwareStub *c = (CameraHardwareStub *)cookie;
diff --git a/camera/libcameraservice/CameraHardwareStub.h b/camera/libcameraservice/CameraHardwareStub.h
index 000906a..f957fa8 100644
--- a/camera/libcameraservice/CameraHardwareStub.h
+++ b/camera/libcameraservice/CameraHardwareStub.h
@@ -51,6 +51,7 @@
     virtual void        releaseRecordingFrame(const sp<IMemory>& mem);
 
     virtual status_t    autoFocus();
+    virtual status_t    cancelAutoFocus();
     virtual status_t    takePicture();
     virtual status_t    cancelPicture();
     virtual status_t    dump(int fd, const Vector<String16>& args) const;
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index f425f6b..bab7d08 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -798,7 +798,6 @@
 }
 #endif
 
-// take a picture - image is returned in callback
 status_t CameraService::Client::autoFocus()
 {
     LOGD("autoFocus (pid %d)", getCallingPid());
@@ -815,6 +814,22 @@
     return mHardware->autoFocus();
 }
 
+status_t CameraService::Client::cancelAutoFocus()
+{
+    LOGD("cancelAutoFocus (pid %d)", getCallingPid());
+
+    Mutex::Autolock lock(mLock);
+    status_t result = checkPid();
+    if (result != NO_ERROR) return result;
+
+    if (mHardware == 0) {
+        LOGE("mHardware is NULL, returning.");
+        return INVALID_OPERATION;
+    }
+
+    return mHardware->cancelAutoFocus();
+}
+
 // take a picture - image is returned in callback
 status_t CameraService::Client::takePicture()
 {
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index f8c7216..0a909cf 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -110,6 +110,9 @@
         // auto focus
         virtual status_t        autoFocus();
 
+        // cancel auto focus
+        virtual status_t        cancelAutoFocus();
+
         // take a picture - returns an IMemory (ref-counted mmap)
         virtual status_t        takePicture();
 
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index be243a5..545db17 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -34,6 +34,7 @@
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -1752,8 +1753,16 @@
      * 
      * <p>If the focused view didn't want this event, this method is called.
      *
-     * <p>The default implementation sets up state to call
-     * {@link #onKeyLongPress}, and does other default key handling
+     * <p>The default implementation takes care of {@link KeyEvent#KEYCODE_BACK}
+     * by calling {@link #onBackPressed()}, though the behavior varies based
+     * on the application compatibility mode: for
+     * {@link android.os.Build.VERSION_CODES#ECLAIR} or later applications,
+     * it will set up the dispatch to call {@link #onKeyUp} where the action
+     * will be performed; for earlier applications, it will perform the
+     * action immediately in on-down, as those versions of the platform
+     * behaved.
+     * 
+     * <p>Other additional default key handling may be performed
      * if configured with {@link #setDefaultKeyMode}.
      * 
      * @return Return <code>true</code> to prevent this event from being propagated
@@ -1764,7 +1773,12 @@
      */
     public boolean onKeyDown(int keyCode, KeyEvent event)  {
         if (keyCode == KeyEvent.KEYCODE_BACK) {
-            event.startTracking();
+            if (getApplicationInfo().targetSdkVersion
+                    >= Build.VERSION_CODES.ECLAIR) {
+                event.startTracking();
+            } else {
+                onBackPressed();
+            }
             return true;
         }
         
@@ -1841,10 +1855,13 @@
      * @see KeyEvent
      */
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
-                && !event.isCanceled()) {
-            onBackPressed();
-            return true;
+        if (getApplicationInfo().targetSdkVersion
+                >= Build.VERSION_CODES.ECLAIR) {
+            if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
+                    && !event.isCanceled()) {
+                onBackPressed();
+                return true;
+            }
         }
         return false;
     }
@@ -2016,11 +2033,14 @@
      */
     public boolean dispatchKeyEvent(KeyEvent event) {
         onUserInteraction();
-        if (getWindow().superDispatchKeyEvent(event)) {
+        Window win = getWindow();
+        if (win.superDispatchKeyEvent(event)) {
             return true;
         }
-        return event.dispatch(this, mDecor != null
-                ? mDecor.getKeyDispatcherState() : null, this);
+        View decor = mDecor;
+        if (decor == null) decor = win.getDecorView();
+        return event.dispatch(this, decor != null
+                ? decor.getKeyDispatcherState() : null, this);
     }
 
     /**
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 62dc9b2..75a90c4 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -51,7 +51,6 @@
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.KeyEvent;
-import android.view.Menu;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -1684,7 +1683,6 @@
     public static class SearchAutoComplete extends AutoCompleteTextView {
 
         private int mThreshold;
-        private int mLastKeyDown;
         private SearchDialog mSearchDialog;
         
         public SearchAutoComplete(Context context) {
@@ -1765,26 +1763,26 @@
          */
         @Override
         public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-            mLastKeyDown = keyCode;
             if (mSearchDialog.mSearchable == null) {
                 return false;
             }
             if (keyCode == KeyEvent.KEYCODE_BACK) {
                 if (event.getAction() == KeyEvent.ACTION_DOWN
                         && event.getRepeatCount() == 0) {
-                    // We releae the back key, might we want to do
+                    // We release the back key, might we want to do
                     // something before the IME?
                     if (mSearchDialog.backToPreviousComponent(false)) {
+                        getKeyDispatcherState().startTracking(event, this);
                         return true;
                     }
                     if (isInputMethodNotNeeded() ||
                             (isEmpty() && getDropDownChildCount() >= getAdapterCount())) {
+                        getKeyDispatcherState().startTracking(event, this);
                         return true;
                     }
-                    mLastKeyDown = 0;
                     return false; // will dismiss soft keyboard if necessary
                 } else if (event.getAction() == KeyEvent.ACTION_UP
-                        && mLastKeyDown == keyCode && !event.isCanceled()) {
+                        && event.isTracking() && !event.isCanceled()) {
                     if (mSearchDialog.backToPreviousComponent(true)) {
                         return true;
                     }
@@ -1815,8 +1813,10 @@
     
     protected boolean handleBackKey(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_BACK) {
-            if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            if (event.getAction() == KeyEvent.ACTION_DOWN
+                    && event.getRepeatCount() == 0) {
                 // Consume the event, to get an up at which point we execute.
+                event.startTracking();
                 return true;
             }
             if (event.getAction() == KeyEvent.ACTION_UP && event.isTracking()
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 04daa1c..9991600 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -383,6 +383,20 @@
     private native final void native_autoFocus();
 
     /**
+     * Cancels auto-focus function. If the auto-focus is still in progress,
+     * this function will cancel it. Whether the auto-focus is in progress
+     * or not, this function will return the focus position to the default.
+     * If the camera does not support auto-focus, this is a no-op.
+     * @hide
+     */
+    public final void cancelAutoFocus()
+    {
+        mAutoFocusCallback = null;
+        native_cancelAutoFocus();
+    }
+    private native final void native_cancelAutoFocus();
+
+    /**
      * An interface which contains a callback for the shutter closing after taking a picture.
      */
     public interface ShutterCallback
@@ -1338,5 +1352,3 @@
         }
     };
 }
-
-
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 6c2a27a..b4778fe 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -143,6 +143,9 @@
          * Service.onStartCommand} function will return the new
          * {@link android.app.Service#START_STICKY} behavior instead of the
          * old compatibility {@link android.app.Service#START_STICKY_COMPATIBILITY}.
+         * <li> The {@link android.app.Activity} class will now execute back
+         * key presses on the key up instead of key down, to be able to detect
+         * canceled presses from virtual keys.
          * </ul>
          */
         public static final int ECLAIR = CUR_DEVELOPMENT;
diff --git a/core/java/android/os/IHardwareService.aidl b/core/java/android/os/IHardwareService.aidl
index aebcb3c..a6ef6473 100755
--- a/core/java/android/os/IHardwareService.aidl
+++ b/core/java/android/os/IHardwareService.aidl
@@ -32,6 +32,9 @@
     // sets the brightness of the backlights (screen, keyboard, button) 0-255
     void setBacklights(int brightness);
 
+    // enables or disables automatic brightness mode
+    void setAutoBrightness(boolean on);
+
     // for the phone
     void setAttentionLight(boolean on);
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 688f377..0bbd1fc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1061,6 +1061,12 @@
         public static final String SCREEN_BRIGHTNESS = "screen_brightness";
 
         /**
+         * Control whether to enable automatic brightness mode.
+         * @hide
+         */
+        public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
+
+        /**
          * Control whether the process CPU usage meter should be shown.
          */
         public static final String SHOW_PROCESSES = "show_processes";
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index daa4b29..d4f9787 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 import android.util.SparseIntArray;
 import android.view.KeyCharacterMap;
 import android.view.KeyCharacterMap.KeyData;
@@ -319,6 +320,9 @@
         return KeyCharacterMap.getDeadChar(accent, c);
     }
     
+    static final boolean DEBUG = false;
+    static final String TAG = "KeyEvent";
+    
     private int mMetaState;
     private int mAction;
     private int mKeyCode;
@@ -1028,13 +1032,17 @@
         switch (mAction) {
             case ACTION_DOWN: {
                 mFlags &= ~FLAG_START_TRACKING;
+                if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
+                        + ": " + this);
                 boolean res = receiver.onKeyDown(mKeyCode, this);
                 if (state != null) {
                     if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
+                        if (DEBUG) Log.v(TAG, "  Start tracking!");
                         state.startTracking(this, target);
                     } else if (isLongPress() && state.isTracking(this)) {
                         try {
                             if (receiver.onKeyLongPress(mKeyCode, this)) {
+                                if (DEBUG) Log.v(TAG, "  Clear from long press!");
                                 state.performedLongPress(this);
                                 res = true;
                             }
@@ -1045,6 +1053,8 @@
                 return res;
             }
             case ACTION_UP:
+                if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
+                        + ": " + this);
                 if (state != null) {
                     state.handleUpEvent(this);
                 }
@@ -1085,6 +1095,7 @@
          * Reset back to initial state.
          */
         public void reset() {
+            if (DEBUG) Log.v(TAG, "Reset: " + this);
             mDownKeyCode = 0;
             mDownTarget = null;
             mActiveLongPresses.clear();
@@ -1095,6 +1106,7 @@
          */
         public void reset(Object target) {
             if (mDownTarget == target) {
+                if (DEBUG) Log.v(TAG, "Reset in " + target + ": " + this);
                 mDownKeyCode = 0;
                 mDownTarget = null;
             }
@@ -1115,6 +1127,7 @@
                 throw new IllegalArgumentException(
                         "Can only start tracking on a down event");
             }
+            if (DEBUG) Log.v(TAG, "Start trackingt in " + target + ": " + this);
             mDownKeyCode = event.getKeyCode();
             mDownTarget = target;
         }
@@ -1145,12 +1158,15 @@
          */
         public void handleUpEvent(KeyEvent event) {
             final int keyCode = event.getKeyCode();
+            if (DEBUG) Log.v(TAG, "Handle key up " + event + ": " + this);
             int index = mActiveLongPresses.indexOfKey(keyCode);
             if (index >= 0) {
+                if (DEBUG) Log.v(TAG, "  Index: " + index);
                 event.mFlags |= FLAG_CANCELED | FLAG_CANCELED_LONG_PRESS;
                 mActiveLongPresses.removeAt(index);
             }
             if (mDownKeyCode == keyCode) {
+                if (DEBUG) Log.v(TAG, "  Tracking!");
                 event.mFlags |= FLAG_TRACKING;
                 mDownKeyCode = 0;
                 mDownTarget = null;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index bed2a7a..b242b58 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2946,7 +2946,9 @@
             break;
         case KeyEvent.KEYCODE_BACK:
             if (mFiltered && mPopup != null && mPopup.isShowing()) {
-                if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                if (event.getAction() == KeyEvent.ACTION_DOWN
+                        && event.getRepeatCount() == 0) {
+                    getKeyDispatcherState().startTracking(event, this);
                     handled = true;
                 } else if (event.getAction() == KeyEvent.ACTION_UP
                         && event.isTracking() && !event.isCanceled()) {
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 7891d3c..4566c4c 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -132,8 +132,6 @@
 
     private AutoCompleteTextView.PassThroughClickListener mPassThroughClickListener;
 
-    private int mDownKeyCode;
-    
     public AutoCompleteTextView(Context context) {
         this(context, null);
     }
@@ -605,19 +603,18 @@
 
     @Override
     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        if (event.getAction() == KeyEvent.ACTION_DOWN
-                && event.getRepeatCount() == 0) {
-            mDownKeyCode = keyCode;
-        }
-        if (isPopupShowing()) {
+        if (keyCode == KeyEvent.KEYCODE_BACK && isPopupShowing()
+                && !mDropDownAlwaysVisible) {
             // special case for the back key, we do not even try to send it
             // to the drop down list but instead, consume it immediately
-            if (keyCode == KeyEvent.KEYCODE_BACK && !mDropDownAlwaysVisible) {
-                if (event.getAction() == KeyEvent.ACTION_UP
-                        && mDownKeyCode == keyCode && !event.isCanceled()) {
-                    dismissDropDown();
-                    return true;
-                }
+            if (event.getAction() == KeyEvent.ACTION_DOWN
+                    && event.getRepeatCount() == 0) {
+                getKeyDispatcherState().startTracking(event, this);
+                return true;
+            } else if (event.getAction() == KeyEvent.ACTION_UP
+                    && event.isTracking() && !event.isCanceled()) {
+                dismissDropDown();
+                return true;
             }
         }
         return super.onKeyPreIme(keyCode, event);
@@ -1026,7 +1023,6 @@
     @Override
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         super.onWindowFocusChanged(hasWindowFocus);
-        mDownKeyCode = 0;
         performValidation();
         if (!hasWindowFocus && !mDropDownAlwaysVisible) {
             dismissDropDown();
@@ -1036,7 +1032,6 @@
     @Override
     protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
         super.onFocusChanged(focused, direction, previouslyFocusedRect);
-        mDownKeyCode = 0;
         performValidation();
         if (!focused && !mDropDownAlwaysVisible) {
             dismissDropDown();
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 548dee9..d86b674 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1323,8 +1323,16 @@
         @Override
         public boolean dispatchKeyEvent(KeyEvent event) {
             if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
-                dismiss();
-                return true;
+                if (event.getAction() == KeyEvent.ACTION_DOWN
+                        && event.getRepeatCount() == 0) {
+                    getKeyDispatcherState().startTracking(event, this);
+                    return true;
+                } else if (event.getAction() == KeyEvent.ACTION_UP
+                        && event.isTracking() && !event.isCanceled()) {
+                    dismiss();
+                    return true;
+                }
+                return super.dispatchKeyEvent(event);
             } else {
                 return super.dispatchKeyEvent(event);
             }
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index a41e2e3..760b8ff 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -476,7 +476,21 @@
         if (isInterestingKey(keyCode)) {
 
             if (keyCode == KeyEvent.KEYCODE_BACK) {
-                setVisible(false);
+                if (event.getAction() == KeyEvent.ACTION_DOWN
+                        && event.getRepeatCount() == 0) {
+                    if (mOwnerView != null) {
+                        KeyEvent.DispatcherState ds = mOwnerView.getKeyDispatcherState();
+                        if (ds != null) {
+                            ds.startTracking(event, this);
+                        }
+                    }
+                    return true;
+                } else if (event.getAction() == KeyEvent.ACTION_UP
+                        && event.isTracking() && !event.isCanceled()) {
+                    setVisible(false);
+                    return true;
+                }
+                
             } else {
                 dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
             }
diff --git a/core/java/com/android/internal/view/menu/MenuDialogHelper.java b/core/java/com/android/internal/view/menu/MenuDialogHelper.java
index 88f7b2f..70f040a 100644
--- a/core/java/com/android/internal/view/menu/MenuDialogHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuDialogHelper.java
@@ -22,6 +22,7 @@
 import android.os.IBinder;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.Window;
 import android.view.WindowManager;
 import android.widget.ListAdapter;
 
@@ -86,18 +87,26 @@
     }
     
     public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
-        /*
-         * Close menu on key down (more responsive, and there's no way to cancel
-         * a key press so no point having it on key up. Note: This is also
-         * needed because when a top-level menu item that shows a submenu is
-         * invoked by chording, this onKey method will be called with the menu
-         * up event.
-         */
-        if (event.getAction() == KeyEvent.ACTION_DOWN && (keyCode == KeyEvent.KEYCODE_MENU)
-                || (keyCode == KeyEvent.KEYCODE_BACK)) {
-            mMenu.close(true);
-            dialog.dismiss();
-            return true;
+        if (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BACK) {
+            if (event.getAction() == KeyEvent.ACTION_DOWN
+                    && event.getRepeatCount() == 0) {
+                Window win = mDialog.getWindow();
+                if (win != null) {
+                    View decor = win.getDecorView();
+                    if (decor != null) {
+                        KeyEvent.DispatcherState ds = decor.getKeyDispatcherState();
+                        if (ds != null) {
+                            ds.startTracking(event, this);
+                            return true;
+                        }
+                    }
+                }
+            } else if (event.getAction() == KeyEvent.ACTION_UP
+                    && event.isTracking() && !event.isCanceled()) {
+                mMenu.close(true);
+                dialog.dismiss();
+                return true;
+            }
         }
 
         // Menu shortcut matching
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 8a312d9..ce2b10c 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -327,6 +327,18 @@
     }
 }
 
+static void android_hardware_Camera_cancelAutoFocus(JNIEnv *env, jobject thiz)
+{
+    LOGV("cancelAutoFocus");
+    JNICameraContext* context;
+    sp<Camera> c = get_native_camera(env, thiz, &context);
+    if (c == 0) return;
+
+    if (c->cancelAutoFocus() != NO_ERROR) {
+        jniThrowException(env, "java/lang/RuntimeException", "cancelAutoFocus failed");
+    }
+}
+
 static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz)
 {
     LOGV("takePicture");
@@ -422,6 +434,9 @@
   { "native_autoFocus",
     "()V",
     (void *)android_hardware_Camera_autoFocus },
+  { "native_cancelAutoFocus",
+    "()V",
+    (void *)android_hardware_Camera_cancelAutoFocus },
   { "native_takePicture",
     "()V",
     (void *)android_hardware_Camera_takePicture },
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index abb575c..117e139 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -81,4 +81,6 @@
         <item>30</item>
     </integer-array>
     
+    <!-- Flag indicating whether the device supports automatic brightness mode. -->
+    <bool name="config_automatic_brightness_available">false</bool>
 </resources>
diff --git a/include/ui/Camera.h b/include/ui/Camera.h
index ae6e255..9ceb8fd 100644
--- a/include/ui/Camera.h
+++ b/include/ui/Camera.h
@@ -143,6 +143,9 @@
             // autoFocus - status returned from callback
             status_t    autoFocus();
 
+            // cancel auto focus
+            status_t    cancelAutoFocus();
+
             // take a picture - picture returned from callback
             status_t    takePicture();
 
diff --git a/include/ui/CameraHardwareInterface.h b/include/ui/CameraHardwareInterface.h
index 535f70e..5fbb7d8 100644
--- a/include/ui/CameraHardwareInterface.h
+++ b/include/ui/CameraHardwareInterface.h
@@ -161,6 +161,14 @@
     virtual status_t    autoFocus() = 0;
 
     /**
+     * Cancels auto-focus function. If the auto-focus is still in progress,
+     * this function will cancel it. Whether the auto-focus is in progress
+     * or not, this function will return the focus position to the default.
+     * If the camera does not support auto-focus, this is a no-op.
+     */
+    virtual status_t    cancelAutoFocus() = 0;
+
+    /**
      * Take a picture.
      */
     virtual status_t    takePicture() = 0;
diff --git a/include/ui/ICamera.h b/include/ui/ICamera.h
index 1df7914..7595e36 100644
--- a/include/ui/ICamera.h
+++ b/include/ui/ICamera.h
@@ -76,6 +76,9 @@
     // auto focus
     virtual status_t        autoFocus() = 0;
 
+    // cancel auto focus
+    virtual status_t        cancelAutoFocus() = 0;
+
     // take a picture
     virtual status_t        takePicture() = 0;
 
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 790a655..4f7500f 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -677,8 +677,8 @@
     }
 }
 
-void AudioFlinger::audioConfigChanged(int event, const sp<ThreadBase>& thread, void *param2) {
-    Mutex::Autolock _l(mLock);
+// audioConfigChanged_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::audioConfigChanged_l(int event, const sp<ThreadBase>& thread, void *param2) {
     int ioHandle = 0;
 
     for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
@@ -700,7 +700,7 @@
         size_t size = mNotificationClients.size();
         for (size_t i = 0; i < size; i++) {
             sp<IBinder> binder = mNotificationClients.itemAt(i);
-            LOGV("audioConfigChanged() Notifying change to client %p", binder.get());
+            LOGV("audioConfigChanged_l() Notifying change to client %p", binder.get());
             sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder);
             client->ioConfigChanged(event, ioHandle, param2);
         }
@@ -803,8 +803,8 @@
         LOGV("processConfigEvents() remaining events %d", mConfigEvents.size());
         ConfigEvent *configEvent = mConfigEvents[0];
         mConfigEvents.removeAt(0);
-        // release mLock because audioConfigChanged() will call
-        // Audioflinger::audioConfigChanged() which locks AudioFlinger mLock thus creating
+        // release mLock because audioConfigChanged() will lock AudioFlinger mLock
+        // before calling Audioflinger::audioConfigChanged_l() thus creating
         // potential cross deadlock between AudioFlinger::mLock and mLock
         mLock.unlock();
         audioConfigChanged(configEvent->mEvent, configEvent->mParam);
@@ -1118,7 +1118,8 @@
     default:
         break;
     }
-    mAudioFlinger->audioConfigChanged(event, this, param2);
+    Mutex::Autolock _l(mAudioFlinger->mLock);
+    mAudioFlinger->audioConfigChanged_l(event, this, param2);
 }
 
 void AudioFlinger::PlaybackThread::readOutputParameters()
@@ -1239,7 +1240,7 @@
                 // active tracks were late. Sleep a little bit to give
                 // them another chance. If we're too late, write 0s to audio
                 // hardware to avoid underrun.
-                if (sleepTime < kMaxBufferRecoveryInUsecs) {
+                if (mBytesWritten == 0 || sleepTime < kMaxBufferRecoveryInUsecs) {
                     usleep(kBufferRecoveryInUsecs);
                 } else {
                     memset (curBuf, 0, mixBufferSize);
@@ -1272,8 +1273,6 @@
     if (!mStandby) {
         mOutput->standby();
     }
-    sendConfigEvent(AudioSystem::OUTPUT_CLOSED);
-    processConfigEvents();
 
     LOGV("MixerThread %p exiting", this);
     return false;
@@ -1741,7 +1740,8 @@
                 standbyTime = systemTime() + kStandbyTimeInNsecs;
             } else {
                 sleepTime += kBufferRecoveryInUsecs;
-                if (sleepTime < kMaxBufferRecoveryInUsecs) {
+                if (mBytesWritten == 0 || !AudioSystem::isLinearPCM(mFormat) ||
+                    sleepTime < kMaxBufferRecoveryInUsecs) {
                     usleep(kBufferRecoveryInUsecs);
                 } else {
                     memset (mMixBuffer, 0, mFrameCount * mFrameSize);
@@ -1771,8 +1771,6 @@
     if (!mStandby) {
         mOutput->standby();
     }
-    sendConfigEvent(AudioSystem::OUTPUT_CLOSED);
-    processConfigEvents();
 
     LOGV("DirectOutputThread %p exiting", this);
     return false;
@@ -1964,9 +1962,6 @@
         }
     }
 
-    sendConfigEvent(AudioSystem::OUTPUT_CLOSED);
-    processConfigEvents();
-
     return false;
 }
 
@@ -3046,9 +3041,6 @@
     }
     mActiveTrack.clear();
 
-    sendConfigEvent(AudioSystem::INPUT_CLOSED);
-    processConfigEvents();
-
     LOGV("RecordThread %p exiting", this);
     return false;
 }
@@ -3233,7 +3225,8 @@
     default:
         break;
     }
-    mAudioFlinger->audioConfigChanged(event, this, param2);
+    Mutex::Autolock _l(mAudioFlinger->mLock);
+    mAudioFlinger->audioConfigChanged_l(event, this, param2);
 }
 
 void AudioFlinger::RecordThread::readInputParameters()
@@ -3378,6 +3371,8 @@
                 }
             }
         }
+        void *param2 = 0;
+        audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, thread, param2);
         mPlaybackThreads.removeItem(output);
     }
     thread->exit();
@@ -3497,6 +3492,8 @@
         }
 
         LOGV("closeInput() %d", input);
+        void *param2 = 0;
+        audioConfigChanged_l(AudioSystem::INPUT_CLOSED, thread, param2);
         mRecordThreads.removeItem(input);
     }
     thread->exit();
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index 65c148e..7a6641f 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -616,7 +616,7 @@
               MixerThread *checkMixerThread_l(int output) const;
               RecordThread *checkRecordThread_l(int input) const;
               float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; }
-              void audioConfigChanged(int event, const sp<ThreadBase>& thread, void *param2);
+              void audioConfigChanged_l(int event, const sp<ThreadBase>& thread, void *param2);
 
     friend class AudioBuffer;
 
diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp
index 12a7725..0c6d340 100644
--- a/libs/ui/Camera.cpp
+++ b/libs/ui/Camera.cpp
@@ -242,6 +242,14 @@
     return c->autoFocus();
 }
 
+status_t Camera::cancelAutoFocus()
+{
+    LOGV("cancelAutoFocus");
+    sp <ICamera> c = mCamera;
+    if (c == 0) return NO_INIT;
+    return c->cancelAutoFocus();
+}
+
 // take a picture
 status_t Camera::takePicture()
 {
diff --git a/libs/ui/ICamera.cpp b/libs/ui/ICamera.cpp
index 805c2ca..fd7e084 100644
--- a/libs/ui/ICamera.cpp
+++ b/libs/ui/ICamera.cpp
@@ -32,6 +32,7 @@
     START_PREVIEW,
     STOP_PREVIEW,
     AUTO_FOCUS,
+    CANCEL_AUTO_FOCUS,
     TAKE_PICTURE,
     SET_PARAMETERS,
     GET_PARAMETERS,
@@ -162,6 +163,17 @@
         return ret;
     }
 
+    // cancel focus
+    status_t cancelAutoFocus()
+    {
+        LOGV("cancelAutoFocus");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
+        remote()->transact(CANCEL_AUTO_FOCUS, data, &reply);
+        status_t ret = reply.readInt32();
+        return ret;
+    }
+
     // take a picture - returns an IMemory (ref-counted mmap)
     status_t takePicture()
     {
@@ -294,6 +306,12 @@
             reply->writeInt32(autoFocus());
             return NO_ERROR;
         } break;
+        case CANCEL_AUTO_FOCUS: {
+            LOGV("CANCEL_AUTO_FOCUS");
+            CHECK_INTERFACE(ICamera, data, reply);
+            reply->writeInt32(cancelAutoFocus());
+            return NO_ERROR;
+        } break;
         case TAKE_PICTURE: {
             LOGV("TAKE_PICTURE");
             CHECK_INTERFACE(ICamera, data, reply);
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 6b20445..d5f1c61 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -27,6 +27,7 @@
     <bool name="def_accelerometer_rotation">true</bool>
     <!-- Default screen brightness, from 0 to 255.  102 is 40%. -->
     <integer name="def_screen_brightness">102</integer>
+    <bool name="def_screen_brightness_automatic_mode">false</bool>
     <fraction name="def_window_animation_scale">100%</fraction>
     <fraction name="def_window_transition_scale">0%</fraction>
     
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 2524a30..f99eb58 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -71,7 +71,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 39;
+    private static final int DATABASE_VERSION = 40;
 
     private Context mContext;
 
@@ -465,6 +465,22 @@
             upgradeVersion = 39;
         }
 
+        if (upgradeVersion == 39) {
+            db.beginTransaction();
+            try {
+                String value =
+                        mContext.getResources().getBoolean(
+                        R.bool.def_screen_brightness_automatic_mode) ? "1" : "0";
+                db.execSQL("INSERT OR IGNORE INTO system(name,value) values('" +
+                        Settings.System.SCREEN_BRIGHTNESS_MODE + "','" + value + "');");
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+
+            upgradeVersion = 40;
+        }
+
         if (upgradeVersion != currentVersion) {
             Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion
                     + ", must wipe the settings provider");
@@ -701,6 +717,9 @@
         loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS,
                 R.integer.def_screen_brightness);
 
+        loadBooleanSetting(stmt, Settings.System.SCREEN_BRIGHTNESS_MODE,
+                R.bool.def_screen_brightness_automatic_mode);
+
         loadDefaultAnimationSettings(stmt);
 
         loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION,
diff --git a/services/java/com/android/server/HardwareService.java b/services/java/com/android/server/HardwareService.java
index 6ac72e0..01daae3 100755
--- a/services/java/com/android/server/HardwareService.java
+++ b/services/java/com/android/server/HardwareService.java
@@ -59,6 +59,8 @@
     private boolean mAttentionLightOn;
     private boolean mPulsing;
 
+    private boolean mAutoBrightnessAvailable;
+
     private class Vibration implements IBinder.DeathRecipient {
         private final IBinder mToken;
         private final long    mTimeout;
@@ -129,6 +131,9 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         context.registerReceiver(mIntentReceiver, filter);
+
+        mAutoBrightnessAvailable = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_automatic_brightness_available);
     }
 
     protected void finalize() throws Throwable {
@@ -302,6 +307,20 @@
         setLight_native(mNativePointer, light, color, mode, onMS, offMS);
     }
 
+    public void setAutoBrightness(boolean on) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires HARDWARE_TEST permission");
+        }
+        setAutoBrightness_UNCHECKED(on);
+    }
+
+    void setAutoBrightness_UNCHECKED(boolean on) {
+        if (mAutoBrightnessAvailable) {
+            setAutoBrightness_native(mNativePointer, on);
+        }
+    }
+
     public void setAttentionLight(boolean on) {
         // Not worthy of a permission.  We shouldn't have a flashlight permission.
         synchronized (this) {
@@ -502,6 +521,7 @@
     private static native int init_native();
     private static native void finalize_native(int ptr);
 
+    private static native void setAutoBrightness_native(int ptr, boolean automatic);
     private static native void setLight_native(int ptr, int light, int color, int mode,
             int onMS, int offMS);
 
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index ba65f01..2951384 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -52,6 +52,7 @@
 import android.view.WindowManagerPolicy;
 import static android.provider.Settings.System.DIM_SCREEN;
 import static android.provider.Settings.System.SCREEN_BRIGHTNESS;
+import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE;
 import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
 import static android.provider.Settings.System.STAY_ON_WHILE_PLUGGED_IN;
 
@@ -87,6 +88,9 @@
     private static final int LONG_KEYLIGHT_DELAY = 6000;        // t+6 sec
     private static final int LONG_DIM_TIME = 7000;              // t+N-5 sec
 
+    // trigger proximity if distance is less than 5 cm
+    private static final float PROXIMITY_THRESHOLD = 5.0f;
+
     // Cached Gservices settings; see updateGservicesValues()
     private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT;
 
@@ -442,7 +446,11 @@
 
         // turn everything on
         setPowerState(ALL_BRIGHT);
-        
+
+        // set auto brightness mode to user setting
+        boolean brightnessMode = Settings.System.getInt(resolver, SCREEN_BRIGHTNESS_MODE, 1) != 0;
+        mHardware.setAutoBrightness_UNCHECKED(brightnessMode);
+
         synchronized (mHandlerThread) {
             mInitComplete = true;
             mHandlerThread.notifyAll();
@@ -2079,18 +2087,19 @@
     public void onSensorChanged(SensorEvent event) {
         long milliseconds = event.timestamp / 1000000;
         synchronized (mLocks) {
-            if (event.values[0] == 0.0) {
+            float distance = event.values[0];
+            if (distance >= 0.0 && distance < PROXIMITY_THRESHOLD) {
                 if (mSpew) {
-                    Log.d(TAG, "onSensorChanged: proximity active");
+                    Log.d(TAG, "onSensorChanged: proximity active, distance: " + distance);
                 }
                 goToSleepLocked(milliseconds);
                 mProximitySensorActive = true;
             } else {
-                // proximity sensor negative events user activity.
+                // proximity sensor negative events trigger as user activity.
                 // temporarily set mUserActivityAllowed to true so this will work
                 // even when the keyguard is on.
                 if (mSpew) {
-                    Log.d(TAG, "onSensorChanged: proximity inactive");
+                    Log.d(TAG, "onSensorChanged: proximity inactive, distance: " + distance);
                 }
                 mProximitySensorActive = false;
                 boolean savedActivityAllowed = mUserActivityAllowed;
diff --git a/services/jni/com_android_server_HardwareService.cpp b/services/jni/com_android_server_HardwareService.cpp
index 22d4bd8..a17e29f 100644
--- a/services/jni/com_android_server_HardwareService.cpp
+++ b/services/jni/com_android_server_HardwareService.cpp
@@ -100,6 +100,18 @@
     free(devices);
 }
 
+static void setAutoBrightness_native(JNIEnv *env, jobject clazz, int ptr,
+        jboolean automatic)
+{
+    Devices* devices = (Devices*)ptr;
+
+    if (devices->lights[LIGHT_INDEX_BACKLIGHT] == NULL) {
+        return;
+    }
+
+    devices->lights[LIGHT_INDEX_BACKLIGHT]->set_als_mode(automatic ? 0 : 1);
+}
+
 static void setLight_native(JNIEnv *env, jobject clazz, int ptr,
         int light, int colorARGB, int flashMode, int onMS, int offMS)
 {
@@ -134,6 +146,7 @@
 static JNINativeMethod method_table[] = {
     { "init_native", "()I", (void*)init_native },
     { "finalize_native", "(I)V", (void*)finalize_native },
+    { "setAutoBrightness_native", "(IZ)V", (void*)setAutoBrightness_native },
     { "setLight_native", "(IIIIII)V", (void*)setLight_native },
     { "vibratorOn", "(J)V", (void*)vibratorOn },
     { "vibratorOff", "()V", (void*)vibratorOff }