Merge change I5d30aa18 into eclair

* changes:
  Make vCard parser invalid lines in vCard file which look like some comment. Do not merge.
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index e548524..df59dcf 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -683,22 +683,30 @@
 {
     LOGD("stopPreview (pid %d)", getCallingPid());
 
-    Mutex::Autolock lock(mLock);
-    if (checkPid() != NO_ERROR) return;
+    // hold main lock during state transition
+    {
+        Mutex::Autolock lock(mLock);
+        if (checkPid() != NO_ERROR) return;
 
-    if (mHardware == 0) {
-        LOGE("mHardware is NULL, returning.");
-        return;
+        if (mHardware == 0) {
+            LOGE("mHardware is NULL, returning.");
+            return;
+        }
+
+        mHardware->stopPreview();
+        mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+        LOGD("stopPreview(), hardware stopped OK");
+
+        if (mSurface != 0 && !mUseOverlay) {
+            mSurface->unregisterBuffers();
+        }
     }
 
-    mHardware->stopPreview();
-    mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
-    LOGD("stopPreview(), hardware stopped OK");
-
-    if (mSurface != 0 && !mUseOverlay) {
-        mSurface->unregisterBuffers();
+    // hold preview buffer lock
+    {
+        Mutex::Autolock lock(mPreviewLock);
+        mPreviewBuffer.clear();
     }
-    mPreviewBuffer.clear();
 }
 
 // stop recording mode
@@ -706,24 +714,31 @@
 {
     LOGD("stopRecording (pid %d)", getCallingPid());
 
-    Mutex::Autolock lock(mLock);
-    if (checkPid() != NO_ERROR) return;
+    // hold main lock during state transition
+    {
+        Mutex::Autolock lock(mLock);
+        if (checkPid() != NO_ERROR) return;
 
-    if (mHardware == 0) {
-        LOGE("mHardware is NULL, returning.");
-        return;
+        if (mHardware == 0) {
+            LOGE("mHardware is NULL, returning.");
+            return;
+        }
+
+        if (mMediaPlayerBeep.get() != NULL) {
+            mMediaPlayerBeep->seekTo(0);
+            mMediaPlayerBeep->start();
+        }
+
+        mHardware->stopRecording();
+        mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME);
+        LOGD("stopRecording(), hardware stopped OK");
     }
 
-    if (mMediaPlayerBeep.get() != NULL) {
-        mMediaPlayerBeep->seekTo(0);
-        mMediaPlayerBeep->start();
+    // hold preview buffer lock
+    {
+        Mutex::Autolock lock(mPreviewLock);
+        mPreviewBuffer.clear();
     }
-
-    mHardware->stopRecording();
-    mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME);
-    LOGD("stopRecording(), hardware stopped OK");
-
-    mPreviewBuffer.clear();
 }
 
 // release a recording frame
@@ -1216,10 +1231,10 @@
     // provided it's big enough. Don't allocate the memory or
     // perform the copy if there's no callback.
 
-    // hold the lock while we grab a reference to the preview buffer
+    // hold the preview lock while we grab a reference to the preview buffer
     sp<MemoryHeapBase> previewBuffer;
     {
-        Mutex::Autolock lock(mLock);
+        Mutex::Autolock lock(mPreviewLock);
         if (mPreviewBuffer == 0) {
             mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
         } else if (size > mPreviewBuffer->virtualSize()) {
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index 41c5d99..3e3e54f 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -181,7 +181,6 @@
         mutable     Condition                   mReady;
                     sp<CameraService>           mCameraService;
                     sp<ISurface>                mSurface;
-                    sp<MemoryHeapBase>          mPreviewBuffer;
                     int                         mPreviewCallbackFlag;
 
                     sp<MediaPlayer>             mMediaPlayerClick;
@@ -197,6 +196,9 @@
                     sp<OverlayRef>              mOverlayRef;
                     int                         mOverlayW;
                     int                         mOverlayH;
+
+        mutable     Mutex                       mPreviewLock;
+                    sp<MemoryHeapBase>          mPreviewBuffer;
     };
 
 // ----------------------------------------------------------------------------
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java
index ce063a7..d6c76a2 100644
--- a/core/java/android/accounts/AccountAuthenticatorCache.java
+++ b/core/java/android/accounts/AccountAuthenticatorCache.java
@@ -18,10 +18,16 @@
 
 import android.content.pm.PackageManager;
 import android.content.pm.RegisteredServicesCache;
+import android.content.pm.XmlSerializerAndParser;
 import android.content.res.TypedArray;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.text.TextUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
 
 /**
  * A cache of services that export the {@link IAccountAuthenticator} interface. This cache
@@ -33,10 +39,12 @@
 /* package private */ class AccountAuthenticatorCache
         extends RegisteredServicesCache<AuthenticatorDescription> {
     private static final String TAG = "Account";
+    private static final MySerializer sSerializer = new MySerializer();
 
     public AccountAuthenticatorCache(Context context) {
         super(context, AccountManager.ACTION_AUTHENTICATOR_INTENT,
-                AccountManager.AUTHENTICATOR_META_DATA_NAME, AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME);
+                AccountManager.AUTHENTICATOR_META_DATA_NAME,
+                AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer);
     }
 
     public AuthenticatorDescription parseServiceAttributes(String packageName, AttributeSet attrs) {
@@ -62,4 +70,16 @@
             sa.recycle();
         }
     }
+
+    private static class MySerializer implements XmlSerializerAndParser<AuthenticatorDescription> {
+        public void writeAsXml(AuthenticatorDescription item, XmlSerializer out)
+                throws IOException {
+            out.attribute(null, "type", item.type);
+        }
+
+        public AuthenticatorDescription createFromXml(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            return AuthenticatorDescription.newKey(parser.getAttributeValue(null, "type"));
+        }
+    }
 }
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 4f59c4e..800ad749 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -72,7 +72,7 @@
  */
 public class AccountManagerService
         extends IAccountManager.Stub
-        implements RegisteredServicesCacheListener {
+        implements RegisteredServicesCacheListener<AuthenticatorDescription> {
     private static final String GOOGLE_ACCOUNT_TYPE = "com.google";
 
     private static final String NO_BROADCAST_FLAG = "nobroadcast";
@@ -220,34 +220,29 @@
         mMessageHandler = new MessageHandler(mMessageThread.getLooper());
 
         mAuthenticatorCache = new AccountAuthenticatorCache(mContext);
-        mAuthenticatorCache.setListener(this);
+        mAuthenticatorCache.setListener(this, null /* Handler */);
         mBindHelper = new AuthenticatorBindHelper(mContext, mAuthenticatorCache, mMessageHandler,
                 MESSAGE_CONNECTED, MESSAGE_DISCONNECTED);
 
         mSimWatcher = new SimWatcher(mContext);
         sThis.set(this);
-
-        onRegisteredServicesCacheChanged();
     }
 
-    public void onRegisteredServicesCacheChanged() {
+    public void onServiceChanged(AuthenticatorDescription desc, boolean removed) {
         boolean accountDeleted = false;
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         Cursor cursor = db.query(TABLE_ACCOUNTS,
                 new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
-                null, null, null, null, null);
+                ACCOUNTS_TYPE + "=?", new String[]{desc.type}, null, null, null);
         try {
             while (cursor.moveToNext()) {
                 final long accountId = cursor.getLong(0);
                 final String accountType = cursor.getString(1);
                 final String accountName = cursor.getString(2);
-                if (mAuthenticatorCache.getServiceInfo(AuthenticatorDescription.newKey(accountType))
-                        == null) {
-                    Log.d(TAG, "deleting account " + accountName + " because type "
-                            + accountType + " no longer has a registered authenticator");
-                    db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
-                    accountDeleted= true;
-                }
+                Log.d(TAG, "deleting account " + accountName + " because type "
+                        + accountType + " no longer has a registered authenticator");
+                db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
+                accountDeleted = true;
             }
         } finally {
             cursor.close();
diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java
index e642700..91c94e6 100644
--- a/core/java/android/accounts/AuthenticatorDescription.java
+++ b/core/java/android/accounts/AuthenticatorDescription.java
@@ -87,6 +87,10 @@
         return type.equals(other.type);
     }
 
+    public String toString() {
+        return "AuthenticatorDescription {type=" + type + "}";
+    }
+
     /** @inhericDoc */
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(type);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index daf4090..b116bf8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -485,7 +485,8 @@
             return mResources;
         }
 
-        public Application makeApplication(boolean forceDefaultAppClass) {
+        public Application makeApplication(boolean forceDefaultAppClass,
+                Instrumentation instrumentation) {
             if (mApplication != null) {
                 return mApplication;
             }
@@ -512,7 +513,21 @@
                 }
             }
             mActivityThread.mAllApplications.add(app);
-            return mApplication = app;
+            mApplication = app;
+            
+            if (instrumentation != null) {
+                try {
+                    instrumentation.callApplicationOnCreate(app);
+                } catch (Exception e) {
+                    if (!instrumentation.onException(app, e)) {
+                        throw new RuntimeException(
+                            "Unable to create application " + app.getClass().getName()
+                            + ": " + e.toString(), e);
+                    }
+                }
+            }
+            
+            return app;
         }
 
         public void removeContextRegistrations(Context context,
@@ -2378,7 +2393,7 @@
         }
 
         try {
-            Application app = r.packageInfo.makeApplication(false);
+            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
 
             if (localLOGV) Log.v(TAG, "Performing launch of " + r);
             if (localLOGV) Log.v(
@@ -2576,7 +2591,7 @@
         }
 
         try {
-            Application app = packageInfo.makeApplication(false);
+            Application app = packageInfo.makeApplication(false, mInstrumentation);
 
             if (localLOGV) Log.v(
                 TAG, "Performing receive of " + data.intent
@@ -2730,7 +2745,7 @@
             ApplicationContext context = new ApplicationContext();
             context.init(packageInfo, null, this);
 
-            Application app = packageInfo.makeApplication(false);
+            Application app = packageInfo.makeApplication(false, mInstrumentation);
             context.setOuterContext(service);
             service.attach(context, this, data.info.name, data.token, app,
                     ActivityManagerNative.getDefault());
@@ -3940,7 +3955,7 @@
 
         // If the app is being launched for full backup or restore, bring it up in
         // a restricted environment with the base application class.
-        Application app = data.info.makeApplication(data.restrictedBackupMode);
+        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
         mInitialApplication = app;
 
         List<ProviderInfo> providers = data.providers;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 4684f45..bd5b07c 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -36,13 +36,30 @@
 import java.util.UUID;
 
 /**
- * Represents the local Bluetooth adapter.
- *
- * <p>Use {@link #getDefaultAdapter} to get the default local Bluetooth
- * adapter.
- *
- * <p>Use the {@link BluetoothDevice} class for operations on remote Bluetooth
+ * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
+ * lets you perform fundamental Bluetooth tasks, such as initiate
+ * device discovery, query a list of bonded (paired) devices,
+ * instantiate a {@link BluetoothDevice} using a known MAC address, and create
+ * a {@link BluetoothServerSocket} to listen for connection requests from other
  * devices.
+ *
+ * <p>To get a {@link BluetoothAdapter} representing the local Bluetooth
+ * adapter, call the static {@link #getDefaultAdapter} method.
+ * Fundamentally, this is your starting point for all
+ * Bluetooth actions. Once you have the local adapter, you can get a set of
+ * {@link BluetoothDevice} objects representing all paired devices with
+ * {@link #getBondedDevices()}; start device discovery with
+ * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
+ * listen for incoming connection requests with
+ * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Most methods require the {@link android.Manifest.permission#BLUETOOTH}
+ * permission and some also require the
+ * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ *
+ * {@see BluetoothDevice}
+ * {@see BluetoothServerSocket}
  */
 public final class BluetoothAdapter {
     private static final String TAG = "BluetoothAdapter";
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 6210380..bc06713 100644
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -20,25 +20,37 @@
 import android.os.Parcelable;
 
 /**
- * Represents a Bluetooth class.
+ * Represents a Bluetooth class, which describes general characteristics
+ * and capabilities of a device. For example, a Bluetooth class will
+ * specify the general device type such as a phone, a computer, or
+ * headset, and whether it's capable of services such as audio or telephony.
  *
- * <p>Bluetooth Class is a 32 bit field. The format of these bits is defined at
- *   http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
- * (login required). This class contains that 32 bit field, and provides
- * constants and methods to determine which Service Class(es) and Device Class
- * are encoded in that field.
+ * <p>The Bluetooth class is useful as a hint to roughly describe a device (for example to
+ * show an icon in the UI), but does not reliably describe which Bluetooth
+ * profiles or services are actually supported by a device.
  *
- * <p>Every Bluetooth Class is composed of zero or more service classes, and
+ * <p>Every Bluetooth class is composed of zero or more service classes, and
  * exactly one device class. The device class is further broken down into major
  * and minor device class components.
  *
- * <p>Class is useful as a hint to roughly describe a device (for example to
- * show an icon in the UI), but does not reliably describe which Bluetooth
- * profiles or services are actually supported by a device. Accurate service
- * discovery is done through SDP requests.
+ * <p>{@link BluetoothClass} is useful as a hint to roughly describe a device
+ * (for example to show an icon in the UI), but does not reliably describe which
+ * Bluetooth profiles or services are actually supported by a device. Accurate
+ * service discovery is done through SDP requests, which are automatically
+ * performed when creating an RFCOMM socket with {@link
+ * BluetoothDevice#createRfcommSocketToServiceRecord(UUID)} and {@link
+ * BluetoothAdapter#listenUsingRfcommWithServiceRecord(String,UUID)}</p>
  *
  * <p>Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for
  * a remote device.
+ *
+ * <!--
+ * The Bluetooth class is a 32 bit field. The format of these bits is defined at
+ * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
+ * (login required). This class contains that 32 bit field, and provides
+ * constants and methods to determine which Service Class(es) and Device Class
+ * are encoded in that field.
+ * -->
  */
 public final class BluetoothClass implements Parcelable {
     /**
@@ -91,7 +103,7 @@
     }
 
     /**
-     * Bluetooth service classes.
+     * Defines all service class constants.
      * <p>Each {@link BluetoothClass} encodes zero or more service classes.
      */
     public static final class Service {
@@ -109,7 +121,8 @@
     }
 
     /**
-     * Return true if the specified service class is supported by this class.
+     * Return true if the specified service class is supported by this
+     * {@link BluetoothClass}.
      * <p>Valid service classes are the public constants in
      * {@link BluetoothClass.Service}. For example, {@link
      * BluetoothClass.Service#AUDIO}.
@@ -122,17 +135,22 @@
     }
 
     /**
-     * Bluetooth device classes.
+     * Defines all device class constants.
      * <p>Each {@link BluetoothClass} encodes exactly one device class, with
      * major and minor components.
      * <p>The constants in {@link
      * BluetoothClass.Device} represent a combination of major and minor
-     * components (the complete device class). The constants in {@link
-     * BluetoothClass.Device.Major} represent just the major device classes.
+     * device components (the complete device class). The constants in {@link
+     * BluetoothClass.Device.Major} represent only major device classes.
+     * <p>See {@link BluetoothClass.Service} for service class constants.
      */
     public static class Device {
         private static final int BITMASK               = 0x1FFC;
 
+        /**
+         * Defines all major device class constants.
+         * <p>See {@link BluetoothClass.Device} for minor classes.
+         */
         public static class Major {
             private static final int BITMASK           = 0x1F00;
 
@@ -215,7 +233,7 @@
     }
 
     /**
-     * Return the major device class component of this Bluetooth class.
+     * Return the major device class component of this {@link BluetoothClass}.
      * <p>Values returned from this function can be compared with the
      * public constants in {@link BluetoothClass.Device.Major} to determine
      * which major class is encoded in this Bluetooth class.
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 849e6c7..6cb9770 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -31,16 +31,31 @@
 import java.util.UUID;
 
 /**
- * Represents a remote Bluetooth device.
- *
- * <p>Use {@link BluetoothAdapter#getRemoteDevice} to create a {@link
- * BluetoothDevice}.
+ * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you
+ * create a connection with the repective device or query information about
+ * it, such as the name, address, class, and bonding state.
  *
  * <p>This class is really just a thin wrapper for a Bluetooth hardware
  * address. Objects of this class are immutable. Operations on this class
  * are performed on the remote Bluetooth hardware address, using the
  * {@link BluetoothAdapter} that was used to create this {@link
  * BluetoothDevice}.
+ *
+ * <p>To get a {@link BluetoothDevice}, use
+ * {@link BluetoothAdapter#getRemoteDevice(String)
+ * BluetoothAdapter.getRemoteDevice(String)} to create one representing a device
+ * of a known MAC address (which you can get through device discovery with
+ * {@link BluetoothAdapter}) or get one from the set of bonded devices
+ * returned by {@link BluetoothAdapter#getBondedDevices()
+ * BluetoothAdapter.getBondedDevices()}. You can then open a
+ * {@link BluetoothSocket} for communciation with the remote device, using
+ * {@link #createRfcommSocketToServiceRecord(UUID)}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * {@see BluetoothAdapter}
+ * {@see BluetoothSocket}
  */
 public final class BluetoothDevice implements Parcelable {
     private static final String TAG = "BluetoothDevice";
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 605bdc1..1b23f6c 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -27,29 +27,31 @@
  * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
  * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
  * side, use a {@link BluetoothServerSocket} to create a listening server
- * socket. It will return a new, connected {@link BluetoothSocket} on an
- * accepted connection. On the client side, use the same
- * {@link BluetoothSocket} object to both intiate the outgoing connection,
- * and to manage the connected socket.
+ * socket. When a connection is accepted by the {@link BluetoothServerSocket},
+ * it will return a new {@link BluetoothSocket} to manage the connection.
+ * On the client side, use a single {@link BluetoothSocket} to both intiate
+ * an outgoing connection and to manage the connection.
  *
- * <p>The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a
- * connection orientated, streaming transport over Bluetooth. It is also known
- * as the Serial Port Profile (SPP).
+ * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
+ * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
+ * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
  *
- * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to create
- * a new {@link BluetoothSocket} ready for an outgoing connection to a remote
- * {@link BluetoothDevice}.
+ * <p>To create a listenting {@link BluetoothServerSocket} that's ready for
+ * incoming connections, use
+ * {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord
+ * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call
+ * {@link #accept()} to listen for incoming connection requests. This call
+ * will block until a connection is established, at which point, it will return
+ * a {@link BluetoothSocket} to manage the connection.
  *
- * <p>Use {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord} to
- * create a listening {@link BluetoothServerSocket} ready for incoming
- * connections to the local {@link BluetoothAdapter}.
- *
- * <p>{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread
+ * <p>{@link BluetoothServerSocket} is thread
  * safe. In particular, {@link #close} will always immediately abort ongoing
- * operations and close the socket.
+ * operations and close the server socket.
  *
- * <p>All methods on a {@link BluetoothServerSocket} require
- * {@link android.Manifest.permission#BLUETOOTH}
+ * <p class="note"><strong>Note:</strong>
+ * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * {@see BluetoothSocket}
  */
 public final class BluetoothServerSocket implements Closeable {
 
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 7e72590..dbcc758 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -33,29 +33,41 @@
  * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
  * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
  * side, use a {@link BluetoothServerSocket} to create a listening server
- * socket. It will return a new, connected {@link BluetoothSocket} on an
- * accepted connection. On the client side, use the same
- * {@link BluetoothSocket} object to both intiate the outgoing connection,
- * and to manage the connected socket.
+ * socket. When a connection is accepted by the {@link BluetoothServerSocket},
+ * it will return a new {@link BluetoothSocket} to manage the connection.
+ * On the client side, use a single {@link BluetoothSocket} to both intiate
+ * an outgoing connection and to manage the connection.
  *
- * <p>The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a
- * connection orientated, streaming transport over Bluetooth. It is also known
- * as the Serial Port Profile (SPP).
+ * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
+ * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
+ * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
  *
- * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to create
- * a new {@link BluetoothSocket} ready for an outgoing connection to a remote
- * {@link BluetoothDevice}.
+ * <p>To create a {@link BluetoothSocket} for connecting to a known device, use
+ * {@link BluetoothDevice#createRfcommSocketToServiceRecord
+ * BluetoothDevice.createRfcommSocketToServiceRecord()}.
+ * Then call {@link #connect()} to attempt a connection to the remote device.
+ * This call will block until a connection is established or the connection
+ * fails.
  *
- * <p>Use {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord} to
- * create a listening {@link BluetoothServerSocket} ready for incoming
- * connections to the local {@link BluetoothAdapter}.
+ * <p>To create a {@link BluetoothSocket} as a server (or "host"), see the
+ * {@link BluetoothServerSocket} documentation.
  *
- * <p>{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread
+ * <p>Once the socket is connected, whether initiated as a client or accepted
+ * as a server, open the IO streams by calling {@link #getInputStream} and
+ * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream}
+ * and {@link java.io.OutputStream} objects, respectively, which are
+ * automatically connected to the socket.
+ *
+ * <p>{@link BluetoothSocket} is thread
  * safe. In particular, {@link #close} will always immediately abort ongoing
  * operations and close the socket.
  *
- * <p>All methods on a {@link BluetoothSocket} require
- * {@link android.Manifest.permission#BLUETOOTH}
+ * <p class="note"><strong>Note:</strong>
+ * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * {@see BluetoothServerSocket}
+ * {@see java.io.InputStream}
+ * {@see java.io.OutputStream}
  */
 public final class BluetoothSocket implements Closeable {
     private static final String TAG = "BluetoothSocket";
diff --git a/core/java/android/bluetooth/package.html b/core/java/android/bluetooth/package.html
index 79abf0c..4f0755e 100644
--- a/core/java/android/bluetooth/package.html
+++ b/core/java/android/bluetooth/package.html
@@ -1,13 +1,109 @@
 <HTML>
 <BODY>
-Provides classes that manage Bluetooth functionality on the device.
-<p>
-The Bluetooth APIs allow applications can connect and disconnect headsets, or scan 
-for other kinds of Bluetooth devices and pair them. Further control includes the 
-ability to write and modify the local Service Discovery Protocol (SDP) database, 
-query the SDP database of other Bluetooth devices, establish RFCOMM 
-channels/sockets on Android, and connect to specified sockets on other devices.
+Provides classes that manage Bluetooth functionality, such as scanning for
+devices, connecting with devices, and managing data transfer between devices.
+
+<p>The Bluetooth APIs let applications:</p>
+<ul>
+  <li>Scan for other Bluetooth devices</li>
+  <li>Query the local Bluetooth adapter for paired Bluetooth devices</li>
+  <li>Establish RFCOMM channels/sockets</li>
+  <li>Connect to specified sockets on other devices</li>
+  <li>Transfer data to and from other devices</li>
+</ul>
+
+<p class="note"><strong>Note:</strong>
+To perform Bluetooth communication using these APIs, an application must
+declare the {@link android.Manifest.permission#BLUETOOTH} permission. Some
+additional functionality, such as requesting device discovery and
+pairing also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+permission.
 </p>
-<p>Remember, not all Android devices are guaranteed to have Bluetooth functionality.</p>
+
+<h3>Overview</h3>
+
+<p>Here's a basic introduction to the Bluetooth classes:</p>
+<dl>
+  <dt>{@link android.bluetooth.BluetoothAdapter}</dt>
+  <dd>This represents the local Bluetooth adapter, which is essentially the
+  entry-point to performing any interaction with Bluetooth. With it, you can
+  discover other Bluetooth devices, query a list of bonded (paired) devices,
+  initialize a {@link android.bluetooth.BluetoothDevice} using a known MAC
+  address, and create a {@link android.bluetooth.BluetoothServerSocket} to
+  listen for communications from other devices.</dd>
+
+  <dt>{@link android.bluetooth.BluetoothDevice}</dt>
+  <dd>This represents a remote Bluetooth device. Use this to request a
+  connection with a remote device through a
+  {@link android.bluetooth.BluetoothSocket}
+  or query information about the device such as its name, address, class, and
+  bonding state.</dd>
+
+  <dt>{@link android.bluetooth.BluetoothSocket}</dt>
+  <dd>This represents the interface for a Bluetooth socket
+  (similar to a TCP client-side {@link java.net.Socket}). This is the
+  connection point that allows an app to transfer data with another Bluetooth
+  device via {@link java.io.InputStream} and {@link java.io.OutputStream}.</dd>
+  <dt>{@link android.bluetooth.BluetoothServerSocket}</dt>
+
+  <dd>This represents an open server socket that listens for incoming requests
+  (similar to a TCP server-side {@link java.net.ServerSocket}).
+  When attempting to connect two Android devices, one device will need to open
+  a server socket with this class. When a connection is accepted, a new
+  {@link android.bluetooth.BluetoothSocket} will be returned,
+  which can be used to manage the connection and transfer data.</dd>
+
+  <dt>{@link android.bluetooth.BluetoothClass}</dt>
+  <dd>This represents the Bluetooth class for a device which describes general
+  characteristics and capabilities of a device. This class and its subclasses
+  don't provide any actual functionality. The sub-classes are entirely composed
+  of constants for the device and service class definitions.</dd>
+</dl>
+
+
+<h3>Example Procedure</h3>
+
+<p>For example, here's an pseudo-code procedure for discovering and
+connecting a remote device, and transfering data:</p>
+
+<ol>
+  <li>Register a {@link android.content.BroadcastReceiver} that accepts the
+  {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent.</li>
+  <li>Call {@link android.bluetooth.BluetoothAdapter#getDefaultAdapter} to
+  retrieve the Android system's local
+  {@link android.bluetooth.BluetoothAdapter}.</li>
+  <li>Call {@link android.bluetooth.BluetoothAdapter#startDiscovery()
+  BluetoothAdapter.startDiscovery()} to scan for local devices. This is where
+  the BroadcastReceiver comes in; Android now scans for devices and will
+  broadcast the {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent
+  for each remote device discovered. The
+  {@link android.content.BroadcastReceiver}
+  you created will receive each Intent.</li>
+  <li>The {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent
+  includes the {@link android.bluetooth.BluetoothDevice#EXTRA_DEVICE}
+  Parcelable extra, which is a {@link android.bluetooth.BluetoothDevice}
+  object. Extract this from the Intent and call
+  {@link android.bluetooth.BluetoothDevice#createRfcommSocketToServiceRecord(java.util.UUID)
+  BluetoothDevice.createRfcommSocketToServiceRecord()}
+  to open a {@link android.bluetooth.BluetoothSocket} with a chosen
+  remote device.</li>
+  <li>Call {@link android.bluetooth.BluetoothSocket#connect()
+  BluetoothSocket.connect()} to connect with the remote device.</li>
+  <li>When successfully connected, call
+  {@link android.bluetooth.BluetoothSocket#getInputStream()
+  BluetoothSocket.getInputStream()} and/or
+  {@link android.bluetooth.BluetoothSocket#getOutputStream()
+  BluetoothSocket.getOutputStream()} to retreive an
+  {@link java.io.InputStream} and {@link java.io.OutputStream}, respectively,
+  which are hooked into the socket.</li>
+  <li>Use {@link java.io.InputStream#read(byte[]) InputStream.read()} and
+  {@link java.io.OutputStream#write(byte[]) OutputStream.write()} to transfer
+  data.</li>
+</ol>
+
+
+
+<p class="note"><strong>Note:</strong>
+Not all Android devices are guaranteed to have Bluetooth functionality.</p>
 </BODY>
 </HTML>
diff --git a/core/java/android/content/AbstractSyncableContentProvider.java b/core/java/android/content/AbstractSyncableContentProvider.java
index eba8715..fbe3548 100644
--- a/core/java/android/content/AbstractSyncableContentProvider.java
+++ b/core/java/android/content/AbstractSyncableContentProvider.java
@@ -135,6 +135,8 @@
         public void onCreate(SQLiteDatabase db) {
             bootstrapDatabase(db);
             mSyncState.createDatabase(db);
+            ContentResolver.requestSync(null /* all accounts */,
+                    mContentUri.getAuthority(), new Bundle());
         }
 
         @Override
diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java
index 7d9f1de..6ade837 100644
--- a/core/java/android/content/SyncAdaptersCache.java
+++ b/core/java/android/content/SyncAdaptersCache.java
@@ -17,9 +17,14 @@
 package android.content;
 
 import android.content.pm.RegisteredServicesCache;
+import android.content.pm.XmlSerializerAndParser;
 import android.content.res.TypedArray;
-import android.content.Context;
 import android.util.AttributeSet;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
 
 /**
  * A cache of services that export the {@link android.content.ISyncAdapter} interface.
@@ -31,9 +36,10 @@
     private static final String SERVICE_INTERFACE = "android.content.SyncAdapter";
     private static final String SERVICE_META_DATA = "android.content.SyncAdapter";
     private static final String ATTRIBUTES_NAME = "sync-adapter";
+    private static final MySerializer sSerializer = new MySerializer();
 
     SyncAdaptersCache(Context context) {
-        super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME);
+        super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, sSerializer);
     }
 
     public SyncAdapterType parseServiceAttributes(String packageName, AttributeSet attrs) {
@@ -57,4 +63,18 @@
             sa.recycle();
         }
     }
+
+    static class MySerializer implements XmlSerializerAndParser<SyncAdapterType> {
+        public void writeAsXml(SyncAdapterType item, XmlSerializer out) throws IOException {
+            out.attribute(null, "authority", item.authority);
+            out.attribute(null, "accountType", item.accountType);
+        }
+    
+        public SyncAdapterType createFromXml(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            final String authority = parser.getAttributeValue(null, "authority");
+            final String accountType = parser.getAttributeValue(null, "accountType");
+            return SyncAdapterType.newKey(authority, accountType);
+        }
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 538373f..b39a67d 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -22,6 +22,8 @@
 import android.content.IntentFilter;
 import android.content.ComponentName;
 import android.content.res.XmlResourceParser;
+import android.os.Environment;
+import android.os.Handler;
 import android.util.Log;
 import android.util.AttributeSet;
 import android.util.Xml;
@@ -29,14 +31,26 @@
 import java.util.Map;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicReference;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.io.IOException;
+import java.io.FileInputStream;
+
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.FastXmlSerializer;
 
 import com.google.android.collect.Maps;
+import com.google.android.collect.Lists;
+
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
 
 /**
  * A cache of registered services. This cache
@@ -52,75 +66,104 @@
     private final String mInterfaceName;
     private final String mMetaDataName;
     private final String mAttributesName;
+    private final XmlSerializerAndParser<V> mSerializerAndParser;
+    private final AtomicReference<BroadcastReceiver> mReceiver;
 
-    public RegisteredServicesCacheListener getListener() {
-        return mListener;
-    }
+    private final Object mServicesLock = new Object();
+    // synchronized on mServicesLock
+    private HashMap<V, Integer> mPersistentServices;
+    // synchronized on mServicesLock
+    private Map<V, ServiceInfo<V>> mServices;
+    // synchronized on mServicesLock
+    private boolean mPersistentServicesFileDidNotExist;
 
-    public void setListener(RegisteredServicesCacheListener listener) {
-        mListener = listener;
-    }
+    /**
+     * This file contains the list of known services. We would like to maintain this forever
+     * so we store it as an XML file.
+     */
+    private final AtomicFile mPersistentServicesFile;
 
-    private volatile RegisteredServicesCacheListener mListener;
-
-    // no need to be synchronized since the map is never changed once mService is written
-    volatile Map<V, ServiceInfo<V>> mServices;
-
-    // synchronized on "this"
-    private BroadcastReceiver mReceiver = null;
+    // the listener and handler are synchronized on "this" and must be updated together
+    private RegisteredServicesCacheListener<V> mListener;
+    private Handler mHandler;
 
     public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
-            String attributeName) {
+            String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
         mContext = context;
         mInterfaceName = interfaceName;
         mMetaDataName = metaDataName;
         mAttributesName = attributeName;
+        mSerializerAndParser = serializerAndParser;
+
+        File dataDir = Environment.getDataDirectory();
+        File systemDir = new File(dataDir, "system");
+        File syncDir = new File(systemDir, "registered_services");
+        mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml"));
+
+        generateServicesMap();
+
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context1, Intent intent) {
+                generateServicesMap();
+            }
+        };
+        mReceiver = new AtomicReference<BroadcastReceiver>(receiver);
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        mContext.registerReceiver(receiver, intentFilter);
     }
 
     public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
-        getAllServices();
-        Map<V, ServiceInfo<V>> services = mServices;
+        Map<V, ServiceInfo<V>> services;
+        synchronized (mServicesLock) {
+            services = mServices;
+        }
         fout.println("RegisteredServicesCache: " + services.size() + " services");
         for (ServiceInfo info : services.values()) {
             fout.println("  " + info);
         }
     }
 
-    private boolean maybeRegisterForPackageChanges() {
+    public RegisteredServicesCacheListener<V> getListener() {
         synchronized (this) {
-            if (mReceiver == null) {
-                synchronized (this) {
-                    mReceiver = new BroadcastReceiver() {
-                        @Override
-                        public void onReceive(Context context, Intent intent) {
-                            mServices = generateServicesMap();
-                            RegisteredServicesCacheListener listener = mListener;
-                            if (listener != null) {
-                                listener.onRegisteredServicesCacheChanged();
-                            }
-                        }
-                    };
-                }
-
-                IntentFilter intentFilter = new IntentFilter();
-                intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-                intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-                intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-                intentFilter.addDataScheme("package");
-                mContext.registerReceiver(mReceiver, intentFilter);
-                return true;
-            }
-            return false;
+            return mListener;
         }
     }
 
-    private void maybeUnregisterForPackageChanges() {
-        synchronized (this) {
-            if (mReceiver != null) {
-                mContext.unregisterReceiver(mReceiver);
-                mReceiver = null;
-            }
+    public void setListener(RegisteredServicesCacheListener<V> listener, Handler handler) {
+        if (handler == null) {
+            handler = new Handler(mContext.getMainLooper());
         }
+        synchronized (this) {
+            mHandler = handler;
+            mListener = listener;
+        }
+    }
+
+    private void notifyListener(final V type, final boolean removed) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added"));
+        }
+        RegisteredServicesCacheListener<V> listener;
+        Handler handler; 
+        synchronized (this) {
+            listener = mListener;
+            handler = mHandler;
+        }
+        if (listener == null) {
+            return;
+        }
+        
+        final RegisteredServicesCacheListener<V> listener2 = listener;
+        handler.post(new Runnable() {
+            public void run() {
+                listener2.onServiceChanged(type, removed);
+            }
+        });
     }
 
     /**
@@ -140,7 +183,7 @@
 
         @Override
         public String toString() {
-            return "ServiceInfo: " + type + ", " + componentName;
+            return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid;
         }
     }
 
@@ -150,11 +193,9 @@
      * @return the AuthenticatorInfo that matches the account type or null if none is present
      */
     public ServiceInfo<V> getServiceInfo(V type) {
-        if (mServices == null) {
-            maybeRegisterForPackageChanges();
-            mServices = generateServicesMap();
+        synchronized (mServicesLock) {
+            return mServices.get(type);
         }
-        return mServices.get(type);
     }
 
     /**
@@ -162,54 +203,171 @@
      * registered authenticators.
      */
     public Collection<ServiceInfo<V>> getAllServices() {
-        if (mServices == null) {
-            maybeRegisterForPackageChanges();
-            mServices = generateServicesMap();
+        synchronized (mServicesLock) {
+            return Collections.unmodifiableCollection(mServices.values());
         }
-        return Collections.unmodifiableCollection(mServices.values());
     }
 
     /**
      * Stops the monitoring of package additions, removals and changes.
      */
     public void close() {
-        maybeUnregisterForPackageChanges();
+        final BroadcastReceiver receiver = mReceiver.getAndSet(null);
+        if (receiver != null) {
+            mContext.unregisterReceiver(receiver);
+        }
     }
 
     @Override
     protected void finalize() throws Throwable {
-        synchronized (this) {
-            if (mReceiver != null) {
-                Log.e(TAG, "RegisteredServicesCache finalized without being closed");
-            }
+        if (mReceiver.get() != null) {
+            Log.e(TAG, "RegisteredServicesCache finalized without being closed");
         }
         close();
         super.finalize();
     }
 
-    Map<V, ServiceInfo<V>> generateServicesMap() {
-        Map<V, ServiceInfo<V>> services = Maps.newHashMap();
+    private boolean inSystemImage(int callerUid) {
+        String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
+        for (String name : packages) {
+            try {
+                PackageInfo packageInfo =
+                        mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
+                if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                    return true;
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    void generateServicesMap() {
         PackageManager pm = mContext.getPackageManager();
-
-        List<ResolveInfo> resolveInfos =
-                pm.queryIntentServices(new Intent(mInterfaceName), PackageManager.GET_META_DATA);
-
+        ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();
+        List<ResolveInfo> resolveInfos = pm.queryIntentServices(new Intent(mInterfaceName),
+                PackageManager.GET_META_DATA);
         for (ResolveInfo resolveInfo : resolveInfos) {
             try {
                 ServiceInfo<V> info = parseServiceInfo(resolveInfo);
-                if (info != null) {
-                    services.put(info.type, info);
-                } else {
-                    Log.w(TAG, "Unable to load input method " + resolveInfo.toString());
+                if (info == null) {
+                    Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
+                    continue;
                 }
+                serviceInfos.add(info);
             } catch (XmlPullParserException e) {
-                Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
+                Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
             } catch (IOException e) {
-                Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
+                Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
             }
         }
 
-        return services;
+        synchronized (mServicesLock) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.d(TAG, "generateServicesMap: " + mInterfaceName);
+            }
+            if (mPersistentServices == null) {
+                readPersistentServicesLocked();
+            }
+            mServices = Maps.newHashMap();
+            boolean changed = false;
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.d(TAG, "found " + serviceInfos.size() + " services");
+            }
+            for (ServiceInfo<V> info : serviceInfos) {
+                // four cases:
+                // - doesn't exist yet
+                //   - add, notify user that it was added
+                // - exists and the UID is the same
+                //   - replace, don't notify user
+                // - exists, the UID is different, and the new one is not a system package
+                //   - ignore
+                // - exists, the UID is different, and the new one is a system package
+                //   - add, notify user that it was added
+                Integer previousUid = mPersistentServices.get(info.type);
+                if (previousUid == null) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.d(TAG, "encountered new type: " + info);
+                    }
+                    changed = true;
+                    mServices.put(info.type, info);
+                    mPersistentServices.put(info.type, info.uid);
+                    if (!mPersistentServicesFileDidNotExist) {
+                        notifyListener(info.type, false /* removed */);
+                    }
+                } else if (previousUid == info.uid) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.d(TAG, "encountered existing type with the same uid: " + info);
+                    }
+                    mServices.put(info.type, info);
+                } else if (inSystemImage(info.uid)
+                        || !containsTypeAndUid(serviceInfos, info.type, previousUid)) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        if (inSystemImage(info.uid)) {
+                            Log.d(TAG, "encountered existing type with a new uid but from"
+                                    + " the system: " + info);
+                        } else {
+                            Log.d(TAG, "encountered existing type with a new uid but existing was"
+                                    + " removed: " + info);
+                        }
+                    }
+                    changed = true;
+                    mServices.put(info.type, info);
+                    mPersistentServices.put(info.type, info.uid);
+                    notifyListener(info.type, false /* removed */);
+                } else {
+                    // ignore
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.d(TAG, "encountered existing type with a new uid, ignoring: " + info);
+                    }
+                }
+            }
+
+            ArrayList<V> toBeRemoved = Lists.newArrayList();
+            for (V v1 : mPersistentServices.keySet()) {
+                if (!containsType(serviceInfos, v1)) {
+                    toBeRemoved.add(v1);
+                }
+            }
+            for (V v1 : toBeRemoved) {
+                mPersistentServices.remove(v1);
+                changed = true;
+                notifyListener(v1, true /* removed */);
+            }
+            if (changed) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.d(TAG, "writing updated list of persistent services");
+                }
+                writePersistentServicesLocked();
+            } else {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.d(TAG, "persistent services did not change, so not writing anything");
+                }
+            }
+            mPersistentServicesFileDidNotExist = false;
+        }
+    }
+
+    private boolean containsType(ArrayList<ServiceInfo<V>> serviceInfos, V type) {
+        for (int i = 0, N = serviceInfos.size(); i < N; i++) {
+            if (serviceInfos.get(i).type.equals(type)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private boolean containsTypeAndUid(ArrayList<ServiceInfo<V>> serviceInfos, V type, int uid) {
+        for (int i = 0, N = serviceInfos.size(); i < N; i++) {
+            final ServiceInfo<V> serviceInfo = serviceInfos.get(i);
+            if (serviceInfo.type.equals(type) && serviceInfo.uid == uid) {
+                return true;
+            }
+        }
+
+        return false;
     }
 
     private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
@@ -252,5 +410,89 @@
         }
     }
 
+    /**
+     * Read all sync status back in to the initial engine state.
+     */
+    private void readPersistentServicesLocked() {
+        mPersistentServices = Maps.newHashMap();
+        if (mSerializerAndParser == null) {
+            return;
+        }
+        FileInputStream fis = null;
+        try {
+            mPersistentServicesFileDidNotExist = !mPersistentServicesFile.getBaseFile().exists();
+            if (mPersistentServicesFileDidNotExist) {
+                return;
+            }
+            fis = mPersistentServicesFile.openRead();
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, null);
+            int eventType = parser.getEventType();
+            while (eventType != XmlPullParser.START_TAG) {
+                eventType = parser.next();
+            }
+            String tagName = parser.getName();
+            if ("services".equals(tagName)) {
+                eventType = parser.next();
+                do {
+                    if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
+                        tagName = parser.getName();
+                        if ("service".equals(tagName)) {
+                            V service = mSerializerAndParser.createFromXml(parser);
+                            if (service == null) {
+                                break;
+                            }
+                            String uidString = parser.getAttributeValue(null, "uid");
+                            int uid = Integer.parseInt(uidString);
+                            mPersistentServices.put(service, uid);
+                        }
+                    }
+                    eventType = parser.next();
+                } while (eventType != XmlPullParser.END_DOCUMENT);
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "Error reading persistent services, starting from scratch", e);
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (java.io.IOException e1) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Write all sync status to the sync status file.
+     */
+    private void writePersistentServicesLocked() {
+        if (mSerializerAndParser == null) {
+            return;
+        }
+        FileOutputStream fos = null;
+        try {
+            fos = mPersistentServicesFile.startWrite();
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(fos, "utf-8");
+            out.startDocument(null, true);
+            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            out.startTag(null, "services");
+            for (Map.Entry<V, Integer> service : mPersistentServices.entrySet()) {
+                out.startTag(null, "service");
+                out.attribute(null, "uid", Integer.toString(service.getValue()));
+                mSerializerAndParser.writeAsXml(service.getKey(), out);
+                out.endTag(null, "service");
+            }
+            out.endTag(null, "services");
+            out.endDocument();
+            mPersistentServicesFile.finishWrite(fos);
+        } catch (java.io.IOException e1) {
+            Log.w(TAG, "Error writing accounts", e1);
+            if (fos != null) {
+                mPersistentServicesFile.failWrite(fos);
+            }
+        }
+    }
+
     public abstract V parseServiceAttributes(String packageName, AttributeSet attrs);
 }
diff --git a/core/java/android/content/pm/RegisteredServicesCacheListener.java b/core/java/android/content/pm/RegisteredServicesCacheListener.java
index c92c86e..2bc0942 100644
--- a/core/java/android/content/pm/RegisteredServicesCacheListener.java
+++ b/core/java/android/content/pm/RegisteredServicesCacheListener.java
@@ -1,12 +1,16 @@
 package android.content.pm;
 
+import android.os.Parcelable;
+
 /**
  * Listener for changes to the set of registered services managed by a RegisteredServicesCache.
  * @hide
  */
-public interface RegisteredServicesCacheListener {
+public interface RegisteredServicesCacheListener<V> {
     /**
-     * Invoked when the registered services cache changes.
+     * Invoked when a service is registered or changed.
+     * @param type the type of registered service
+     * @param removed true if the service was removed
      */
-    void onRegisteredServicesCacheChanged();
+    void onServiceChanged(V type, boolean removed);
 }
diff --git a/core/java/android/content/pm/XmlSerializerAndParser.java b/core/java/android/content/pm/XmlSerializerAndParser.java
new file mode 100644
index 0000000..33598f0
--- /dev/null
+++ b/core/java/android/content/pm/XmlSerializerAndParser.java
@@ -0,0 +1,14 @@
+package android.content.pm;
+
+import org.xmlpull.v1.XmlSerializer;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import android.os.Parcel;
+
+import java.io.IOException;
+
+/** @hide */
+public interface XmlSerializerAndParser<T> {
+    void writeAsXml(T item, XmlSerializer out) throws IOException;
+    T createFromXml(XmlPullParser parser) throws IOException, XmlPullParserException;
+}
diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java
index 9638262..f9dce25 100644
--- a/core/java/android/pim/vcard/VCardComposer.java
+++ b/core/java/android/pim/vcard/VCardComposer.java
@@ -41,6 +41,8 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.telephony.PhoneNumberUtils;
+import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 import android.text.format.Time;
 import android.util.CharsetUtils;
@@ -761,27 +763,58 @@
         }
     }
 
+    private boolean containsNonEmptyName(ContentValues contentValues) {
+        final String familyName = contentValues.getAsString(StructuredName.FAMILY_NAME);
+        final String middleName = contentValues.getAsString(StructuredName.MIDDLE_NAME);
+        final String givenName = contentValues.getAsString(StructuredName.GIVEN_NAME);
+        final String prefix = contentValues.getAsString(StructuredName.PREFIX);
+        final String suffix = contentValues.getAsString(StructuredName.SUFFIX);
+        final String displayName = contentValues.getAsString(StructuredName.DISPLAY_NAME);
+        return !(TextUtils.isEmpty(familyName) && TextUtils.isEmpty(middleName) &&
+                TextUtils.isEmpty(givenName) && TextUtils.isEmpty(prefix) &&
+                TextUtils.isEmpty(suffix) && TextUtils.isEmpty(displayName));
+    }
+
     private void appendStructuredNamesInternal(final StringBuilder builder,
             final List<ContentValues> contentValuesList) {
         // For safety, we'll emit just one value around StructuredName, as external importers
         // may get confused with multiple "N", "FN", etc. properties, though it is valid in
         // vCard spec.
         ContentValues primaryContentValues = null;
+        ContentValues subprimaryContentValues = null;
         for (ContentValues contentValues : contentValuesList) {
+            if (contentValues == null){
+                continue;
+            }
             Integer isSuperPrimary = contentValues.getAsInteger(StructuredName.IS_SUPER_PRIMARY);
-            if (isSuperPrimary != null && isSuperPrimary != 0) {
+            if (isSuperPrimary != null && isSuperPrimary > 0) {
                 // We choose "super primary" ContentValues.
                 primaryContentValues = contentValues;
                 break;
-            } else if (primaryContentValues == null && contentValues != null) {
-                // We choose the first ContentValues if "super primary" ContentValues does not exist.
-                primaryContentValues = contentValues;
+            } else if (primaryContentValues == null) {
+                // We choose the first "primary" ContentValues
+                // if "super primary" ContentValues does not exist.
+                Integer isPrimary = contentValues.getAsInteger(StructuredName.IS_PRIMARY);
+                if (isPrimary != null && isPrimary > 0 &&
+                        containsNonEmptyName(contentValues)) {
+                    primaryContentValues = contentValues;
+                    // Do not break, since there may be ContentValues with "super primary"
+                    // afterword.
+                } else if (subprimaryContentValues == null &&
+                        containsNonEmptyName(contentValues)) {
+                    subprimaryContentValues = contentValues;
+                }
             }
         }
 
         if (primaryContentValues == null) {
-            Log.e(LOG_TAG, "All ContentValues given from database is empty.");
-            primaryContentValues = new ContentValues();
+            if (subprimaryContentValues != null) {
+                // We choose the first ContentValues if any "primary" ContentValues does not exist.
+                primaryContentValues = subprimaryContentValues;
+            } else {
+                Log.e(LOG_TAG, "All ContentValues given from database is empty.");
+                primaryContentValues = new ContentValues();
+            }
         }
 
         final String familyName = primaryContentValues
@@ -1140,13 +1173,39 @@
                 if (TextUtils.isEmpty(phoneNumber)) {
                     continue;
                 }
-                phoneLineExists = true;
                 int type = (typeAsObject != null ? typeAsObject : Phone.TYPE_HOME);
-                // TODO: Premature, since this allows two phone numbers which are
-                //        same from the view of phone number format (e.g. "100" v.s. "1-0-0")
-                if (!phoneSet.contains(phoneNumber)) {
-                    phoneSet.add(phoneNumber);
-                    appendVCardTelephoneLine(builder, type, label, phoneNumber);
+
+                phoneLineExists = true;
+                if (type == Phone.TYPE_PAGER) {
+                    phoneLineExists = true;
+                    if (!phoneSet.contains(phoneNumber)) {
+                        phoneSet.add(phoneNumber);
+                        appendVCardTelephoneLine(builder, type, label, phoneNumber);
+                    }
+                } else {
+                    // The entry "may" have several phone numbers when the contact entry is
+                    // corrupted because of its original source.
+                    //
+                    // e.g. I encountered the entry like the following.
+                    // "111-222-3333 (Miami)\n444-555-6666 (Broward; 305-653-6796 (Miami); ..."
+                    // This kind of entry is not able to be inserted via Android devices, but
+                    // possible if the source of the data is already corrupted.
+                    List<String> phoneNumberList = splitIfSeveralPhoneNumbersExist(phoneNumber);
+                    if (phoneNumberList.isEmpty()) {
+                        continue;
+                    }
+                    phoneLineExists = true;
+                    for (String actualPhoneNumber : phoneNumberList) {
+                        if (!phoneSet.contains(actualPhoneNumber)) {
+                            final int format = VCardUtils.getPhoneNumberFormat(mVCardType);
+                            SpannableStringBuilder tmpBuilder =
+                                new SpannableStringBuilder(actualPhoneNumber);
+                            PhoneNumberUtils.formatNumber(tmpBuilder, format);
+                            final String formattedPhoneNumber = tmpBuilder.toString();
+                            phoneSet.add(actualPhoneNumber);
+                            appendVCardTelephoneLine(builder, type, label, formattedPhoneNumber);
+                        }
+                    }
                 }
             }
         }
@@ -1156,6 +1215,27 @@
         }
     }
 
+    private List<String> splitIfSeveralPhoneNumbersExist(final String phoneNumber) {
+        List<String> phoneList = new ArrayList<String>();
+
+        StringBuilder builder = new StringBuilder();
+        final int length = phoneNumber.length();
+        for (int i = 0; i < length; i++) {
+            final char ch = phoneNumber.charAt(i);
+            if (Character.isDigit(ch)) {
+                builder.append(ch);
+            } else if ((ch == ';' || ch == '\n') && builder.length() > 0) {
+                phoneList.add(builder.toString());
+                builder = new StringBuilder();
+            }
+        }
+        if (builder.length() > 0) {
+            phoneList.add(builder.toString());
+        }
+
+        return phoneList;
+    }
+
     private void appendEmails(final StringBuilder builder,
             final Map<String, List<ContentValues>> contentValuesListMap) {
         final List<ContentValues> contentValuesList = contentValuesListMap
diff --git a/core/java/android/pim/vcard/VCardUtils.java b/core/java/android/pim/vcard/VCardUtils.java
index 4f50103..dd44288 100644
--- a/core/java/android/pim/vcard/VCardUtils.java
+++ b/core/java/android/pim/vcard/VCardUtils.java
@@ -20,6 +20,7 @@
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 
 import java.util.Collection;
@@ -168,6 +169,14 @@
         return list;
     }
 
+    public static int getPhoneNumberFormat(final int vcardType) {
+        if (VCardConfig.isJapaneseDevice(vcardType)) {
+            return PhoneNumberUtils.FORMAT_JAPAN;
+        } else {
+            return PhoneNumberUtils.FORMAT_NANP;
+        }
+    }
+
     /**
      * Inserts postal data into the builder object.
      * 
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index bbd9dde..37e6133 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -16,11 +16,14 @@
 
 package android.service.wallpaper;
 
+import android.view.MotionEvent;
+
 /**
  * @hide
  */
 oneway interface IWallpaperEngine {
     void setDesiredSize(int width, int height);
     void setVisibility(boolean visible);
+    void dispatchPointer(in MotionEvent event);
 	void destroy();
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index e79832b..b29d837 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -746,6 +746,12 @@
             mCaller.sendMessage(msg);
         }
 
+        public void dispatchPointer(MotionEvent event) {
+            if (mEngine != null) {
+                mEngine.mWindow.onDispatchPointer(event, event.getEventTime(), false);
+            }
+        }
+        
         public void destroy() {
             Message msg = mCaller.obtainMessage(DO_DETACH);
             mCaller.sendMessage(msg);
@@ -805,6 +811,7 @@
                             mEngine.mPendingMove = null;
                         }
                     }
+                    if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev);
                     mEngine.onTouchEvent(ev);
                     ev.recycle();
                 } break;
diff --git a/docs/html/sdk/adding-components.jd b/docs/html/sdk/adding-components.jd
index 967c3521..bc82170 100644
--- a/docs/html/sdk/adding-components.jd
+++ b/docs/html/sdk/adding-components.jd
@@ -54,8 +54,13 @@
 
 <h2 id="InstallingComponents">Installing SDK Components</h2>
 
-<p>Use the Android SDK and AVD Manager to install new SDK components. 
-You can launch the SDK and AVD Manager in one of these ways:</p>
+<p>Use the Android SDK and AVD Manager to install new SDK components. </p>
+
+<p class="caution"><strong>Important:</strong> Before you install SDK components, 
+we recommend that you disable any antivirus programs that may be running on
+your computer.</p>
+
+<p>You can launch the SDK and AVD Manager in one of these ways:</p>
 <ul>
   <li>Execute the {@code android}</a> tool command with no options. If you
 haven't used the tool before, change to the <code>&lt;sdk&gt;/tools</code>
diff --git a/docs/html/sdk/installing.jd b/docs/html/sdk/installing.jd
index edc77e8..261b49f 100644
--- a/docs/html/sdk/installing.jd
+++ b/docs/html/sdk/installing.jd
@@ -26,8 +26,8 @@
 </div>
 </div>
 
-<p>This page describes how to install the Android SDK and set up your
-development environment for the first time.</p>
+<p>This page describes how to install the latest version of the Android SDK 
+and set up your development environment for the first time.</p>
 
 <p>If you encounter any problems during installation, see the 
 <a href="#troubleshooting">Troubleshooting</a> section at the bottom of
@@ -36,14 +36,14 @@
 <h4>Updating?</h4>
 
 <p>If you are currently using the Android 1.6 SDK, you do not necessarily need
-to install the new SDK, since your existing SDK already includes the Android SDK
-and AVD Manager tool. To develop against the new Android 2.0 platform, for
-example, you could just download the updated SDK Tools (Revision 3) and the
-Android 2.0 platform into your existing SDK.</p>
+to install a newer SDK, since you can already update the platforms, tools, and
+other components using the Android SDK and AVD Manager tool. To develop against
+the latest Android platform, for example, you could just download the latest SDK
+Tools and then add the new Android platform into your existing SDK.</p>
 
 <p>If you are using Android 1.5 SDK or older, you should install the new SDK as
 described in this document and move your application projects to the new
-environment. </p>
+SDK environment. </p>
 
 <h2 id="Preparing">Prepare for Installation</h2>
 
@@ -65,13 +65,15 @@
 <h2 id="Installing">Download and Install the SDK</h2>
 
 <p>Download the SDK package that is appropriate for your development computer.
-Unpack the Android SDK archive to a suitable location on your machine. By
-default, the SDK files are unpacked into a directory named
-<code>android-sdk-&lt;machine-platform&gt;</code>. </p>
+You can get the latest version from the <a href="{@docRoot}sdk/index.html">SDK
+download page</a>.</p>
 
-<p>Make a note of the name and location of the unpacked SDK directory on your
-system &mdash; you will need to refer to the SDK directory later, when setting
-up the ADT plugin or when using the SDK tools.</p>
+<p>After downloading, unpack the Android SDK archive to a suitable location on your
+machine. By default, the SDK files are unpacked into a directory named
+<code>android-sdk-&lt;machine-platform&gt;</code>. Make a note of the name and
+location of the unpacked SDK directory on your system &mdash; you will need to
+refer to the SDK directory later, when setting up the ADT plugin or when using
+the SDK tools.</p>
 
 <p>Optionally, you may want to add the location of the SDK's primary
 <code>tools</code> directory to your system <code>PATH</code>. The primary
@@ -128,7 +130,7 @@
 
 <h2 id="components">Add Android Platforms and Other Components</h2>
 
-<div class="sidebox-wrapper">
+<div class="sidebox-wrapper" style="margin-right:2.5em;">
 <div class="sidebox">
 <p>The <strong>Android SDK and AVD Manager</strong> tool is pre-installed in
 your SDK. Using the tool is a key part of performing the initial setup of your
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index 9a06d13..dbee451 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -40,11 +40,13 @@
     return err;
 }
 
+// Warning: The filedescriptor passed into this method will only be valid until
+// the method returns, if you want to keep it, dup it!
 status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
     LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
 
     reset();
-    mPlayer = new MediaPlayerImpl(fd, offset, length);
+    mPlayer = new MediaPlayerImpl(dup(fd), offset, length);
 
     status_t err = mPlayer->initCheck();
     if (err != OK) {
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 6908220..246f9fc 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -19,6 +19,7 @@
     
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <application>    
         <uses-library android:name="android.test.runner" />
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
index 5e830a8..9fb49b1 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
@@ -42,6 +42,7 @@
 
 import java.io.File;
 import java.io.FileDescriptor;
+import java.net.InetAddress;
 
  
 public class MediaFrameworkTest extends Activity {
@@ -142,4 +143,9 @@
       return super.onKeyDown(keyCode, event);
      
   }  
+
+  public static boolean checkStreamingServer() throws Exception {
+      InetAddress address = InetAddress.getByAddress(MediaNames.STREAM_SERVER);
+      return address.isReachable(10000);
+  }
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index 3b69df898..5127255 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -489,6 +489,7 @@
   };
   
   //Streaming test files
+  public static final byte [] STREAM_SERVER = new byte[] {(byte)75,(byte)17,(byte)48,(byte)204};
   public static final String STREAM_H264_480_360_1411k = 
       "http://75.17.48.204:10088/yslau/stress_media/h264_regular.mp4";
   public static final String STREAM_WMV = 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
index 5725c44..4e30f91 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
@@ -439,6 +439,7 @@
     
     @MediumTest
     public void testPrepareAsyncReset() throws Exception {
+      assertTrue(MediaFrameworkTest.checkStreamingServer());
       boolean isReset = CodecTest.prepareAsyncReset(MediaNames.STREAM_MP3);
       assertTrue("PrepareAsync Reset", isReset);         
     }
@@ -471,6 +472,7 @@
     
     @LargeTest
     public void testStreamPrepareAsyncCallback() throws Exception {
+        assertTrue(MediaFrameworkTest.checkStreamingServer());
         boolean onPrepareSuccess = 
             CodecTest.prepareAsyncCallback(MediaNames.STREAM_H264_480_360_1411k, false);
         assertTrue("StreamH264PrepareAsyncCallback", onPrepareSuccess);
@@ -478,6 +480,7 @@
     
     @LargeTest
     public void testStreamPrepareAsyncCallbackReset() throws Exception {
+        assertTrue(MediaFrameworkTest.checkStreamingServer());
         boolean onPrepareSuccess = 
             CodecTest.prepareAsyncCallback(MediaNames.STREAM_H264_480_360_1411k, true);
         assertTrue("StreamH264PrepareAsyncCallback", onPrepareSuccess);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
index fdb43da..b476e01 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
@@ -64,6 +64,7 @@
 
         mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
         try {
+            assertTrue(MediaFrameworkTest.checkStreamingServer());
             for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) {
                 MediaPlayer mp = new MediaPlayer();
                 mp.setDataSource(MediaNames.STREAM_H264_480_360_1411k);
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java
index 65663b1..05b498c 100644
--- a/obex/javax/obex/ClientOperation.java
+++ b/obex/javax/obex/ClientOperation.java
@@ -269,7 +269,7 @@
 
         if (mPrivateOutput == null) {
             // there are 3 bytes operation headers and 3 bytes body headers //
-            mPrivateOutput = new PrivateOutputStream(this, mMaxPacketSize - 6);
+            mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
         }
 
         mPrivateOutputOpen = true;
@@ -278,7 +278,13 @@
     }
 
     public int getMaxPacketSize() {
-        return mMaxPacketSize - 6;
+        return mMaxPacketSize - 6 - getHeaderLength();
+    }
+
+    public int getHeaderLength() {
+        // OPP may need it
+        byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
+        return headerArray.length;
     }
 
     /**
diff --git a/obex/javax/obex/Operation.java b/obex/javax/obex/Operation.java
index 20653f2..25656ed 100644
--- a/obex/javax/obex/Operation.java
+++ b/obex/javax/obex/Operation.java
@@ -163,6 +163,8 @@
 
     long getLength();
 
+    int getHeaderLength();
+
     String getType();
 
     InputStream openInputStream() throws IOException;
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
index 504fe35..07a3a53 100644
--- a/obex/javax/obex/ServerOperation.java
+++ b/obex/javax/obex/ServerOperation.java
@@ -124,21 +124,30 @@
              * It is a PUT request.
              */
             mGetOperation = false;
-        } else {
+
+            /*
+             * Determine if the final bit is set
+             */
+            if ((request & 0x80) == 0) {
+                finalBitSet = false;
+            } else {
+                finalBitSet = true;
+                mRequestFinished = true;
+            }
+        } else if ((request == 0x03) || (request == 0x83)) {
             /*
              * It is a GET request.
              */
             mGetOperation = true;
-        }
 
-        /*
-         * Determine if the final bit is set
-         */
-        if ((request & 0x80) == 0) {
+            // For Get request, final bit set is decided by server side logic
             finalBitSet = false;
+
+            if (request == 0x83) {
+                mRequestFinished = true;
+            }
         } else {
-            finalBitSet = true;
-            mRequestFinished = true;
+            throw new IOException("ServerOperation can not handle such request");
         }
 
         int length = in.read();
@@ -216,12 +225,9 @@
         }
 
         // wait for get request finished !!!!
-        while (mGetOperation && !finalBitSet) {
+        while (mGetOperation && !mRequestFinished) {
             sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
         }
-        if (finalBitSet && mGetOperation) {
-            mRequestFinished = true;
-        }
     }
 
     public boolean isValidBody() {
@@ -333,6 +339,12 @@
             out.write(headerArray);
         }
 
+        // For Get operation: if response code is OBEX_HTTP_OK, then this is the
+        // last packet; so set finalBitSet to true.
+        if (mGetOperation && type == ResponseCodes.OBEX_HTTP_OK) {
+            finalBitSet = true;
+        }
+
         if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) {
             if (bodyLength > 0) {
                 /*
@@ -410,9 +422,10 @@
                 }
             } else {
 
-                if ((headerID == ObexHelper.OBEX_OPCODE_PUT_FINAL)
-                        || (headerID == ObexHelper.OBEX_OPCODE_GET_FINAL)) {
+                if ((headerID == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
                     finalBitSet = true;
+                } else if (headerID == ObexHelper.OBEX_OPCODE_GET_FINAL) {
+                    mRequestFinished = true;
                 }
 
                 /*
@@ -584,7 +597,20 @@
     }
 
     public int getMaxPacketSize() {
-        return mMaxPacketLength - 6;
+        return mMaxPacketLength - 6 - getHeaderLength();
+    }
+
+    public int getHeaderLength() {
+        long id = mListener.getConnectionId();
+        if (id == -1) {
+            replyHeader.mConnectionID = null;
+        } else {
+            replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
+        }
+
+        byte[] headerArray = ObexHelper.createHeader(replyHeader, false);
+
+        return headerArray.length;
     }
 
     /**
@@ -623,7 +649,7 @@
         }
 
         if (mPrivateOutput == null) {
-            mPrivateOutput = new PrivateOutputStream(this, mMaxPacketLength - 6);
+            mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
         }
         mPrivateOutputOpen = true;
         return mPrivateOutput;
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 0330163..9ca57ba 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -18,7 +18,6 @@
 
 import java.io.Writer;
 import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
 
 import javax.microedition.khronos.egl.EGL10;
 import javax.microedition.khronos.egl.EGL11;
@@ -943,6 +942,9 @@
      * to a Renderer instance to do the actual drawing. Can be configured to
      * render continuously or on request.
      *
+     * All potentially blocking synchronization is done through the
+     * sGLThreadManager object. This avoids multiple-lock ordering issues.
+     *
      */
     class GLThread extends Thread {
         GLThread(Renderer renderer) {
@@ -962,51 +964,31 @@
                 Log.i("GLThread", "starting tid=" + getId());
             }
 
-            /*
-             * When the android framework launches a second instance of
-             * an activity, the new instance's onCreate() method may be
-             * called before the first instance returns from onDestroy().
-             *
-             * This semaphore ensures that only one instance at a time
-             * accesses EGL.
-             */
             try {
                 guardedRun();
             } catch (InterruptedException e) {
                 // fall thru and exit normally
             } finally {
-                synchronized(this) {
-                    if (LOG_THREADS) {
-                        Log.i("GLThread", "exiting tid=" +  getId());
-                    }
-                    mDone = true;
-                    notifyAll();
-                }
+                sGLThreadManager.threadExiting(this);
             }
         }
 
-        private void startEgl() throws InterruptedException {
-            if (! mHaveEgl) {
-                mHaveEgl = true;
-                sGLThreadManager.start(this);
-                mEglHelper.start();
-            }
-        }
-
-        private void stopEgl() {
+        /*
+         * This private method should only be called inside a
+         * synchronized(sGLThreadManager) block.
+         */
+        private void stopEglLocked() {
             if (mHaveEgl) {
                 mHaveEgl = false;
                 mEglHelper.destroySurface();
                 mEglHelper.finish();
-                sGLThreadManager.end(this);
+                sGLThreadManager.releaseEglSurface(this);
             }
         }
 
         private void guardedRun() throws InterruptedException {
             mEglHelper = new EglHelper();
             try {
-                startEgl();
-
                 GL10 gl = null;
                 boolean tellRendererSurfaceCreated = true;
                 boolean tellRendererSurfaceChanged = true;
@@ -1015,63 +997,97 @@
                  * This is our main activity thread's loop, we go until
                  * asked to quit.
                  */
-                while (!mDone) {
-
+                while (!isDone()) {
                     /*
                      *  Update the asynchronous state (window size)
                      */
-                    int w, h;
-                    boolean changed;
+                    int w = 0;
+                    int h = 0;
+                    boolean changed = false;
                     boolean needStart = false;
-                    synchronized (this) {
+                    boolean eventsWaiting = false;
+
+                    synchronized (sGLThreadManager) {
+                        while (true) {
+                            // Manage acquiring and releasing the SurfaceView
+                            // surface and the EGL surface.
+                            if (mPaused) {
+                                stopEglLocked();
+                            }
+                            if (!mHasSurface) {
+                                if (!mWaitingForSurface) {
+                                    stopEglLocked();
+                                    mWaitingForSurface = true;
+                                    sGLThreadManager.notifyAll();
+                                }
+                            } else {
+                                if (!mHaveEgl) {
+                                    if (sGLThreadManager.tryAcquireEglSurface(this)) {
+                                        mHaveEgl = true;
+                                        mEglHelper.start();
+                                        mRequestRender = true;
+                                        needStart = true;
+                                    }
+                                }
+                            }
+
+                            // Check if we need to wait. If not, update any state
+                            // that needs to be updated, copy any state that
+                            // needs to be copied, and use "break" to exit the
+                            // wait loop.
+
+                            if (mDone) {
+                                return;
+                            }
+
+                            if (mEventsWaiting) {
+                                eventsWaiting = true;
+                                mEventsWaiting = false;
+                                break;
+                            }
+
+                            if ( (! mPaused) && mHasSurface && mHaveEgl
+                                    && (mWidth > 0) && (mHeight > 0)
+                                    && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))
+                                    ) {
+                                changed = mSizeChanged;
+                                w = mWidth;
+                                h = mHeight;
+                                mSizeChanged = false;
+                                mRequestRender = false;
+                                if (mHasSurface && mWaitingForSurface) {
+                                    changed = true;
+                                    mWaitingForSurface = false;
+                                    sGLThreadManager.notifyAll();
+                                }
+                                break;
+                            }
+
+                            // By design, this is the only place where we wait().
+
+                            if (LOG_THREADS) {
+                                Log.i("GLThread", "waiting tid=" + getId());
+                            }
+                            sGLThreadManager.wait();
+                        }
+                    } // end of synchronized(sGLThreadManager)
+
+                    /*
+                     * Handle queued events
+                     */
+                    if (eventsWaiting) {
                         Runnable r;
                         while ((r = getEvent()) != null) {
                             r.run();
-                        }
-                        if (mPaused) {
-                            stopEgl();
-                            needStart = true;
-                        }
-                        while(true) {
-                            if (!mHasSurface) {
-                                if (!mWaitingForSurface) {
-                                    stopEgl();
-                                    mWaitingForSurface = true;
-                                    notifyAll();
-                                }
-                            } else {
-                                boolean shouldHaveEgl = sGLThreadManager.shouldHaveEgl(this);
-                                if (mHaveEgl && (!shouldHaveEgl)) {
-                                    stopEgl();
-                                } else if ((!mHaveEgl) && shouldHaveEgl) {
-                                    startEgl();
-                                    needStart = true;
-                                }
+                            if (isDone()) {
+                                return;
                             }
-                            if (!needToWait()) {
-                                break;
-                            }
-                            if (LOG_THREADS) {
-                                Log.i("GLThread", "needToWait tid=" + getId());
-                            }
-                            wait();
                         }
-                        if (mDone) {
-                            break;
-                        }
-                        changed = mSizeChanged;
-                        w = mWidth;
-                        h = mHeight;
-                        mSizeChanged = false;
-                        mRequestRender = false;
-                        if (mHasSurface && mWaitingForSurface) {
-                            changed = true;
-                            mWaitingForSurface = false;
-                            notifyAll();
-                        }
+                        // Go back and see if we need to wait to render.
+                        continue;
                     }
+
                     if (needStart) {
-                        startEgl();
                         tellRendererSurfaceCreated = true;
                         changed = true;
                     }
@@ -1102,71 +1118,63 @@
                 /*
                  * clean-up everything...
                  */
-                stopEgl();
+                synchronized (sGLThreadManager) {
+                    stopEglLocked();
+                }
             }
         }
 
-        private boolean needToWait() {
-            if (mDone) {
-                return false;
+        private boolean isDone() {
+            synchronized (sGLThreadManager) {
+                return mDone;
             }
-
-            if (mPaused || (! mHasSurface) || (! mHaveEgl)) {
-                return true;
-            }
-
-            if ((mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
-                return false;
-            }
-
-            return true;
         }
 
         public void setRenderMode(int renderMode) {
             if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
                 throw new IllegalArgumentException("renderMode");
             }
-            synchronized(this) {
+            synchronized(sGLThreadManager) {
                 mRenderMode = renderMode;
                 if (renderMode == RENDERMODE_CONTINUOUSLY) {
-                    notifyAll();
+                    sGLThreadManager.notifyAll();
                 }
             }
         }
 
         public int getRenderMode() {
-            synchronized(this) {
+            synchronized(sGLThreadManager) {
                 return mRenderMode;
             }
         }
 
         public void requestRender() {
-            synchronized(this) {
+            synchronized(sGLThreadManager) {
                 mRequestRender = true;
-                notifyAll();
+                sGLThreadManager.notifyAll();
             }
         }
 
         public void surfaceCreated() {
-            synchronized(this) {
+            synchronized(sGLThreadManager) {
                 if (LOG_THREADS) {
                     Log.i("GLThread", "surfaceCreated tid=" + getId());
                 }
                 mHasSurface = true;
-                notifyAll();
+                sGLThreadManager.notifyAll();
             }
         }
 
         public void surfaceDestroyed() {
-            synchronized(this) {
+            synchronized(sGLThreadManager) {
                 if (LOG_THREADS) {
                     Log.i("GLThread", "surfaceDestroyed tid=" + getId());
                 }
                 mHasSurface = false;
-                notifyAll();
+                sGLThreadManager.notifyAll();
                 while(!mWaitingForSurface && isAlive() && ! mDone) {
                     try {
-                        wait();
+                        sGLThreadManager.wait();
                     } catch (InterruptedException e) {
                         Thread.currentThread().interrupt();
                     }
@@ -1175,35 +1183,35 @@
         }
 
         public void onPause() {
-            synchronized (this) {
+            synchronized (sGLThreadManager) {
                 mPaused = true;
-                notifyAll();
+                sGLThreadManager.notifyAll();
             }
         }
 
         public void onResume() {
-            synchronized (this) {
+            synchronized (sGLThreadManager) {
                 mPaused = false;
                 mRequestRender = true;
-                notifyAll();
+                sGLThreadManager.notifyAll();
             }
         }
 
         public void onWindowResize(int w, int h) {
-            synchronized (this) {
+            synchronized (sGLThreadManager) {
                 mWidth = w;
                 mHeight = h;
                 mSizeChanged = true;
-                notifyAll();
+                sGLThreadManager.notifyAll();
             }
         }
 
         public void requestExitAndWait() {
             // don't call this from GLThread thread or it is a guaranteed
             // deadlock!
-            synchronized(this) {
+            synchronized(sGLThreadManager) {
                 mDone = true;
-                notifyAll();
+                sGLThreadManager.notifyAll();
             }
             try {
                 join();
@@ -1219,6 +1227,10 @@
         public void queueEvent(Runnable r) {
             synchronized(this) {
                 mEventQueue.add(r);
+                synchronized(sGLThreadManager) {
+                    mEventsWaiting = true;
+                    sGLThreadManager.notifyAll();
+                }
             }
         }
 
@@ -1232,6 +1244,8 @@
             return null;
         }
 
+        // Once the thread is started, all accesses to the following member
+        // variables are protected by the sGLThreadManager monitor
         private boolean mDone;
         private boolean mPaused;
         private boolean mHasSurface;
@@ -1241,6 +1255,9 @@
         private int mHeight;
         private int mRenderMode;
         private boolean mRequestRender;
+        private boolean mEventsWaiting;
+        // End of member variables protected by the sGLThreadManager monitor.
+
         private Renderer mRenderer;
         private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
         private EglHelper mEglHelper;
@@ -1286,37 +1303,43 @@
         }
     }
 
-    static class GLThreadManager {
-        public boolean shouldHaveEgl(GLThread thread) {
-            synchronized(this) {
-                return thread == mMostRecentGLThread || mMostRecentGLThread == null;
+    private static class GLThreadManager {
+
+        public synchronized void threadExiting(GLThread thread) {
+            if (LOG_THREADS) {
+                Log.i("GLThread", "exiting tid=" +  thread.getId());
             }
+            thread.mDone = true;
+            if (mEglOwner == thread) {
+                mEglOwner = null;
+            }
+            notifyAll();
         }
-        public void start(GLThread thread) throws InterruptedException {
-            GLThread oldThread = null;
-            synchronized(this) {
-                oldThread = mMostRecentGLThread;
-                mMostRecentGLThread = thread;
+
+        /*
+         * Tries once to acquire the right to use an EGL
+         * surface. Does not block.
+         * @return true if the right to use an EGL surface was acquired.
+         */
+        public synchronized boolean tryAcquireEglSurface(GLThread thread) {
+            if (mEglOwner == thread || mEglOwner == null) {
+                mEglOwner = thread;
+                notifyAll();
+                return true;
             }
-            if (oldThread != null) {
-                synchronized(oldThread) {
-                    oldThread.notifyAll();
-                }
-            }
-            sEglSemaphore.acquire();
+            return false;
         }
-        public void end(GLThread thread) {
-            sEglSemaphore.release();
-            synchronized(this) {
-                if (mMostRecentGLThread == thread) {
-                    mMostRecentGLThread = null;
-                }
+
+        public synchronized void releaseEglSurface(GLThread thread) {
+            if (mEglOwner == thread) {
+                mEglOwner = null;
             }
+            notifyAll();
         }
-        private GLThread mMostRecentGLThread;
+
+        private GLThread mEglOwner;
     }
 
-    private static final Semaphore sEglSemaphore = new Semaphore(1);
     private static final GLThreadManager sGLThreadManager = new GLThreadManager();
     private boolean mSizeChanged = true;
 
diff --git a/services/java/com/android/server/HardwareService.java b/services/java/com/android/server/HardwareService.java
index 3e3cf06..88074c2 100755
--- a/services/java/com/android/server/HardwareService.java
+++ b/services/java/com/android/server/HardwareService.java
@@ -54,6 +54,16 @@
     static final int LIGHT_FLASH_TIMED = 1;
     static final int LIGHT_FLASH_HARDWARE = 2;
 
+    /**
+     * Light brightness is managed by a user setting.
+     */
+    static final int BRIGHTNESS_MODE_USER = 0;
+
+    /**
+     * Light brightness is managed by a light sensor.
+     */
+    static final int BRIGHTNESS_MODE_SENSOR = 1;
+
     private final LinkedList<Vibration> mVibrations;
     private Vibration mCurrentVibration;
 
@@ -266,21 +276,21 @@
     }
 
     void setLightOff_UNCHECKED(int light) {
-        setLight_native(mNativePointer, light, 0, LIGHT_FLASH_NONE, 0, 0);
+        setLight_native(mNativePointer, light, 0, LIGHT_FLASH_NONE, 0, 0, 0);
     }
 
-    void setLightBrightness_UNCHECKED(int light, int brightness) {
+    void setLightBrightness_UNCHECKED(int light, int brightness, int brightnessMode) {
         int b = brightness & 0x000000ff;
         b = 0xff000000 | (b << 16) | (b << 8) | b;
-        setLight_native(mNativePointer, light, b, LIGHT_FLASH_NONE, 0, 0);
+        setLight_native(mNativePointer, light, b, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
     }
 
     void setLightColor_UNCHECKED(int light, int color) {
-        setLight_native(mNativePointer, light, color, LIGHT_FLASH_NONE, 0, 0);
+        setLight_native(mNativePointer, light, color, LIGHT_FLASH_NONE, 0, 0, 0);
     }
 
     void setLightFlashing_UNCHECKED(int light, int color, int mode, int onMS, int offMS) {
-        setLight_native(mNativePointer, light, color, mode, onMS, offMS);
+        setLight_native(mNativePointer, light, color, mode, onMS, offMS, 0);
     }
 
     public void setAttentionLight(boolean on, int color) {
@@ -289,7 +299,7 @@
             mAttentionLightOn = on;
             mPulsing = false;
             setLight_native(mNativePointer, LIGHT_ID_ATTENTION, color,
-                    LIGHT_FLASH_HARDWARE, on ? 3 : 0, 0);
+                    LIGHT_FLASH_HARDWARE, on ? 3 : 0, 0, 0);
         }
     }
 
@@ -304,7 +314,7 @@
             if (!mAttentionLightOn && !mPulsing) {
                 mPulsing = true;
                 setLight_native(mNativePointer, LIGHT_ID_ATTENTION, 0x00ffffff,
-                        LIGHT_FLASH_HARDWARE, 7, 0);
+                        LIGHT_FLASH_HARDWARE, 7, 0, 0);
                 mH.sendMessageDelayed(Message.obtain(mH, 1), 3000);
             }
         }
@@ -321,7 +331,7 @@
                     mPulsing = false;
                     setLight_native(mNativePointer, LIGHT_ID_ATTENTION,
                             mAttentionLightOn ? 0xffffffff : 0,
-                            LIGHT_FLASH_NONE, 0, 0);
+                            LIGHT_FLASH_NONE, 0, 0, 0);
                 }
             }
         }
@@ -484,7 +494,7 @@
     private static native void finalize_native(int ptr);
 
     private static native void setLight_native(int ptr, int light, int color, int mode,
-            int onMS, int offMS);
+            int onMS, int offMS, int brightnessMode);
 
     private final Context mContext;
     private final PowerManager.WakeLock mWakeLock;
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index fd518c3..e1425d4 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -158,12 +158,13 @@
     private int[] mBroadcastQueue = new int[] { -1, -1, -1 };
     private int[] mBroadcastWhy = new int[3];
     private int mPartialCount = 0;
-    private int mProximityCount = 0;
     private int mPowerState;
     private boolean mOffBecauseOfUser;
     private int mUserState;
     private boolean mKeyboardVisible = false;
     private boolean mUserActivityAllowed = true;
+    private int mProximityWakeLockCount = 0;
+    private boolean mProximitySensorEnabled = false;
     private boolean mProximitySensorActive = false;
     private int mProximityPendingValue = -1; // -1 == nothing, 0 == inactive, 1 == active
     private long mLastProximityEventTime;
@@ -235,6 +236,7 @@
 
     // could be either static or controllable at runtime
     private static final boolean mSpew = false;
+    private static final boolean mDebugProximitySensor = (true || mSpew);
     private static final boolean mDebugLightSensor = (false || mSpew);
 
     /*
@@ -657,8 +659,8 @@
             }
             Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME);
         } else if ((flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
-            mProximityCount++;
-            if (mProximityCount == 1) {
+            mProximityWakeLockCount++;
+            if (mProximityWakeLockCount == 1) {
                 enableProximityLockLocked();
             }
         }
@@ -718,9 +720,16 @@
                 Power.releaseWakeLock(PARTIAL_NAME);
             }
         } else if ((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
-            mProximityCount--;
-            if (mProximityCount == 0) {
-                disableProximityLockLocked();
+            mProximityWakeLockCount--;
+            if (mProximityWakeLockCount == 0) {
+                if (mProximitySensorActive) {
+                    // wait for proximity sensor to go negative before disabling sensor
+                    if (mDebugProximitySensor) {
+                        Log.d(TAG, "waiting for proximity sensor to go negative");
+                    }
+                } else {
+                    disableProximityLockLocked();
+                }
             }
         }
         // Unlink the lock from the binder.
@@ -898,6 +907,8 @@
         pw.println("  mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock);
         pw.println("  mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock);
         pw.println("  mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock);
+        pw.println("  mProximityWakeLockCount=" + mProximityWakeLockCount);
+        pw.println("  mProximitySensorEnabled=" + mProximitySensorEnabled);
         pw.println("  mProximitySensorActive=" + mProximitySensorActive);
         pw.println("  mProximityPendingValue=" + mProximityPendingValue);
         pw.println("  mLastProximityEventTime=" + mLastProximityEventTime);
@@ -1313,8 +1324,13 @@
                 enableLightSensor(on);
                 if (!on) {
                     // make sure button and key backlights are off too
-                    mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, 0);
-                    mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, 0);
+                    int brightnessMode = (mUseSoftwareAutoBrightness
+                            ? HardwareService.BRIGHTNESS_MODE_SENSOR
+                            : HardwareService.BRIGHTNESS_MODE_USER);
+                    mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, 0,
+                        brightnessMode);
+                    mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, 0,
+                        brightnessMode);
                     // clear current value so we will update based on the new conditions
                     // when the sensor is reenabled.
                     mLightSensorValue = -1;
@@ -1364,7 +1380,7 @@
             boolean oldScreenOn = (mPowerState & SCREEN_ON_BIT) != 0;
             boolean newScreenOn = (newState & SCREEN_ON_BIT) != 0;
 
-            if (mSpew) {
+            if (mPowerState != newState) {
                 Log.d(TAG, "setPowerState: mPowerState=" + mPowerState
                         + " newState=" + newState + " noChangeLights=" + noChangeLights);
                 Log.d(TAG, "  oldKeyboardBright=" + ((mPowerState & KEYBOARD_BRIGHT_BIT) != 0)
@@ -1669,14 +1685,23 @@
     }
 
     private void setLightBrightness(int mask, int value) {
+        int brightnessMode = (mAutoBrightessEnabled
+                            ? HardwareService.BRIGHTNESS_MODE_SENSOR
+                            : HardwareService.BRIGHTNESS_MODE_USER);
         if ((mask & SCREEN_BRIGHT_BIT) != 0) {
-            mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, value);
+            mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, value,
+                brightnessMode);
         }
+        brightnessMode = (mUseSoftwareAutoBrightness
+                            ? HardwareService.BRIGHTNESS_MODE_SENSOR
+                            : HardwareService.BRIGHTNESS_MODE_USER);
         if ((mask & BUTTON_BRIGHT_BIT) != 0) {
-            mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, value);
+            mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, value,
+                brightnessMode);
         }
         if ((mask & KEYBOARD_BRIGHT_BIT) != 0) {
-            mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, value);
+            mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, value,
+                brightnessMode);
         }
     }
 
@@ -1975,8 +2000,11 @@
                             startAnimation = true;
                         }
                     } else {
+                        int brightnessMode = (mAutoBrightessEnabled
+                                            ? HardwareService.BRIGHTNESS_MODE_SENSOR
+                                            : HardwareService.BRIGHTNESS_MODE_USER);
                         mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT,
-                                lcdValue);
+                                lcdValue, brightnessMode);
                     }
                 }
                 if (ANIMATE_BUTTON_LIGHTS) {
@@ -1986,8 +2014,11 @@
                         startAnimation = true;
                     }
                 } else {
+                    int brightnessMode = (mUseSoftwareAutoBrightness
+                                        ? HardwareService.BRIGHTNESS_MODE_SENSOR
+                                        : HardwareService.BRIGHTNESS_MODE_USER);
                     mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS,
-                            buttonValue);
+                            buttonValue, brightnessMode);
                 }
                 if (ANIMATE_KEYBOARD_LIGHTS) {
                     if (mKeyboardBrightness.setTargetLocked(keyboardValue,
@@ -1996,8 +2027,11 @@
                         startAnimation = true;
                     }
                 } else {
+                    int brightnessMode = (mUseSoftwareAutoBrightness
+                                        ? HardwareService.BRIGHTNESS_MODE_SENSOR
+                                        : HardwareService.BRIGHTNESS_MODE_USER);
                     mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD,
-                            keyboardValue);
+                            keyboardValue, brightnessMode);
                 }
                 if (startAnimation) {
                     if (mDebugLightSensor) {
@@ -2095,10 +2129,19 @@
 
     /**
      * When the keyguard is up, it manages the power state, and userActivity doesn't do anything.
+     * When disabling user activity we also reset user power state so the keyguard can reset its
+     * short screen timeout when keyguard is unhidden.
      */
     public void enableUserActivity(boolean enabled) {
+        if (mSpew) {
+            Log.d(TAG, "enableUserActivity " + enabled);
+        }
         synchronized (mLocks) {
             mUserActivityAllowed = enabled;
+            if (!enabled) {
+                // cancel timeout and clear mUserState so the keyguard can set a short timeout
+                setTimeoutLocked(SystemClock.uptimeMillis(), 0);
+            }
         }
     }
 
@@ -2309,10 +2352,12 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
         // Don't let applications turn the screen all the way off
         brightness = Math.max(brightness, Power.BRIGHTNESS_DIM);
-        mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, brightness);
+        mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, brightness,
+                HardwareService.BRIGHTNESS_MODE_USER);
         mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD,
-            (mKeyboardVisible ? brightness : 0));
-        mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, brightness);
+            (mKeyboardVisible ? brightness : 0), HardwareService.BRIGHTNESS_MODE_USER);
+        mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, brightness,
+            HardwareService.BRIGHTNESS_MODE_USER);
         long identity = Binder.clearCallingIdentity();
         try {
             mBatteryStats.noteScreenBrightness(brightness);
@@ -2341,43 +2386,49 @@
     }
 
     private void enableProximityLockLocked() {
-        if (mSpew) {
+        if (mDebugProximitySensor) {
             Log.d(TAG, "enableProximityLockLocked");
         }
-        // clear calling identity so sensor manager battery stats are accurate
-        long identity = Binder.clearCallingIdentity();
-        try {
-            mSensorManager.registerListener(mProximityListener, mProximitySensor,
-                    SensorManager.SENSOR_DELAY_NORMAL);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+        if (!mProximitySensorEnabled) {
+            // clear calling identity so sensor manager battery stats are accurate
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mSensorManager.registerListener(mProximityListener, mProximitySensor,
+                        SensorManager.SENSOR_DELAY_NORMAL);
+                mProximitySensorEnabled = true;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
     private void disableProximityLockLocked() {
-        if (mSpew) {
+        if (mDebugProximitySensor) {
             Log.d(TAG, "disableProximityLockLocked");
         }
-        // clear calling identity so sensor manager battery stats are accurate
-        long identity = Binder.clearCallingIdentity();
-        try {
-            mSensorManager.unregisterListener(mProximityListener);
-            mHandler.removeCallbacks(mProximityTask);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-        if (mProximitySensorActive) {
-            mProximitySensorActive = false;
-            forceUserActivityLocked();
+        if (mProximitySensorEnabled) {
+            // clear calling identity so sensor manager battery stats are accurate
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mSensorManager.unregisterListener(mProximityListener);
+                mHandler.removeCallbacks(mProximityTask);
+                mProximitySensorEnabled = false;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            if (mProximitySensorActive) {
+                mProximitySensorActive = false;
+                forceUserActivityLocked();
+            }
         }
     }
 
     private void proximityChangedLocked(boolean active) {
-        if (mSpew) {
+        if (mDebugProximitySensor) {
             Log.d(TAG, "proximityChangedLocked, active: " + active);
         }
-        if (mProximityCount <= 0) {
-            Log.d(TAG, "Ignoring proximity change after last proximity lock is released");
+        if (!mProximitySensorEnabled) {
+            Log.d(TAG, "Ignoring proximity change after sensor is disabled");
             return;
         }
         if (active) {
@@ -2389,6 +2440,11 @@
             // even when the keyguard is on.
             mProximitySensorActive = false;
             forceUserActivityLocked();
+
+            if (mProximityWakeLockCount == 0) {
+                // disable sensor if we have no listeners left after proximity negative
+                disableProximityLockLocked();
+            }
         }
     }
 
@@ -2427,6 +2483,9 @@
                 boolean active = (distance >= 0.0 && distance < PROXIMITY_THRESHOLD &&
                         distance < mProximitySensor.getMaximumRange());
 
+                if (mDebugProximitySensor) {
+                    Log.d(TAG, "mProximityListener.onSensorChanged active: " + active);
+                }
                 if (timeSinceLastEvent < PROXIMITY_SENSOR_DELAY) {
                     // enforce delaying atleast PROXIMITY_SENSOR_DELAY before processing
                     mProximityPendingValue = (active ? 1 : 0);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 69f4b89..327cd72 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -435,6 +435,7 @@
     float mLastWallpaperY = -1;
     float mLastWallpaperXStep = -1;
     float mLastWallpaperYStep = -1;
+    boolean mSendingPointersToWallpaper = false;
     // This is set when we are waiting for a wallpaper to tell us it is done
     // changing its scroll position.
     WindowState mWaitingOnWallpaper;
@@ -1749,8 +1750,20 @@
                 }
                 try {
                     MotionEvent ev = MotionEvent.obtainNoHistory(pointer);
-                    ev.offsetLocation(srcWin.mFrame.left-wallpaper.mFrame.left,
-                            srcWin.mFrame.top-wallpaper.mFrame.top);
+                    if (srcWin != null) {
+                        ev.offsetLocation(srcWin.mFrame.left-wallpaper.mFrame.left,
+                                srcWin.mFrame.top-wallpaper.mFrame.top);
+                    } else {
+                        ev.offsetLocation(-wallpaper.mFrame.left, -wallpaper.mFrame.top);
+                    }
+                    switch (pointer.getAction()) {
+                        case MotionEvent.ACTION_DOWN:
+                            mSendingPointersToWallpaper = true;
+                            break;
+                        case MotionEvent.ACTION_UP:
+                            mSendingPointersToWallpaper = false;
+                            break;
+                    }
                     wallpaper.mClient.dispatchPointer(ev, eventTime, false);
                 } catch (RemoteException e) {
                     Log.w(TAG, "Failure sending pointer to wallpaper", e);
@@ -4836,6 +4849,12 @@
             if (action != MotionEvent.ACTION_MOVE) {
                 Log.w(TAG, "No window to dispatch pointer action " + ev.getAction());
             }
+            synchronized (mWindowMap) {
+                if (mSendingPointersToWallpaper) {
+                    Log.i(TAG, "Sending skipped pointer to wallpaper!");
+                    sendPointerToWallpaperLocked(null, ev, ev.getEventTime());
+                }
+            }
             if (qev != null) {
                 mQueue.recycleEvent(qev);
             }
@@ -4843,6 +4862,12 @@
             return INJECT_FAILED;
         }
         if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
+            synchronized (mWindowMap) {
+                if (mSendingPointersToWallpaper) {
+                    Log.i(TAG, "Sending skipped pointer to wallpaper!");
+                    sendPointerToWallpaperLocked(null, ev, ev.getEventTime());
+                }
+            }
             if (qev != null) {
                 mQueue.recycleEvent(qev);
             }
@@ -4963,6 +4988,19 @@
         }
 
         synchronized(mWindowMap) {
+            if (!target.isVisibleLw()) {
+                // During this motion dispatch, the target window has become
+                // invisible.
+                if (mSendingPointersToWallpaper) {
+                    sendPointerToWallpaperLocked(null, ev, eventTime);
+                }
+                if (qev != null) {
+                    mQueue.recycleEvent(qev);
+                }
+                ev.recycle();
+                return INJECT_SUCCEEDED;
+            }
+            
             if (qev != null && action == MotionEvent.ACTION_MOVE) {
                 mKeyWaiter.bindTargetWindowLocked(target,
                         KeyWaiter.RETURN_PENDING_POINTER, qev);
@@ -4987,15 +5025,16 @@
                         mKeyWaiter.mOutsideTouchTargets = null;
                     }
                 }
-                final Rect frame = target.mFrame;
-                ev.offsetLocation(-(float)frame.left, -(float)frame.top);
-                mKeyWaiter.bindTargetWindowLocked(target);
                 
                 // If we are on top of the wallpaper, then the wallpaper also
                 // gets to see this movement.
-                if (mWallpaperTarget == target) {
-                    sendPointerToWallpaperLocked(target, ev, eventTime);
+                if (mWallpaperTarget == target || mSendingPointersToWallpaper) {
+                    sendPointerToWallpaperLocked(null, ev, eventTime);
                 }
+                
+                final Rect frame = target.mFrame;
+                ev.offsetLocation(-(float)frame.left, -(float)frame.top);
+                mKeyWaiter.bindTargetWindowLocked(target);
             }
         }
 
@@ -5918,12 +5957,12 @@
                         res.offsetLocation(-win.mFrame.left, -win.mFrame.top);
                     }
                 }
-            }
-            
-            if (res != null && returnWhat == RETURN_PENDING_POINTER) {
-                synchronized (mWindowMap) {
-                    if (mWallpaperTarget == win) {
-                        sendPointerToWallpaperLocked(win, res, res.getEventTime());
+                
+                if (res != null && returnWhat == RETURN_PENDING_POINTER) {
+                    synchronized (mWindowMap) {
+                        if (mWallpaperTarget == win || mSendingPointersToWallpaper) {
+                            sendPointerToWallpaperLocked(win, res, res.getEventTime());
+                        }
                     }
                 }
             }
diff --git a/services/jni/com_android_server_HardwareService.cpp b/services/jni/com_android_server_HardwareService.cpp
index 22d4bd8..253e655 100644
--- a/services/jni/com_android_server_HardwareService.cpp
+++ b/services/jni/com_android_server_HardwareService.cpp
@@ -101,7 +101,7 @@
 }
 
 static void setLight_native(JNIEnv *env, jobject clazz, int ptr,
-        int light, int colorARGB, int flashMode, int onMS, int offMS)
+        int light, int colorARGB, int flashMode, int onMS, int offMS, int brightnessMode)
 {
     Devices* devices = (Devices*)ptr;
     light_state_t state;
@@ -115,6 +115,7 @@
     state.flashMode = flashMode;
     state.flashOnMS = onMS;
     state.flashOffMS = offMS;
+    state.brightnessMode = brightnessMode;
 
     devices->lights[light]->set_light(devices->lights[light], &state);
 }
@@ -134,7 +135,7 @@
 static JNINativeMethod method_table[] = {
     { "init_native", "()I", (void*)init_native },
     { "finalize_native", "(I)V", (void*)finalize_native },
-    { "setLight_native", "(IIIIII)V", (void*)setLight_native },
+    { "setLight_native", "(IIIIIII)V", (void*)setLight_native },
     { "vibratorOn", "(J)V", (void*)vibratorOn },
     { "vibratorOff", "()V", (void*)vibratorOff }
 };