am d8de2ba4: Merge change 5750 into donut

Merge commit 'd8de2ba42679a10db2adc11995d6596a0045ab3a'

* commit 'd8de2ba42679a10db2adc11995d6596a0045ab3a':
  Add a new checkin server flag for Vending Machine client server ping frequency for pending downloads.
diff --git a/Android.mk b/Android.mk
index f32129e..01aef7a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -65,8 +65,11 @@
 ## READ ME: ########################################################
 LOCAL_SRC_FILES += \
 	core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
-  core/java/android/accessibilityservice/IEventListener.aidl \
-	core/java/android/accounts/IAccountsService.aidl \
+        core/java/android/accessibilityservice/IEventListener.aidl \
+	core/java/android/accounts/IAccountManager.aidl \
+	core/java/android/accounts/IAccountManagerResponse.aidl \
+	core/java/android/accounts/IAccountAuthenticator.aidl \
+	core/java/android/accounts/IAccountAuthenticatorResponse.aidl \
 	core/java/android/app/IActivityPendingResult.aidl \
 	core/java/android/app/IActivityWatcher.aidl \
 	core/java/android/app/IAlarmManager.aidl \
@@ -86,7 +89,6 @@
 	core/java/android/backup/IRestoreSession.aidl \
 	core/java/android/bluetooth/IBluetoothA2dp.aidl \
 	core/java/android/bluetooth/IBluetoothDevice.aidl \
-	core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \
 	core/java/android/bluetooth/IBluetoothHeadset.aidl \
         core/java/android/content/IContentService.aidl \
 	core/java/android/content/IIntentReceiver.aidl \
@@ -194,7 +196,10 @@
 # relative to the root of the build tree.
 # ============================================================
 aidl_files := \
-	frameworks/base/core/java/android/accounts/IAccountsService.aidl \
+	frameworks/base/core/java/android/accounts/IAccountManager.aidl \
+	frameworks/base/core/java/android/accounts/IAccountManagerResponse.aidl \
+	frameworks/base/core/java/android/accounts/IAccountAuthenticator.aidl \
+	frameworks/base/core/java/android/accounts/IAccountAuthenticatorResponse.aidl \
 	frameworks/base/core/java/android/app/Notification.aidl \
 	frameworks/base/core/java/android/app/PendingIntent.aidl \
 	frameworks/base/core/java/android/content/ComponentName.aidl \
@@ -478,5 +483,3 @@
 ifeq (,$(ONE_SHOT_MAKEFILE))
 include $(call first-makefiles-under,$(LOCAL_PATH))
 endif
-
-
diff --git a/api/current.xml b/api/current.xml
index 1287f59..e0a6289 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -1409,7 +1409,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="17432583"
+ value="17432609"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -1420,7 +1420,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="17432585"
+ value="17432611"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -1431,7 +1431,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="17432586"
+ value="17432612"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -1603,6 +1603,61 @@
  visibility="public"
 >
 </field>
+<field name="donut_resource_pad22"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432587"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="donut_resource_pad23"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432586"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="donut_resource_pad24"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432585"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="donut_resource_pad25"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432584"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="donut_resource_pad26"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432583"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="donut_resource_pad3"
  type="int"
  transient="false"
@@ -1706,7 +1761,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="17432587"
+ value="17432613"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -1717,7 +1772,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="17432584"
+ value="17432610"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -1978,6 +2033,17 @@
  visibility="public"
 >
 </field>
+<field name="accountType"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843424"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="action"
  type="int"
  transient="false"
@@ -2946,6 +3012,17 @@
  visibility="public"
 >
 </field>
+<field name="contentAuthority"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843425"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="contentDescription"
  type="int"
  transient="false"
@@ -10802,6 +10879,28 @@
  visibility="public"
 >
 </field>
+<field name="donut_resource_pad41"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17301672"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="donut_resource_pad42"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17301671"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="donut_resource_pad5"
  type="int"
  transient="false"
@@ -12192,7 +12291,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="17301671"
+ value="17301713"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -12203,7 +12302,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="17301672"
+ value="17301714"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -15802,6 +15901,2131 @@
 </field>
 </class>
 </package>
+<package name="android.accounts"
+>
+<class name="AbstractAccountAuthenticator"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AbstractAccountAuthenticator"
+ type="android.accounts.AbstractAccountAuthenticator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addAccount"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="requiredFeatures" type="java.lang.String[]">
+</parameter>
+<parameter name="options" type="android.os.Bundle">
+</parameter>
+<exception name="NetworkErrorException" type="android.accounts.NetworkErrorException">
+</exception>
+</method>
+<method name="confirmCredentials"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+</method>
+<method name="confirmPassword"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="password" type="java.lang.String">
+</parameter>
+<exception name="NetworkErrorException" type="android.accounts.NetworkErrorException">
+</exception>
+</method>
+<method name="editProperties"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+</method>
+<method name="getAuthToken"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="loginOptions" type="android.os.Bundle">
+</parameter>
+<exception name="NetworkErrorException" type="android.accounts.NetworkErrorException">
+</exception>
+</method>
+<method name="getIAccountAuthenticator"
+ return="android.accounts.IAccountAuthenticator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasFeatures"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="features" type="java.lang.String[]">
+</parameter>
+<exception name="NetworkErrorException" type="android.accounts.NetworkErrorException">
+</exception>
+</method>
+<method name="updateCredentials"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="loginOptions" type="android.os.Bundle">
+</parameter>
+</method>
+</class>
+<class name="Account"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="Account"
+ type="android.accounts.Account"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="Account"
+ type="android.accounts.Account"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="in" type="android.os.Parcel">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mName"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mType"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="AccountAuthenticatorActivity"
+ extends="android.app.Activity"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AccountAuthenticatorActivity"
+ type="android.accounts.AccountAuthenticatorActivity"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="setAccountAuthenticatorResult"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="result" type="android.os.Bundle">
+</parameter>
+</method>
+</class>
+<class name="AccountAuthenticatorResponse"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="AccountAuthenticatorResponse"
+ type="android.accounts.AccountAuthenticatorResponse"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+</constructor>
+<constructor name="AccountAuthenticatorResponse"
+ type="android.accounts.AccountAuthenticatorResponse"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parcel" type="android.os.Parcel">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onError"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="errorCode" type="int">
+</parameter>
+<parameter name="errorMessage" type="java.lang.String">
+</parameter>
+</method>
+<method name="onRequestContinued"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onResult"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="result" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="AccountManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="addAccount"
+ return="android.accounts.Future2"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="requiredFeatures" type="java.lang.String[]">
+</parameter>
+<parameter name="addAccountOptions" type="android.os.Bundle">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="callback" type="android.accounts.Future2Callback">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="addAccountExplicitly"
+ return="android.accounts.Future1&lt;java.lang.Boolean&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;java.lang.Boolean&gt;">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="password" type="java.lang.String">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="addOnAccountsUpdatedListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.accounts.OnAccountsUpdatedListener">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+<parameter name="updateImmediately" type="boolean">
+</parameter>
+</method>
+<method name="blockingAddAccountExplicitly"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="password" type="java.lang.String">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="blockingClearPassword"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+</method>
+<method name="blockingGetAccounts"
+ return="android.accounts.Account[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="blockingGetAccountsByType"
+ return="android.accounts.Account[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+</method>
+<method name="blockingGetAccountsWithTypeAndFeatures"
+ return="android.accounts.Account[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="features" type="java.lang.String[]">
+</parameter>
+<exception name="AuthenticatorException" type="android.accounts.AuthenticatorException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+<exception name="OperationCanceledException" type="android.accounts.OperationCanceledException">
+</exception>
+</method>
+<method name="blockingGetAuthToken"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="notifyAuthFailure" type="boolean">
+</parameter>
+<exception name="AuthenticatorException" type="android.accounts.AuthenticatorException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+<exception name="OperationCanceledException" type="android.accounts.OperationCanceledException">
+</exception>
+</method>
+<method name="blockingGetAuthenticatorTypes"
+ return="android.accounts.AuthenticatorDescription[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="blockingGetPassword"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+</method>
+<method name="blockingGetUserData"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="key" type="java.lang.String">
+</parameter>
+</method>
+<method name="blockingInvalidateAuthToken"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="authToken" type="java.lang.String">
+</parameter>
+</method>
+<method name="blockingPeekAuthToken"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+</method>
+<method name="blockingRemoveAccount"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+</method>
+<method name="blockingSetAuthToken"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="authToken" type="java.lang.String">
+</parameter>
+</method>
+<method name="blockingSetPassword"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="password" type="java.lang.String">
+</parameter>
+</method>
+<method name="blockingSetUserData"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="value" type="java.lang.String">
+</parameter>
+</method>
+<method name="clearPassword"
+ return="android.accounts.Future1&lt;java.lang.Void&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;java.lang.Void&gt;">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="confirmCredentials"
+ return="android.accounts.Future2"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="callback" type="android.accounts.Future2Callback">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="confirmPassword"
+ return="android.accounts.Future1&lt;java.lang.Boolean&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="password" type="java.lang.String">
+</parameter>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;java.lang.Boolean&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="editProperties"
+ return="android.accounts.Future2"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="callback" type="android.accounts.Future2Callback">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="get"
+ return="android.accounts.AccountManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="getAccounts"
+ return="android.accounts.Future1&lt;android.accounts.Account[]&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;android.accounts.Account[]&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="getAccountsByType"
+ return="android.accounts.Future1&lt;android.accounts.Account[]&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;android.accounts.Account[]&gt;">
+</parameter>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="getAccountsWithTypeAndFeatures"
+ return="android.accounts.Future2"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="features" type="java.lang.String[]">
+</parameter>
+<parameter name="callback" type="android.accounts.Future2Callback">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="getAuthToken"
+ return="android.accounts.Future2"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="loginOptions" type="android.os.Bundle">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="callback" type="android.accounts.Future2Callback">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="getAuthToken"
+ return="android.accounts.Future2"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="notifyAuthFailure" type="boolean">
+</parameter>
+<parameter name="callback" type="android.accounts.Future2Callback">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="getAuthTokenByFeatures"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="features" type="java.lang.String[]">
+</parameter>
+<parameter name="activityForPrompting" type="android.app.Activity">
+</parameter>
+<parameter name="addAccountOptions" type="android.os.Bundle">
+</parameter>
+<parameter name="loginOptions" type="android.os.Bundle">
+</parameter>
+<parameter name="callback" type="android.accounts.Future2Callback">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="getAuthenticatorTypes"
+ return="android.accounts.Future1&lt;android.accounts.AuthenticatorDescription[]&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;android.accounts.AuthenticatorDescription[]&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="getPassword"
+ return="android.accounts.Future1&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;java.lang.String&gt;">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="getUserData"
+ return="android.accounts.Future1&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;java.lang.String&gt;">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="invalidateAuthToken"
+ return="android.accounts.Future1&lt;java.lang.Void&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;java.lang.Void&gt;">
+</parameter>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="authToken" type="java.lang.String">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="peekAuthToken"
+ return="android.accounts.Future1&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;java.lang.String&gt;">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="removeAccount"
+ return="android.accounts.Future1&lt;java.lang.Void&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;java.lang.Void&gt;">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="removeOnAccountsUpdatedListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.accounts.OnAccountsUpdatedListener">
+</parameter>
+</method>
+<method name="setAuthToken"
+ return="android.accounts.Future1&lt;java.lang.Void&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;java.lang.Void&gt;">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="authToken" type="java.lang.String">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="setPassword"
+ return="android.accounts.Future1&lt;java.lang.Void&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;java.lang.Void&gt;">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="password" type="java.lang.String">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="setUserData"
+ return="android.accounts.Future1&lt;java.lang.Void&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.accounts.Future1Callback&lt;java.lang.Void&gt;">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="value" type="java.lang.String">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="updateCredentials"
+ return="android.accounts.Future2"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="loginOptions" type="android.os.Bundle">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="callback" type="android.accounts.Future2Callback">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+</class>
+<class name="AuthenticatorDescription"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="AuthenticatorDescription"
+ type="android.accounts.AuthenticatorDescription"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+<parameter name="labelId" type="int">
+</parameter>
+<parameter name="iconId" type="int">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newKey"
+ return="android.accounts.AuthenticatorDescription"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="iconId"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="labelId"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="packageName"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="type"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="AuthenticatorException"
+ extends="java.lang.Exception"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AuthenticatorException"
+ type="android.accounts.AuthenticatorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="AuthenticatorException"
+ type="android.accounts.AuthenticatorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="AuthenticatorException"
+ type="android.accounts.AuthenticatorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="AuthenticatorException"
+ type="android.accounts.AuthenticatorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+</class>
+<class name="ChooseAccountActivity"
+ extends="android.app.ListActivity"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ChooseAccountActivity"
+ type="android.accounts.ChooseAccountActivity"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onCreate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="savedInstanceState" type="android.os.Bundle">
+</parameter>
+</method>
+</class>
+<class name="Constants"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="ACCOUNTS_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;accounts&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACCOUNT_AUTHENTICATOR_RESPONSE_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;accountAuthenticatorResponse&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACCOUNT_MANAGER_RESPONSE_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;accountManagerResponse&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACCOUNT_NAME_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;authAccount&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACCOUNT_TYPE_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;accountType&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AUTHENTICATOR_TYPES_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;authenticator_types&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AUTHTOKEN_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;authtoken&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AUTH_FAILED_MESSAGE_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;authFailedMessage&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="BOOLEAN_RESULT_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;booleanResult&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_BAD_ARGUMENTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_BAD_REQUEST"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_CANCELED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_INVALID_RESPONSE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;errorCode&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_NETWORK_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_REMOTE_EXCEPTION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_UNSUPPORTED_OPERATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_MESSAGE_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;errorMessage&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INTENT_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;intent&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="LOGIN_ACCOUNTS_CHANGED_ACTION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.accounts.LOGIN_ACCOUNTS_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PASSWORD_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;password&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USERDATA_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;userdata&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="Future1"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.util.concurrent.Future">
+</implements>
+<method name="getResult"
+ return="V"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="OperationCanceledException" type="android.accounts.OperationCanceledException">
+</exception>
+</method>
+<method name="getResult"
+ return="V"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="timeout" type="long">
+</parameter>
+<parameter name="unit" type="java.util.concurrent.TimeUnit">
+</parameter>
+<exception name="OperationCanceledException" type="android.accounts.OperationCanceledException">
+</exception>
+</method>
+</interface>
+<interface name="Future1Callback"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="run"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="future" type="android.accounts.Future1&lt;V&gt;">
+</parameter>
+</method>
+</interface>
+<interface name="Future2"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.util.concurrent.Future">
+</implements>
+<method name="getResult"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="AuthenticatorException" type="android.accounts.AuthenticatorException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+<exception name="OperationCanceledException" type="android.accounts.OperationCanceledException">
+</exception>
+</method>
+<method name="getResult"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="timeout" type="long">
+</parameter>
+<parameter name="unit" type="java.util.concurrent.TimeUnit">
+</parameter>
+<exception name="AuthenticatorException" type="android.accounts.AuthenticatorException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+<exception name="OperationCanceledException" type="android.accounts.OperationCanceledException">
+</exception>
+</method>
+</interface>
+<interface name="Future2Callback"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="run"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="future" type="android.accounts.Future2">
+</parameter>
+</method>
+</interface>
+<interface name="IAccountAuthenticator"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.IInterface">
+</implements>
+<method name="addAccount"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="requiredFeatures" type="java.lang.String[]">
+</parameter>
+<parameter name="options" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="confirmCredentials"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="confirmPassword"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="password" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="editProperties"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="getAuthToken"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="options" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="hasFeatures"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="features" type="java.lang.String[]">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="updateCredentials"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="options" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</interface>
+<class name="IAccountAuthenticator.Stub"
+ extends="android.os.Binder"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.accounts.IAccountAuthenticator">
+</implements>
+<constructor name="IAccountAuthenticator.Stub"
+ type="android.accounts.IAccountAuthenticator.Stub"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="asBinder"
+ return="android.os.IBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="asInterface"
+ return="android.accounts.IAccountAuthenticator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="obj" type="android.os.IBinder">
+</parameter>
+</method>
+<method name="onTransact"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="code" type="int">
+</parameter>
+<parameter name="data" type="android.os.Parcel">
+</parameter>
+<parameter name="reply" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</class>
+<interface name="IAccountAuthenticatorResponse"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.IInterface">
+</implements>
+<method name="onError"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="errorCode" type="int">
+</parameter>
+<parameter name="errorMessage" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="onRequestContinued"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="onResult"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</interface>
+<class name="IAccountAuthenticatorResponse.Stub"
+ extends="android.os.Binder"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.accounts.IAccountAuthenticatorResponse">
+</implements>
+<constructor name="IAccountAuthenticatorResponse.Stub"
+ type="android.accounts.IAccountAuthenticatorResponse.Stub"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="asBinder"
+ return="android.os.IBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="asInterface"
+ return="android.accounts.IAccountAuthenticatorResponse"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="obj" type="android.os.IBinder">
+</parameter>
+</method>
+<method name="onTransact"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="code" type="int">
+</parameter>
+<parameter name="data" type="android.os.Parcel">
+</parameter>
+<parameter name="reply" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</class>
+<class name="NetworkErrorException"
+ extends="java.lang.Exception"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="NetworkErrorException"
+ type="android.accounts.NetworkErrorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="NetworkErrorException"
+ type="android.accounts.NetworkErrorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="NetworkErrorException"
+ type="android.accounts.NetworkErrorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="NetworkErrorException"
+ type="android.accounts.NetworkErrorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+</class>
+<interface name="OnAccountsUpdatedListener"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onAccountsUpdated"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accounts" type="android.accounts.Account[]">
+</parameter>
+</method>
+</interface>
+<class name="OperationCanceledException"
+ extends="java.lang.Exception"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="OperationCanceledException"
+ type="android.accounts.OperationCanceledException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="OperationCanceledException"
+ type="android.accounts.OperationCanceledException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="OperationCanceledException"
+ type="android.accounts.OperationCanceledException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="OperationCanceledException"
+ type="android.accounts.OperationCanceledException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+</class>
+</package>
 <package name="android.app"
 >
 <class name="Activity"
@@ -25058,6 +27282,75 @@
 </package>
 <package name="android.content"
 >
+<class name="AbstractCursorEntityIterator"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.content.EntityIterator">
+</implements>
+<constructor name="AbstractCursorEntityIterator"
+ type="android.content.AbstractCursorEntityIterator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="db" type="android.database.sqlite.SQLiteDatabase">
+</parameter>
+<parameter name="entityCursor" type="android.database.Cursor">
+</parameter>
+</constructor>
+<method name="close"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasNext"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newEntityFromCursorLocked"
+ return="android.content.Entity"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+</method>
+<method name="next"
+ return="android.content.Entity"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
 <class name="ActivityNotFoundException"
  extends="java.lang.RuntimeException"
  abstract="false"
@@ -25887,6 +28180,21 @@
  visibility="public"
 >
 </constructor>
+<method name="applyBatch"
+ return="android.content.ContentProviderResult[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="operations" type="java.util.ArrayList&lt;android.content.ContentProviderOperation&gt;">
+</parameter>
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
+</exception>
+</method>
 <method name="attachInfo"
  return="void"
  abstract="false"
@@ -25995,6 +28303,21 @@
 <parameter name="values" type="android.content.ContentValues">
 </parameter>
 </method>
+<method name="insertEntity"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="entity" type="android.content.Entity">
+</parameter>
+</method>
 <method name="isTemporary"
  return="boolean"
  abstract="false"
@@ -26113,6 +28436,25 @@
 <parameter name="sortOrder" type="java.lang.String">
 </parameter>
 </method>
+<method name="queryEntities"
+ return="android.content.EntityIterator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="sortOrder" type="java.lang.String">
+</parameter>
+</method>
 <method name="setReadPermission"
  return="void"
  abstract="false"
@@ -26158,6 +28500,671 @@
 <parameter name="selectionArgs" type="java.lang.String[]">
 </parameter>
 </method>
+<method name="updateEntity"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="entity" type="android.content.Entity">
+</parameter>
+</method>
+</class>
+<class name="ContentProviderClient"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="applyBatch"
+ return="android.content.ContentProviderResult[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="operations" type="java.util.ArrayList&lt;android.content.ContentProviderOperation&gt;">
+</parameter>
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
+</exception>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="bulkInsert"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="initialValues" type="android.content.ContentValues[]">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="delete"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="getLocalContentProvider"
+ return="android.content.ContentProvider"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getType"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="insert"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="initialValues" type="android.content.ContentValues">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="insertEntity"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="entity" type="android.content.Entity">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="openAssetFile"
+ return="android.content.res.AssetFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="mode" type="java.lang.String">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="openFile"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="mode" type="java.lang.String">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="query"
+ return="android.database.Cursor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="projection" type="java.lang.String[]">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="sortOrder" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="queryEntities"
+ return="android.content.EntityIterator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="sortOrder" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="release"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="update"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="updateEntity"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="entity" type="android.content.Entity">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</class>
+<class name="ContentProviderOperation"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="apply"
+ return="android.content.ContentProviderResult"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="provider" type="android.content.ContentProvider">
+</parameter>
+<parameter name="backRefs" type="android.content.ContentProviderResult[]">
+</parameter>
+<parameter name="numBackRefs" type="int">
+</parameter>
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
+</exception>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isReadOperation"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isWriteOperation"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newCountQuery"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="newDelete"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="newInsert"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="newUpdate"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="resolveSelectionArgsBackReferences"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="backRefs" type="android.content.ContentProviderResult[]">
+</parameter>
+<parameter name="numBackRefs" type="int">
+</parameter>
+</method>
+<method name="resolveValueBackReferences"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="backRefs" type="android.content.ContentProviderResult[]">
+</parameter>
+<parameter name="numBackRefs" type="int">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="ContentProviderOperation.Builder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="build"
+ return="android.content.ContentProviderOperation"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="withExpectedCount"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="count" type="int">
+</parameter>
+</method>
+<method name="withSelection"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="withSelectionBackReference"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="selectionArgIndex" type="int">
+</parameter>
+<parameter name="previousResult" type="int">
+</parameter>
+</method>
+<method name="withValue"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="value" type="java.lang.Object">
+</parameter>
+</method>
+<method name="withValueBackReference"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="previousResult" type="int">
+</parameter>
+</method>
+<method name="withValueBackReferences"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="backReferences" type="android.content.ContentValues">
+</parameter>
+</method>
+<method name="withValues"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+</method>
+</class>
+<class name="ContentProviderResult"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="ContentProviderResult"
+ type="android.content.ContentProviderResult"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</constructor>
+<constructor name="ContentProviderResult"
+ type="android.content.ContentProviderResult"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="count" type="int">
+</parameter>
+</constructor>
+<constructor name="ContentProviderResult"
+ type="android.content.ContentProviderResult"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="android.os.Parcel">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="count"
+ type="java.lang.Integer"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="uri"
+ type="android.net.Uri"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="ContentQueryMap"
  extends="java.util.Observable"
@@ -26261,6 +29268,66 @@
 <parameter name="context" type="android.content.Context">
 </parameter>
 </constructor>
+<method name="acquireContentProviderClient"
+ return="android.content.ContentProviderClient"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="acquireContentProviderClient"
+ return="android.content.ContentProviderClient"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
+<method name="addStatusChangeListener"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mask" type="int">
+</parameter>
+<parameter name="callback" type="android.content.SyncStatusObserver">
+</parameter>
+</method>
+<method name="applyBatch"
+ return="android.content.ContentProviderResult[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="operations" type="java.util.ArrayList&lt;android.content.ContentProviderOperation&gt;">
+</parameter>
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
+</exception>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
 <method name="bulkInsert"
  return="int"
  abstract="false"
@@ -26283,12 +29350,27 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="uri" type="android.net.Uri">
 </parameter>
 </method>
+<method name="cancelSync"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+</method>
 <method name="delete"
  return="int"
  abstract="false"
@@ -26306,6 +29388,43 @@
 <parameter name="selectionArgs" type="java.lang.String[]">
 </parameter>
 </method>
+<method name="getMasterSyncAutomatically"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSyncAdapterTypes"
+ return="android.content.SyncAdapterType[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSyncAutomatically"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+</method>
 <method name="getType"
  return="java.lang.String"
  abstract="false"
@@ -26334,6 +29453,36 @@
 <parameter name="values" type="android.content.ContentValues">
 </parameter>
 </method>
+<method name="isSyncActive"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+</method>
+<method name="isSyncPending"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+</method>
 <method name="notifyChange"
  return="void"
  abstract="false"
@@ -26468,6 +29617,27 @@
 <parameter name="sortOrder" type="java.lang.String">
 </parameter>
 </method>
+<method name="queryEntities"
+ return="android.content.EntityIterator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="sortOrder" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
 <method name="registerContentObserver"
  return="void"
  abstract="false"
@@ -26485,6 +29655,66 @@
 <parameter name="observer" type="android.database.ContentObserver">
 </parameter>
 </method>
+<method name="removeStatusChangeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="handle" type="java.lang.Object">
+</parameter>
+</method>
+<method name="requestSync"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="setMasterSyncAutomatically"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sync" type="boolean">
+</parameter>
+</method>
+<method name="setSyncAutomatically"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="sync" type="boolean">
+</parameter>
+</method>
 <method name="startSync"
  return="void"
  abstract="false"
@@ -26492,7 +29722,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="uri" type="android.net.Uri">
@@ -26607,7 +29837,7 @@
  value="&quot;account&quot;"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -26640,6 +29870,17 @@
  value="&quot;force&quot;"
  static="true"
  final="true"
+ deprecated="deprecated"
+ visibility="public"
+>
+</field>
+<field name="SYNC_EXTRAS_MANUAL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;force&quot;"
+ static="true"
+ final="true"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -28213,6 +31454,17 @@
  visibility="public"
 >
 </field>
+<field name="ACCOUNT_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;account&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTIVITY_SERVICE"
  type="java.lang.String"
  transient="false"
@@ -29718,6 +32970,186 @@
 </parameter>
 </method>
 </interface>
+<class name="Entity"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="Entity"
+ type="android.content.Entity"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+</constructor>
+<method name="addSubValue"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getEntityValues"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSubValues"
+ return="java.util.ArrayList&lt;android.content.Entity.NamedContentValues&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Entity.NamedContentValues"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Entity.NamedContentValues"
+ type="android.content.Entity.NamedContentValues"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+</constructor>
+<field name="uri"
+ type="android.net.Uri"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="values"
+ type="android.content.ContentValues"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="EntityIterator"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="close"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasNext"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="next"
+ return="android.content.Entity"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</interface>
 <interface name="IIntentReceiver"
  abstract="true"
  static="false"
@@ -31900,7 +35332,7 @@
  type="java.lang.String"
  transient="false"
  volatile="false"
- value="&quot;android.intent.action.ACTION_POWER_CONNECTED&quot;"
+ value="&quot;android.intent.action.POWER_CONNECTED&quot;"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -31911,7 +35343,7 @@
  type="java.lang.String"
  transient="false"
  volatile="false"
- value="&quot;android.intent.action.ACTION_POWER_DISCONNECTED&quot;"
+ value="&quot;android.intent.action.POWER_DISCONNECTED&quot;"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -31951,6 +35383,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_REMOTE_INTENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.REMOTE_INTENT&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_RUN"
  type="java.lang.String"
  transient="false"
@@ -32500,6 +35943,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_REMOTE_INTENT_TOKEN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.remote_intent_token&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EXTRA_REPLACING"
  type="java.lang.String"
  transient="false"
@@ -34126,6 +37580,55 @@
 </parameter>
 </method>
 </class>
+<class name="OperationApplicationException"
+ extends="java.lang.Exception"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="OperationApplicationException"
+ type="android.content.OperationApplicationException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="OperationApplicationException"
+ type="android.content.OperationApplicationException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="OperationApplicationException"
+ type="android.content.OperationApplicationException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="OperationApplicationException"
+ type="android.content.OperationApplicationException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+</class>
 <class name="ReceiverCallNotAllowedException"
  extends="android.util.AndroidRuntimeException"
  abstract="false"
@@ -34616,6 +38119,116 @@
 </parameter>
 </method>
 </interface>
+<class name="SyncAdapterType"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="SyncAdapterType"
+ type="android.content.SyncAdapterType"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="SyncAdapterType"
+ type="android.content.SyncAdapterType"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="android.os.Parcel">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="accountType"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="authority"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="SyncStatusObserver"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onStatusChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="which" type="int">
+</parameter>
+</method>
+</interface>
 <class name="UriMatcher"
  extends="java.lang.Object"
  abstract="false"
@@ -36675,6 +40288,19 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
+<method name="getInstallerPackageName"
+ return="java.lang.String"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+</method>
 <method name="getInstrumentationInfo"
  return="android.content.pm.InstrumentationInfo"
  abstract="true"
@@ -38007,7 +41633,7 @@
  volatile="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -41755,6 +45381,32 @@
 <parameter name="columnIndex" type="int">
 </parameter>
 </method>
+<method name="isFloat"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
+<method name="isLong"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
 <method name="isNull"
  return="boolean"
  abstract="false"
@@ -41768,6 +45420,19 @@
 <parameter name="columnIndex" type="int">
 </parameter>
 </method>
+<method name="isString"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
 <method name="setWindow"
  return="void"
  abstract="false"
@@ -42848,6 +46513,36 @@
 <parameter name="col" type="int">
 </parameter>
 </method>
+<method name="isFloat"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="row" type="int">
+</parameter>
+<parameter name="col" type="int">
+</parameter>
+</method>
+<method name="isLong"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="row" type="int">
+</parameter>
+<parameter name="col" type="int">
+</parameter>
+</method>
 <method name="isNull"
  return="boolean"
  abstract="false"
@@ -42863,6 +46558,21 @@
 <parameter name="col" type="int">
 </parameter>
 </method>
+<method name="isString"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="row" type="int">
+</parameter>
+<parameter name="col" type="int">
+</parameter>
+</method>
 <method name="newFromParcel"
  return="android.database.CursorWindow"
  abstract="false"
@@ -44058,6 +47768,21 @@
 <exception name="FileNotFoundException" type="java.io.FileNotFoundException">
 </exception>
 </method>
+<method name="readExceptionWithOperationApplicationExceptionFromParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reply" type="android.os.Parcel">
+</parameter>
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
+</exception>
+</method>
 <method name="sqlEscapeString"
  return="java.lang.String"
  abstract="false"
@@ -69719,6 +73444,21 @@
  visibility="public"
 >
 </method>
+<method name="invoke"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="request" type="android.os.Parcel">
+</parameter>
+<parameter name="reply" type="android.os.Parcel">
+</parameter>
+</method>
 <method name="isLooping"
  return="boolean"
  abstract="false"
@@ -69741,6 +73481,17 @@
  visibility="public"
 >
 </method>
+<method name="newRequest"
+ return="android.os.Parcel"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="pause"
  return="void"
  abstract="false"
@@ -101472,6 +105223,17 @@
  visibility="public"
 >
 </field>
+<field name="GROUP_SYNC_ACCOUNT_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;group_sync_account_type&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="GROUP_SYNC_ID"
  type="java.lang.String"
  transient="false"
@@ -103414,6 +107176,17 @@
  visibility="public"
 >
 </field>
+<field name="_SYNC_ACCOUNT_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;_sync_account_type&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </interface>
 <class name="LiveFolders"
  extends="java.lang.Object"
@@ -107421,6 +111194,17 @@
  visibility="public"
 >
 </field>
+<field name="ALARM_ALERT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;alarm_alert&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ALWAYS_FINISH_ACTIVITIES"
  type="java.lang.String"
  transient="false"
@@ -107541,6 +111325,16 @@
  visibility="public"
 >
 </field>
+<field name="DEFAULT_ALARM_ALERT_URI"
+ type="android.net.Uri"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="DEFAULT_NOTIFICATION_URI"
  type="android.net.Uri"
  transient="false"
@@ -114073,7 +117867,7 @@
 >
 <parameter name="uri" type="android.net.Uri">
 </parameter>
-<parameter name="account" type="java.lang.String">
+<parameter name="accountName" type="java.lang.String">
 </parameter>
 <parameter name="authority" type="java.lang.String">
 </parameter>
@@ -116313,6 +120107,19 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
+<method name="getInstallerPackageName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+</method>
 <method name="getInstrumentationInfo"
  return="android.content.pm.InstrumentationInfo"
  abstract="false"
@@ -122812,6 +126619,27 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
+<method name="formatDateRange"
+ return="java.util.Formatter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="formatter" type="java.util.Formatter">
+</parameter>
+<parameter name="startMillis" type="long">
+</parameter>
+<parameter name="endMillis" type="long">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
 <method name="formatDateTime"
  return="java.lang.String"
  abstract="false"
@@ -155328,6 +159156,25 @@
 <parameter name="contentLength" type="long">
 </parameter>
 </method>
+<method name="onExceededDatabaseQuota"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="java.lang.String">
+</parameter>
+<parameter name="databaseIdentifier" type="java.lang.String">
+</parameter>
+<parameter name="currentQuota" type="long">
+</parameter>
+<parameter name="quotaUpdater" type="android.webkit.WebStorage.QuotaUpdater">
+</parameter>
+</method>
 <method name="onFormResubmission"
  return="void"
  abstract="false"
@@ -156969,6 +160816,25 @@
 <parameter name="resultMsg" type="android.os.Message">
 </parameter>
 </method>
+<method name="onExceededDatabaseQuota"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="java.lang.String">
+</parameter>
+<parameter name="databaseIdentifier" type="java.lang.String">
+</parameter>
+<parameter name="currentQuota" type="long">
+</parameter>
+<parameter name="quotaUpdater" type="android.webkit.WebStorage.QuotaUpdater">
+</parameter>
+</method>
 <method name="onJsAlert"
  return="boolean"
  abstract="false"
@@ -157354,6 +161220,28 @@
  visibility="public"
 >
 </method>
+<method name="getDatabaseEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDatabasePath"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getDefaultFixedFontSize"
  return="int"
  abstract="false"
@@ -157592,7 +161480,7 @@
  synchronized="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -157694,6 +161582,32 @@
 <parameter name="font" type="java.lang.String">
 </parameter>
 </method>
+<method name="setDatabaseEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flag" type="boolean">
+</parameter>
+</method>
+<method name="setDatabasePath"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="databasePath" type="java.lang.String">
+</parameter>
+</method>
 <method name="setDefaultFixedFontSize"
  return="void"
  abstract="false"
@@ -158026,7 +161940,7 @@
  synchronized="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="use" type="boolean">
@@ -158248,6 +162162,44 @@
 >
 </method>
 </class>
+<class name="WebStorage"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="WebStorage"
+ type="android.webkit.WebStorage"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+</class>
+<interface name="WebStorage.QuotaUpdater"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="updateQuota"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="newQuota" type="long">
+</parameter>
+</method>
+</interface>
 <class name="WebSyncManager"
  extends="java.lang.Object"
  abstract="true"
@@ -162351,6 +166303,17 @@
  visibility="public"
 >
 </method>
+<method name="getDropDownBackground"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getDropDownHeight"
  return="int"
  abstract="false"
@@ -162362,6 +166325,28 @@
  visibility="public"
 >
 </method>
+<method name="getDropDownHorizontalOffset"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDropDownVerticalOffset"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getDropDownWidth"
  return="int"
  abstract="false"
@@ -162585,6 +166570,32 @@
 <parameter name="id" type="int">
 </parameter>
 </method>
+<method name="setDropDownBackgroundDrawable"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="d" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setDropDownBackgroundResource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
 <method name="setDropDownHeight"
  return="void"
  abstract="false"
@@ -162598,6 +166609,32 @@
 <parameter name="height" type="int">
 </parameter>
 </method>
+<method name="setDropDownHorizontalOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
+<method name="setDropDownVerticalOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
 <method name="setDropDownWidth"
  return="void"
  abstract="false"
@@ -172274,6 +176311,30 @@
 <parameter name="isExpanded" type="boolean">
 </parameter>
 </method>
+<method name="getViewBinder"
+ return="android.widget.SimpleCursorTreeAdapter.ViewBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setViewBinder"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewBinder" type="android.widget.SimpleCursorTreeAdapter.ViewBinder">
+</parameter>
+</method>
 <method name="setViewImage"
  return="void"
  abstract="false"
@@ -172289,7 +176350,47 @@
 <parameter name="value" type="java.lang.String">
 </parameter>
 </method>
+<method name="setViewText"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="v" type="android.widget.TextView">
+</parameter>
+<parameter name="text" type="java.lang.String">
+</parameter>
+</method>
 </class>
+<interface name="SimpleCursorTreeAdapter.ViewBinder"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="setViewValue"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
+</interface>
 <class name="SimpleExpandableListAdapter"
  extends="android.widget.BaseExpandableListAdapter"
  abstract="false"
@@ -283403,7 +287504,11 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="val" type="int">
+<parameter name="buf" type="byte[]">
+</parameter>
+<parameter name="off" type="int">
+</parameter>
+<parameter name="nbytes" type="int">
 </parameter>
 </method>
 <method name="update"
@@ -283416,11 +287521,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="buf" type="byte[]">
-</parameter>
-<parameter name="off" type="int">
-</parameter>
-<parameter name="nbytes" type="int">
+<parameter name="val" type="int">
 </parameter>
 </method>
 </interface>
diff --git a/camera/libcameraservice/Android.mk b/camera/libcameraservice/Android.mk
index 96cc512..be78a62 100644
--- a/camera/libcameraservice/Android.mk
+++ b/camera/libcameraservice/Android.mk
@@ -42,6 +42,7 @@
 LOCAL_SHARED_LIBRARIES:= \
     libui \
     libutils \
+    libbinder \
     libcutils \
     libmedia
 
diff --git a/camera/libcameraservice/CameraHardwareStub.h b/camera/libcameraservice/CameraHardwareStub.h
index 0d26d47..efae935 100644
--- a/camera/libcameraservice/CameraHardwareStub.h
+++ b/camera/libcameraservice/CameraHardwareStub.h
@@ -21,8 +21,8 @@
 #include "FakeCamera.h"
 #include <utils/threads.h>
 #include <ui/CameraHardwareInterface.h>
-#include <utils/MemoryBase.h>
-#include <utils/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
 #include <utils/threads.h>
 
 namespace android {
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index 022fe5a..030d887 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -20,12 +20,12 @@
 #define LOG_TAG "CameraService"
 #include <utils/Log.h>
 
-#include <utils/IServiceManager.h>
-#include <utils/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
 #include <utils/String16.h>
 #include <utils/Errors.h>
-#include <utils/MemoryBase.h>
-#include <utils/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
 #include <ui/ICameraService.h>
 
 #include <media/mediaplayer.h>
@@ -1161,12 +1161,6 @@
 }
 
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t CameraService::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index d825d5a..7decf9a 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -7,8 +7,8 @@
 
 #define LOG_TAG "appproc"
 
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
 #include <utils/Log.h>
 #include <cutils/process_name.h>
 #include <cutils/memory.h>
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 3b9db8d..d565dc1 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -22,7 +22,7 @@
 #include <fcntl.h>
 #include <utils/misc.h>
 
-#include <utils/IPCThreadState.h>
+#include <binder/IPCThreadState.h>
 #include <utils/threads.h>
 #include <utils/Atomic.h>
 #include <utils/Errors.h>
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index 675ea81..346f156 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -16,9 +16,9 @@
 
 #define LOG_TAG "BootAnimation"
 
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
 #include <utils/Log.h>
 #include <utils/threads.h>
 
diff --git a/cmds/dumpsys/Android.mk b/cmds/dumpsys/Android.mk
index 0c623cc..42b1b73 100644
--- a/cmds/dumpsys/Android.mk
+++ b/cmds/dumpsys/Android.mk
@@ -5,7 +5,9 @@
 	dumpsys.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libutils
+	libutils \
+	libbinder
+	
 
 ifeq ($(TARGET_OS),linux)
 	LOCAL_CFLAGS += -DXP_UNIX
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index a62fe55..945a690 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -6,9 +6,9 @@
 #define LOG_TAG "dumpsys"
 
 #include <utils/Log.h>
-#include <utils/Parcel.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
 #include <utils/TextOutput.h>
 #include <utils/Vector.h>
 
diff --git a/cmds/runtime/Android.mk b/cmds/runtime/Android.mk
index 521eb2b..6a72d10 100644
--- a/cmds/runtime/Android.mk
+++ b/cmds/runtime/Android.mk
@@ -10,6 +10,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
 	libutils \
+	libbinder \
 	libandroid_runtime \
 	libcutils \
 	libui \
diff --git a/cmds/runtime/ServiceManager.cpp b/cmds/runtime/ServiceManager.cpp
index 758a95c..b2bef07 100644
--- a/cmds/runtime/ServiceManager.cpp
+++ b/cmds/runtime/ServiceManager.cpp
@@ -9,9 +9,9 @@
 
 #include <utils/Debug.h>
 #include <utils/Log.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 #include <utils/String8.h>
-#include <utils/ProcessState.h>
+#include <binder/ProcessState.h>
 
 #include <private/utils/Static.h>
 
diff --git a/cmds/runtime/ServiceManager.h b/cmds/runtime/ServiceManager.h
index d09cec8..090ca6d 100644
--- a/cmds/runtime/ServiceManager.h
+++ b/cmds/runtime/ServiceManager.h
@@ -4,7 +4,7 @@
 #ifndef ANDROID_SERVICE_MANAGER_H
 #define ANDROID_SERVICE_MANAGER_H
 
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
 
diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp
index 476f38a..21e0e4d 100644
--- a/cmds/runtime/main_runtime.cpp
+++ b/cmds/runtime/main_runtime.cpp
@@ -7,9 +7,11 @@
 #include "ServiceManager.h"
 #include "SignalHandler.h"
 
-#include <utils.h>
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
+#include <utils/threads.h>
+#include <utils/Errors.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
 #include <utils/Log.h>  
 #include <cutils/zygote.h>
 
diff --git a/cmds/service/Android.mk b/cmds/service/Android.mk
index 8c5005c..275bbb2 100644
--- a/cmds/service/Android.mk
+++ b/cmds/service/Android.mk
@@ -4,8 +4,7 @@
 LOCAL_SRC_FILES:= \
 	service.cpp
 
-LOCAL_SHARED_LIBRARIES := \
-	libutils
+LOCAL_SHARED_LIBRARIES := libutils libbinder
 
 ifeq ($(TARGET_OS),linux)
 	LOCAL_CFLAGS += -DXP_UNIX
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index 859a9bf..32db83b 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -3,9 +3,9 @@
  *
  */
  
-#include <utils/Parcel.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
 #include <utils/TextOutput.h>
 
 #include <getopt.h>
diff --git a/cmds/surfaceflinger/Android.mk b/cmds/surfaceflinger/Android.mk
index 37c3d94..bfa58a1 100644
--- a/cmds/surfaceflinger/Android.mk
+++ b/cmds/surfaceflinger/Android.mk
@@ -6,6 +6,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
 	libsurfaceflinger \
+	libbinder \
 	libutils
 
 LOCAL_C_INCLUDES := \
diff --git a/cmds/surfaceflinger/main_surfaceflinger.cpp b/cmds/surfaceflinger/main_surfaceflinger.cpp
index 7c89578..d650721 100644
--- a/cmds/surfaceflinger/main_surfaceflinger.cpp
+++ b/cmds/surfaceflinger/main_surfaceflinger.cpp
@@ -1,6 +1,6 @@
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
 #include <utils/Log.h>
 
 #include <SurfaceFlinger.h>
diff --git a/cmds/system_server/Android.mk b/cmds/system_server/Android.mk
index 0a684e8..ad537977 100644
--- a/cmds/system_server/Android.mk
+++ b/cmds/system_server/Android.mk
@@ -6,6 +6,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
 	libutils \
+	libbinder \
 	libsystem_server 
 
 LOCAL_C_INCLUDES := \
diff --git a/cmds/system_server/library/Android.mk b/cmds/system_server/library/Android.mk
index 580331a..1813d3e 100644
--- a/cmds/system_server/library/Android.mk
+++ b/cmds/system_server/library/Android.mk
@@ -20,6 +20,7 @@
     libcameraservice \
     libmediaplayerservice \
 	libutils \
+	libbinder \
 	libcutils
 
 LOCAL_MODULE:= libsystem_server
diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp
index 73b23e2..ea78461 100644
--- a/cmds/system_server/library/system_init.cpp
+++ b/cmds/system_server/library/system_init.cpp
@@ -8,9 +8,9 @@
 
 #define LOG_TAG "sysproc"
 
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
 #include <utils/TextOutput.h>
 #include <utils/Log.h>
 
diff --git a/cmds/system_server/system_main.cpp b/cmds/system_server/system_main.cpp
index ca16e57..543f650 100644
--- a/cmds/system_server/system_main.cpp
+++ b/cmds/system_server/system_main.cpp
@@ -9,7 +9,7 @@
 
 #define LOG_TAG "sysproc"
 
-#include <utils/IPCThreadState.h>
+#include <binder/IPCThreadState.h>
 #include <utils/Log.h>
 
 #include <private/android_filesystem_config.h>
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
new file mode 100644
index 0000000..474755c
--- /dev/null
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+
+/**
+ * Base class for creating AccountAuthenticators. This implements the IAccountAuthenticator
+ * binder interface and also provides helper libraries to simplify the creation of
+ * AccountAuthenticators.
+ */
+public abstract class AbstractAccountAuthenticator {
+    class Transport extends IAccountAuthenticator.Stub {
+        public void addAccount(IAccountAuthenticatorResponse response, String accountType,
+                String authTokenType, String[] requiredFeatures, Bundle options)
+                throws RemoteException {
+            final Bundle result;
+            try {
+                result = AbstractAccountAuthenticator.this.addAccount(
+                    new AccountAuthenticatorResponse(response),
+                        accountType, authTokenType, requiredFeatures, options);
+            } catch (NetworkErrorException e) {
+                response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+                return;
+            } catch (UnsupportedOperationException e) {
+                response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+                        "addAccount not supported");
+                return;
+            }
+            if (result != null) {
+                response.onResult(result);
+            }
+        }
+
+        public void confirmPassword(IAccountAuthenticatorResponse response,
+                Account account, String password) throws RemoteException {
+            boolean result;
+            try {
+                result = AbstractAccountAuthenticator.this.confirmPassword(
+                    new AccountAuthenticatorResponse(response),
+                        account, password);
+            } catch (UnsupportedOperationException e) {
+                response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+                        "confirmPassword not supported");
+                return;
+            } catch (NetworkErrorException e) {
+                response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+                return;
+            }
+            Bundle bundle = new Bundle();
+            bundle.putBoolean(Constants.BOOLEAN_RESULT_KEY, result);
+            response.onResult(bundle);
+        }
+
+        public void confirmCredentials(IAccountAuthenticatorResponse response,
+                Account account) throws RemoteException {
+            final Bundle result;
+            try {
+                result = AbstractAccountAuthenticator.this.confirmCredentials(
+                    new AccountAuthenticatorResponse(response), account);
+            } catch (UnsupportedOperationException e) {
+                response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+                        "confirmCredentials not supported");
+                return;
+            }
+            if (result != null) {
+                response.onResult(result);
+            }
+        }
+
+        public void getAuthToken(IAccountAuthenticatorResponse response,
+                Account account, String authTokenType, Bundle loginOptions)
+                throws RemoteException {
+            try {
+                final Bundle result = AbstractAccountAuthenticator.this.getAuthToken(
+                        new AccountAuthenticatorResponse(response), account,
+                        authTokenType, loginOptions);
+                if (result != null) {
+                    response.onResult(result);
+                }
+            } catch (UnsupportedOperationException e) {
+                response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+                        "getAuthToken not supported");
+            } catch (NetworkErrorException e) {
+                response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+            }
+        }
+
+        public void updateCredentials(IAccountAuthenticatorResponse response, Account account,
+                String authTokenType, Bundle loginOptions) throws RemoteException {
+            final Bundle result;
+            try {
+                result = AbstractAccountAuthenticator.this.updateCredentials(
+                    new AccountAuthenticatorResponse(response), account,
+                        authTokenType, loginOptions);
+            } catch (UnsupportedOperationException e) {
+                response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+                        "updateCredentials not supported");
+                return;
+            }
+            if (result != null) {
+                response.onResult(result);
+            }
+        }
+
+        public void editProperties(IAccountAuthenticatorResponse response,
+                String accountType) throws RemoteException {
+            final Bundle result;
+            try {
+                result = AbstractAccountAuthenticator.this.editProperties(
+                    new AccountAuthenticatorResponse(response), accountType);
+            } catch (UnsupportedOperationException e) {
+                response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+                        "editProperties not supported");
+                return;
+            }
+            if (result != null) {
+                response.onResult(result);
+            }
+        }
+
+        public void hasFeatures(IAccountAuthenticatorResponse response,
+                Account account, String[] features) throws RemoteException {
+            final Bundle result;
+            try {
+                result = AbstractAccountAuthenticator.this.hasFeatures(
+                    new AccountAuthenticatorResponse(response), account, features);
+            } catch (UnsupportedOperationException e) {
+                response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+                        "hasFeatures not supported");
+                return;
+            } catch (NetworkErrorException e) {
+                response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+                return;
+            }
+            if (result != null) {
+                response.onResult(result);
+            }
+        }
+    }
+
+    Transport mTransport = new Transport();
+
+    /**
+     * @return the IAccountAuthenticator binder transport object
+     */
+    public final IAccountAuthenticator getIAccountAuthenticator()
+    {
+        return mTransport;
+    }
+
+    /**
+     * Returns a Bundle that contains the Intent of the activity that can be used to edit the
+     * properties. In order to indicate success the activity should call response.setResult()
+     * with a non-null Bundle.
+     * @param response used to set the result for the request. If the Constants.INTENT_KEY
+     *   is set in the bundle then this response field is to be used for sending future
+     *   results if and when the Intent is started.
+     * @param accountType the AccountType whose properties are to be edited.
+     * @return a Bundle containing the result or the Intent to start to continue the request.
+     *   If this is null then the request is considered to still be active and the result should
+     *   sent later using response.
+     */
+    public abstract Bundle editProperties(AccountAuthenticatorResponse response,
+            String accountType);
+    public abstract Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
+            String authTokenType, String[] requiredFeatures, Bundle options)
+            throws NetworkErrorException;
+    /* @deprecated */
+    public abstract boolean confirmPassword(AccountAuthenticatorResponse response,
+            Account account, String password) throws NetworkErrorException;
+    public abstract Bundle confirmCredentials(AccountAuthenticatorResponse response,
+            Account account);
+    public abstract Bundle getAuthToken(AccountAuthenticatorResponse response,
+            Account account, String authTokenType, Bundle loginOptions)
+            throws NetworkErrorException;
+    public abstract Bundle updateCredentials(AccountAuthenticatorResponse response,
+            Account account, String authTokenType, Bundle loginOptions);
+    public abstract Bundle hasFeatures(AccountAuthenticatorResponse response,
+            Account account, String[] features) throws NetworkErrorException;
+}
diff --git a/include/utils/executablepath.h b/core/java/android/accounts/Account.aidl
similarity index 65%
copy from include/utils/executablepath.h
copy to core/java/android/accounts/Account.aidl
index c979432..8752d99 100644
--- a/include/utils/executablepath.h
+++ b/core/java/android/accounts/Account.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,6 @@
  * limitations under the License.
  */
 
-#ifndef _UTILS_EXECUTABLEPATH_H
-#define _UTILS_EXECUTABLEPATH_H
+package android.accounts;
 
-#include <limits.h>
-
-// returns the path to this executable
-#if __cplusplus
-extern "C"
-#endif
-void executablepath(char s[PATH_MAX]);
-
-#endif // _UTILS_EXECUTABLEPATH_H
+parcelable Account;
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
new file mode 100644
index 0000000..30c91b0
--- /dev/null
+++ b/core/java/android/accounts/Account.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+/**
+ * Value type that represents an Account in the {@link AccountManager}. This object is
+ * {@link Parcelable} and also overrides {@link #equals} and {@link #hashCode}, making it
+ * suitable for use as the key of a {@link java.util.Map}
+ */
+public class Account implements Parcelable {
+    public final String mName;
+    public final String mType;
+
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (!(o instanceof Account)) return false;
+        final Account other = (Account)o;
+        return mName.equals(other.mName) && mType.equals(other.mType);
+    }
+
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + mName.hashCode();
+        result = 31 * result + mType.hashCode();
+        return result;
+    }
+
+    public Account(String name, String type) {
+        if (TextUtils.isEmpty(name)) {
+            throw new IllegalArgumentException("the name must not be empty: " + name);
+        }
+        if (TextUtils.isEmpty(type)) {
+            throw new IllegalArgumentException("the type must not be empty: " + type);
+        }
+        mName = name;
+        mType = type;
+    }
+
+    public Account(Parcel in) {
+        mName = in.readString();
+        mType = in.readString();
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mName);
+        dest.writeString(mType);
+    }
+
+    public static final Creator<Account> CREATOR = new Creator<Account>() {
+        public Account createFromParcel(Parcel source) {
+            return new Account(source);
+        }
+
+        public Account[] newArray(int size) {
+            return new Account[size];
+        }
+    };
+
+    public String toString() {
+        return "Account {name=" + mName + ", type=" + mType + "}";
+    }
+}
diff --git a/core/java/android/accounts/AccountAuthenticatorActivity.java b/core/java/android/accounts/AccountAuthenticatorActivity.java
new file mode 100644
index 0000000..0319ab9
--- /dev/null
+++ b/core/java/android/accounts/AccountAuthenticatorActivity.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Base class for implementing an Activity that is used to help implement an
+ * AbstractAccountAuthenticator. If the AbstractAccountAuthenticator needs to return an Intent
+ * that is to be used to launch an Activity that needs to return results to satisfy an
+ * AbstractAccountAuthenticator request, it should store the AccountAuthenticatorResponse
+ * inside of the Intent as follows:
+ * <p>
+ *      intent.putExtra(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY, response);
+ * <p>
+ * The activity that it launches should extend the AccountAuthenticatorActivity. If this
+ * activity has a result that satisfies the original request it sets it via:
+ * <p>
+ *       setAccountAuthenticatorResult(result)
+ * <p>
+ * This result will be sent as the result of the request when the activity finishes. If this
+ * is never set or if it is set to null then the request will be canceled when the activity
+ * finishes.
+ */
+public class AccountAuthenticatorActivity extends Activity {
+    private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null;
+    private Bundle mResultBundle = null;
+
+    /**
+     * Set the result that is to be sent as the result of the request that caused this
+     * Activity to be launched. If result is null or this method is never called then
+     * the request will be canceled.
+     * @param result this is returned as the result of the AbstractAccountAuthenticator request
+     */
+    public final void setAccountAuthenticatorResult(Bundle result) {
+        mResultBundle = result;
+    }
+
+    /**
+     * Retreives the AccountAuthenticatorResponse from either the intent of the icicle, if the
+     * icicle is non-zero.
+     * @param icicle the save instance data of this Activity, may be null
+     */
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        if (icicle == null) {
+            Intent intent = getIntent();
+            mAccountAuthenticatorResponse =
+                    intent.getParcelableExtra(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY);
+        } else {
+            mAccountAuthenticatorResponse =
+                    icicle.getParcelable(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY);
+        }
+
+        if (mAccountAuthenticatorResponse != null) {
+            mAccountAuthenticatorResponse.onRequestContinued();
+        }
+    }
+
+    /**
+     * Saves the AccountAuthenticatorResponse in the instance state.
+     * @param outState where to store any instance data
+     */
+    protected void onSaveInstanceState(Bundle outState) {
+        outState.putParcelable(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY,
+                mAccountAuthenticatorResponse);
+        super.onSaveInstanceState(outState);
+    }
+
+    /**
+     * Sends the result or a Constants.ERROR_CODE_CANCELED error if a result isn't present.
+     */
+    public void finish() {
+        if (mAccountAuthenticatorResponse != null) {
+            // send the result bundle back if set, otherwise send an error.
+            if (mResultBundle != null) {
+                mAccountAuthenticatorResponse.onResult(mResultBundle);
+            } else {
+                mAccountAuthenticatorResponse.onError(Constants.ERROR_CODE_CANCELED, "canceled");
+            }
+            mAccountAuthenticatorResponse = null;
+        }
+        super.finish();
+    }
+}
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java
new file mode 100644
index 0000000..c8fc12c
--- /dev/null
+++ b/core/java/android/accounts/AccountAuthenticatorCache.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.content.pm.PackageManager;
+import android.content.pm.RegisteredServicesCache;
+import android.content.res.TypedArray;
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * A cache of services that export the {@link IAccountAuthenticator} interface. This cache
+ * is built by interrogating the {@link PackageManager} and is updated as packages are added,
+ * removed and changed. The authenticators are referred to by their account type and
+ * are made available via the {@link RegisteredServicesCache#getServiceInfo} method.
+ * @hide
+ */
+/* package private */ class AccountAuthenticatorCache
+        extends RegisteredServicesCache<AuthenticatorDescription> {
+    private static final String TAG = "Account";
+
+    private static final String SERVICE_INTERFACE = "android.accounts.AccountAuthenticator";
+    private static final String SERVICE_META_DATA = "android.accounts.AccountAuthenticator";
+    private static final String ATTRIBUTES_NAME = "account-authenticator";
+
+    public AccountAuthenticatorCache(Context context) {
+        super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME);
+    }
+
+    public AuthenticatorDescription parseServiceAttributes(String packageName, AttributeSet attrs) {
+        TypedArray sa = mContext.getResources().obtainAttributes(attrs,
+                com.android.internal.R.styleable.AccountAuthenticator);
+        try {
+            final String accountType =
+                    sa.getString(com.android.internal.R.styleable.AccountAuthenticator_accountType);
+            final int labelId = sa.getResourceId(
+                    com.android.internal.R.styleable.AccountAuthenticator_label, 0);
+            final int iconId = sa.getResourceId(
+                    com.android.internal.R.styleable.AccountAuthenticator_icon, 0);
+            return new AuthenticatorDescription(accountType, packageName, labelId, iconId);
+        } finally {
+            sa.recycle();
+        }
+    }
+}
diff --git a/core/java/android/accounts/AccountAuthenticatorResponse.java b/core/java/android/accounts/AccountAuthenticatorResponse.java
new file mode 100644
index 0000000..7198046
--- /dev/null
+++ b/core/java/android/accounts/AccountAuthenticatorResponse.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.os.RemoteException;
+
+/**
+ * Object that wraps calls to an {@link IAccountAuthenticatorResponse} object.
+ * TODO: this interface is still in flux
+ */
+public class AccountAuthenticatorResponse implements Parcelable {
+    private IAccountAuthenticatorResponse mAccountAuthenticatorResponse;
+
+    public AccountAuthenticatorResponse(IAccountAuthenticatorResponse response) {
+        mAccountAuthenticatorResponse = response;
+    }
+
+    public AccountAuthenticatorResponse(Parcel parcel) {
+        mAccountAuthenticatorResponse =
+                IAccountAuthenticatorResponse.Stub.asInterface(parcel.readStrongBinder());
+    }
+
+    public void onResult(Bundle result) {
+        try {
+            mAccountAuthenticatorResponse.onResult(result);
+        } catch (RemoteException e) {
+            // this should never happen
+        }
+    }
+
+    public void onRequestContinued() {
+        try {
+            mAccountAuthenticatorResponse.onRequestContinued();
+        } catch (RemoteException e) {
+            // this should never happen
+        }
+    }
+
+    public void onError(int errorCode, String errorMessage) {
+        try {
+            mAccountAuthenticatorResponse.onError(errorCode, errorMessage);
+        } catch (RemoteException e) {
+            // this should never happen
+        }
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStrongBinder(mAccountAuthenticatorResponse.asBinder());
+    }
+
+    public static final Creator<AccountAuthenticatorResponse> CREATOR =
+            new Creator<AccountAuthenticatorResponse>() {
+        public AccountAuthenticatorResponse createFromParcel(Parcel source) {
+            return new AccountAuthenticatorResponse(source);
+        }
+
+        public AccountAuthenticatorResponse[] newArray(int size) {
+            return new AccountAuthenticatorResponse[size];
+        }
+    };
+}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
new file mode 100644
index 0000000..5182f2e
--- /dev/null
+++ b/core/java/android/accounts/AccountManager.java
@@ -0,0 +1,1073 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.BroadcastReceiver;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.Parcelable;
+import android.util.Config;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.android.collect.Maps;
+
+/**
+ * A class that helps with interactions with the AccountManagerService. It provides
+ * methods to allow for account, password, and authtoken management for all accounts on the
+ * device. Some of these calls are implemented with the help of the corresponding
+ * {@link IAccountAuthenticator} services. One accesses the {@link AccountManager} by calling:
+ *    AccountManager accountManager = AccountManager.get(context);
+ *
+ * <p>
+ * TODO(fredq) this interface is still in flux
+ */
+public class AccountManager {
+    private static final String TAG = "AccountManager";
+
+    private final Context mContext;
+    private final IAccountManager mService;
+    private final Handler mMainHandler;
+
+    /**
+     * @hide
+     */
+    public AccountManager(Context context, IAccountManager service) {
+        mContext = context;
+        mService = service;
+        mMainHandler = new Handler(mContext.getMainLooper());
+    }
+
+    /**
+     * @hide used for testing only
+     */
+    public AccountManager(Context context, IAccountManager service, Handler handler) {
+        mContext = context;
+        mService = service;
+        mMainHandler = handler;
+    }
+
+    public static AccountManager get(Context context) {
+        return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
+    }
+
+    public String blockingGetPassword(Account account) {
+        ensureNotOnMainThread();
+        try {
+            return mService.getPassword(account);
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Future1<String> getPassword(final Future1Callback<String> callback,
+            final Account account, final Handler handler) {
+        return startAsFuture(callback, handler, new Callable<String>() {
+            public String call() throws Exception {
+                return blockingGetPassword(account);
+            }
+        });
+    }
+
+    public String blockingGetUserData(Account account, String key) {
+        ensureNotOnMainThread();
+        try {
+            return mService.getUserData(account, key);
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Future1<String> getUserData(Future1Callback<String> callback,
+            final Account account, final String key, Handler handler) {
+        return startAsFuture(callback, handler, new Callable<String>() {
+            public String call() throws Exception {
+                return blockingGetUserData(account, key);
+            }
+        });
+    }
+
+    public AuthenticatorDescription[] blockingGetAuthenticatorTypes() {
+        ensureNotOnMainThread();
+        try {
+            return mService.getAuthenticatorTypes();
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Future1<AuthenticatorDescription[]> getAuthenticatorTypes(
+            Future1Callback<AuthenticatorDescription[]> callback, Handler handler) {
+        return startAsFuture(callback, handler, new Callable<AuthenticatorDescription[]>() {
+            public AuthenticatorDescription[] call() throws Exception {
+                return blockingGetAuthenticatorTypes();
+            }
+        });
+    }
+
+    public Account[] blockingGetAccounts() {
+        ensureNotOnMainThread();
+        try {
+            return mService.getAccounts();
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Account[] blockingGetAccountsByType(String accountType) {
+        ensureNotOnMainThread();
+        try {
+            return mService.getAccountsByType(accountType);
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Future1<Account[]> getAccounts(Future1Callback<Account[]> callback, Handler handler) {
+        return startAsFuture(callback, handler, new Callable<Account[]>() {
+            public Account[] call() throws Exception {
+                return blockingGetAccounts();
+            }
+        });
+    }
+
+    public Future1<Account[]> getAccountsByType(Future1Callback<Account[]> callback,
+            final String type, Handler handler) {
+        return startAsFuture(callback, handler, new Callable<Account[]>() {
+            public Account[] call() throws Exception {
+                return blockingGetAccountsByType(type);
+            }
+        });
+    }
+
+    public boolean blockingAddAccountExplicitly(Account account, String password, Bundle extras) {
+        ensureNotOnMainThread();
+        try {
+            return mService.addAccount(account, password, extras);
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Future1<Boolean> addAccountExplicitly(final Future1Callback<Boolean> callback,
+            final Account account, final String password, final Bundle extras,
+            final Handler handler) {
+        return startAsFuture(callback, handler, new Callable<Boolean>() {
+            public Boolean call() throws Exception {
+                return blockingAddAccountExplicitly(account, password, extras);
+            }
+        });
+    }
+
+    public void blockingRemoveAccount(Account account) {
+        ensureNotOnMainThread();
+        try {
+            mService.removeAccount(account);
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+        }
+    }
+
+    public Future1<Void> removeAccount(Future1Callback<Void> callback, final Account account,
+            final Handler handler) {
+        return startAsFuture(callback, handler, new Callable<Void>() {
+            public Void call() throws Exception {
+                blockingRemoveAccount(account);
+                return null;
+            }
+        });
+    }
+
+    public void blockingInvalidateAuthToken(String accountType, String authToken) {
+        ensureNotOnMainThread();
+        try {
+            mService.invalidateAuthToken(accountType, authToken);
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+        }
+    }
+
+    public Future1<Void> invalidateAuthToken(Future1Callback<Void> callback,
+            final String accountType, final String authToken, final Handler handler) {
+        return startAsFuture(callback, handler, new Callable<Void>() {
+            public Void call() throws Exception {
+                blockingInvalidateAuthToken(accountType, authToken);
+                return null;
+            }
+        });
+    }
+
+    public String blockingPeekAuthToken(Account account, String authTokenType) {
+        ensureNotOnMainThread();
+        try {
+            return mService.peekAuthToken(account, authTokenType);
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Future1<String> peekAuthToken(Future1Callback<String> callback,
+            final Account account, final String authTokenType, final Handler handler) {
+        return startAsFuture(callback, handler, new Callable<String>() {
+            public String call() throws Exception {
+                return blockingPeekAuthToken(account, authTokenType);
+            }
+        });
+    }
+
+    public void blockingSetPassword(Account account, String password) {
+        ensureNotOnMainThread();
+        try {
+            mService.setPassword(account, password);
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+        }
+    }
+
+    public Future1<Void> setPassword(Future1Callback<Void> callback,
+            final Account account, final String password, final Handler handler) {
+        return startAsFuture(callback, handler, new Callable<Void>() {
+            public Void call() throws Exception {
+                blockingSetPassword(account, password);
+                return null;
+            }
+        });
+    }
+
+    public void blockingClearPassword(Account account) {
+        ensureNotOnMainThread();
+        try {
+            mService.clearPassword(account);
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+        }
+    }
+
+    public Future1<Void> clearPassword(final Future1Callback<Void> callback, final Account account,
+            final Handler handler) {
+        return startAsFuture(callback, handler, new Callable<Void>() {
+            public Void call() throws Exception {
+                blockingClearPassword(account);
+                return null;
+            }
+        });
+    }
+
+    public void blockingSetUserData(Account account, String key, String value) {
+        ensureNotOnMainThread();
+        try {
+            mService.setUserData(account, key, value);
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+        }
+    }
+
+    public Future1<Void> setUserData(Future1Callback<Void> callback,
+            final Account account, final String key, final String value, final Handler handler) {
+        return startAsFuture(callback, handler, new Callable<Void>() {
+            public Void call() throws Exception {
+                blockingSetUserData(account, key, value);
+                return null;
+            }
+        });
+    }
+
+    public void blockingSetAuthToken(Account account, String authTokenType, String authToken) {
+        ensureNotOnMainThread();
+        try {
+            mService.setAuthToken(account, authTokenType, authToken);
+        } catch (RemoteException e) {
+            // if this happens the entire runtime will restart
+        }
+    }
+
+    public Future1<Void> setAuthToken(Future1Callback<Void> callback,
+            final Account account, final String authTokenType, final String authToken,
+            final Handler handler) {
+        return startAsFuture(callback, handler, new Callable<Void>() {
+            public Void call() throws Exception {
+                blockingSetAuthToken(account, authTokenType, authToken);
+                return null;
+            }
+        });
+    }
+
+    public String blockingGetAuthToken(Account account, String authTokenType,
+            boolean notifyAuthFailure)
+            throws OperationCanceledException, IOException, AuthenticatorException {
+        ensureNotOnMainThread();
+        Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */,
+                null /* handler */).getResult();
+        return bundle.getString(Constants.AUTHTOKEN_KEY);
+    }
+
+    /**
+     * Request the auth token for this account/authTokenType. If this succeeds then the
+     * auth token will then be passed to the activity. If this results in an authentication
+     * failure then a login intent will be returned that can be invoked to prompt the user to
+     * update their credentials. This login activity will return the auth token to the calling
+     * activity. If activity is null then the login intent will not be invoked.
+     *
+     * @param account the account whose auth token should be retrieved
+     * @param authTokenType the auth token type that should be retrieved
+     * @param loginOptions
+     * @param activity the activity to launch the login intent, if necessary, and to which
+     */
+    public Future2 getAuthToken(
+            final Account account, final String authTokenType, final Bundle loginOptions,
+            final Activity activity, Future2Callback callback, Handler handler) {
+        if (activity == null) throw new IllegalArgumentException("activity is null");
+        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
+        return new AmsTask(activity, handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.getAuthToken(mResponse, account, authTokenType,
+                        false /* notifyOnAuthFailure */, true /* expectActivityLaunch */,
+                        loginOptions);
+            }
+        }.start();
+    }
+
+    public Future2 getAuthToken(
+            final Account account, final String authTokenType, final boolean notifyAuthFailure,
+            Future2Callback callback, Handler handler) {
+        if (account == null) throw new IllegalArgumentException("account is null");
+        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
+        return new AmsTask(null, handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.getAuthToken(mResponse, account, authTokenType,
+                        notifyAuthFailure, false /* expectActivityLaunch */, null /* options */);
+            }
+        }.start();
+    }
+
+    public Future2 addAccount(final String accountType,
+            final String authTokenType, final String[] requiredFeatures,
+            final Bundle addAccountOptions,
+            final Activity activity, Future2Callback callback, Handler handler) {
+        return new AmsTask(activity, handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.addAcount(mResponse, accountType, authTokenType,
+                        requiredFeatures, activity != null, addAccountOptions);
+            }
+        }.start();
+    }
+
+    /** @deprecated use {@link #confirmCredentials} instead */
+    public Future1<Boolean> confirmPassword(final Account account, final String password,
+            Future1Callback<Boolean> callback, Handler handler) {
+        return new AMSTaskBoolean(handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.confirmPassword(response, account, password);
+            }
+        };
+    }
+
+    public Account[] blockingGetAccountsWithTypeAndFeatures(String type, String[] features)
+            throws AuthenticatorException, IOException, OperationCanceledException {
+        Future2 future = getAccountsWithTypeAndFeatures(type, features,
+                null /* callback */, null /* handler */);
+        Bundle result = future.getResult();
+        Parcelable[] accountsTemp = result.getParcelableArray(Constants.ACCOUNTS_KEY);
+        if (accountsTemp == null) {
+            throw new AuthenticatorException("accounts should not be null");
+        }
+        Account[] accounts = new Account[accountsTemp.length];
+        for (int i = 0; i < accountsTemp.length; i++) {
+            accounts[i] = (Account) accountsTemp[i];
+        }
+        return accounts;
+    }
+
+    public Future2 getAccountsWithTypeAndFeatures(
+            final String type, final String[] features,
+            Future2Callback callback, Handler handler) {
+        if (type == null) throw new IllegalArgumentException("type is null");
+        return new AmsTask(null /* activity */, handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.getAccountsByTypeAndFeatures(mResponse, type, features);
+            }
+        }.start();
+    }
+
+    public Future2 confirmCredentials(final Account account, final Activity activity,
+            final Future2Callback callback,
+            final Handler handler) {
+        return new AmsTask(activity, handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.confirmCredentials(mResponse, account, activity != null);
+            }
+        }.start();
+    }
+
+    public Future2 updateCredentials(final Account account, final String authTokenType,
+            final Bundle loginOptions, final Activity activity,
+            final Future2Callback callback,
+            final Handler handler) {
+        return new AmsTask(activity, handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.updateCredentials(mResponse, account, authTokenType, activity != null,
+                        loginOptions);
+            }
+        }.start();
+    }
+
+    public Future2 editProperties(final String accountType, final Activity activity,
+            final Future2Callback callback,
+            final Handler handler) {
+        return new AmsTask(activity, handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.editProperties(mResponse, accountType, activity != null);
+            }
+        }.start();
+    }
+
+    private void ensureNotOnMainThread() {
+        final Looper looper = Looper.myLooper();
+        if (looper != null && looper == mContext.getMainLooper()) {
+            // We really want to throw an exception here, but GTalkService exercises this
+            // path quite a bit and needs some serious rewrite in order to work properly.
+            //noinspection ThrowableInstanceNeverThrow
+//            Log.e(TAG, "calling this from your main thread can lead to deadlock and/or ANRs",
+//                    new Exception());
+            // TODO(fredq) remove the log and throw this exception when the callers are fixed
+//            throw new IllegalStateException(
+//                    "calling this from your main thread can lead to deadlock");
+        }
+    }
+
+    private void postToHandler(Handler handler, final Future2Callback callback,
+            final Future2 future) {
+        handler = handler == null ? mMainHandler : handler;
+        handler.post(new Runnable() {
+            public void run() {
+                callback.run(future);
+            }
+        });
+    }
+
+    private void postToHandler(Handler handler, final OnAccountsUpdatedListener listener,
+            final Account[] accounts) {
+        handler = handler == null ? mMainHandler : handler;
+        handler.post(new Runnable() {
+            public void run() {
+                listener.onAccountsUpdated(accounts);
+            }
+        });
+    }
+
+    private <V> void postToHandler(Handler handler, final Future1Callback<V> callback,
+            final Future1<V> future) {
+        handler = handler == null ? mMainHandler : handler;
+        handler.post(new Runnable() {
+            public void run() {
+                callback.run(future);
+            }
+        });
+    }
+
+    private <V> Future1<V> startAsFuture(Future1Callback<V> callback, Handler handler,
+            Callable<V> callable) {
+        final FutureTaskWithCallback<V> task =
+                new FutureTaskWithCallback<V>(callback, callable, handler);
+        new Thread(task).start();
+        return task;
+    }
+
+    private class FutureTaskWithCallback<V> extends FutureTask<V> implements Future1<V> {
+        final Future1Callback<V> mCallback;
+        final Handler mHandler;
+
+        public FutureTaskWithCallback(Future1Callback<V> callback, Callable<V> callable,
+                Handler handler) {
+            super(callable);
+            mCallback = callback;
+            mHandler = handler;
+        }
+
+        protected void done() {
+            if (mCallback != null) {
+                postToHandler(mHandler, mCallback, this);
+            }
+        }
+
+        public V internalGetResult(Long timeout, TimeUnit unit) throws OperationCanceledException {
+            try {
+                if (timeout == null) {
+                    return get();
+                } else {
+                    return get(timeout, unit);
+                }
+            } catch (InterruptedException e) {
+                // we will cancel the task below
+            } catch (CancellationException e) {
+                // we will cancel the task below
+            } catch (TimeoutException e) {
+                // we will cancel the task below
+            } catch (ExecutionException e) {
+                // this should never happen
+                throw new IllegalStateException(e.getCause());
+            } finally {
+                cancel(true /* interruptIfRunning */);
+            }
+            throw new OperationCanceledException();
+        }
+
+        public V getResult() throws OperationCanceledException {
+            return internalGetResult(null, null);
+        }
+
+        public V getResult(long timeout, TimeUnit unit) throws OperationCanceledException {
+            return internalGetResult(null, null);
+        }
+    }
+
+    private abstract class AmsTask extends FutureTask<Bundle> implements Future2 {
+        final IAccountManagerResponse mResponse;
+        final Handler mHandler;
+        final Future2Callback mCallback;
+        final Activity mActivity;
+        final Thread mThread;
+        public AmsTask(Activity activity, Handler handler, Future2Callback callback) {
+            super(new Callable<Bundle>() {
+                public Bundle call() throws Exception {
+                    throw new IllegalStateException("this should never be called");
+                }
+            });
+
+            mHandler = handler;
+            mCallback = callback;
+            mActivity = activity;
+            mResponse = new Response();
+            mThread = new Thread(new Runnable() {
+                public void run() {
+                    try {
+                        doWork();
+                    } catch (RemoteException e) {
+                        // never happens
+                    }
+                }
+            }, "AmsTask");
+        }
+
+        public final Future2 start() {
+            mThread.start();
+            return this;
+        }
+
+        public abstract void doWork() throws RemoteException;
+
+        private Bundle internalGetResult(Long timeout, TimeUnit unit)
+                throws OperationCanceledException, IOException, AuthenticatorException {
+            try {
+                if (timeout == null) {
+                    return get();
+                } else {
+                    return get(timeout, unit);
+                }
+            } catch (CancellationException e) {
+                throw new OperationCanceledException();
+            } catch (TimeoutException e) {
+                // fall through and cancel
+            } catch (InterruptedException e) {
+                // fall through and cancel
+            } catch (ExecutionException e) {
+                final Throwable cause = e.getCause();
+                if (cause instanceof IOException) {
+                    throw (IOException) cause;
+                } else if (cause instanceof UnsupportedOperationException) {
+                    throw new AuthenticatorException(cause);
+                } else if (cause instanceof AuthenticatorException) {
+                    throw (AuthenticatorException) cause;
+                } else if (cause instanceof RuntimeException) {
+                    throw (RuntimeException) cause;
+                } else if (cause instanceof Error) {
+                    throw (Error) cause;
+                } else {
+                    throw new IllegalStateException(cause);
+                }
+            } finally {
+                cancel(true /* interrupt if running */);
+            }
+            throw new OperationCanceledException();
+        }
+
+        public Bundle getResult()
+                throws OperationCanceledException, IOException, AuthenticatorException {
+            return internalGetResult(null, null);
+        }
+
+        public Bundle getResult(long timeout, TimeUnit unit)
+                throws OperationCanceledException, IOException, AuthenticatorException {
+            return internalGetResult(timeout, unit);
+        }
+
+        protected void done() {
+            if (mCallback != null) {
+                postToHandler(mHandler, mCallback, this);
+            }
+        }
+
+        /** Handles the responses from the AccountManager */
+        private class Response extends IAccountManagerResponse.Stub {
+            public void onResult(Bundle bundle) {
+                Intent intent = bundle.getParcelable("intent");
+                if (intent != null && mActivity != null) {
+                    // since the user provided an Activity we will silently start intents
+                    // that we see
+                    mActivity.startActivity(intent);
+                    // leave the Future running to wait for the real response to this request
+                } else {
+                    set(bundle);
+                }
+            }
+
+            public void onError(int code, String message) {
+                if (code == Constants.ERROR_CODE_CANCELED) {
+                    // the authenticator indicated that this request was canceled, do so now
+                    cancel(true /* mayInterruptIfRunning */);
+                    return;
+                }
+                setException(convertErrorToException(code, message));
+            }
+        }
+
+    }
+
+    private abstract class AMSTaskBoolean extends FutureTask<Boolean> implements Future1<Boolean> {
+        final IAccountManagerResponse response;
+        final Handler mHandler;
+        final Future1Callback<Boolean> mCallback;
+        public AMSTaskBoolean(Handler handler, Future1Callback<Boolean> callback) {
+            super(new Callable<Boolean>() {
+                public Boolean call() throws Exception {
+                    throw new IllegalStateException("this should never be called");
+                }
+            });
+
+            mHandler = handler;
+            mCallback = callback;
+            response = new Response();
+
+            new Thread(new Runnable() {
+                public void run() {
+                    try {
+                        doWork();
+                    } catch (RemoteException e) {
+                        // never happens
+                    }
+                }
+            }).start();
+        }
+
+        public abstract void doWork() throws RemoteException;
+
+
+        protected void done() {
+            if (mCallback != null) {
+                postToHandler(mHandler, mCallback, this);
+            }
+        }
+
+        private Boolean internalGetResult(Long timeout, TimeUnit unit) {
+            try {
+                if (timeout == null) {
+                    return get();
+                } else {
+                    return get(timeout, unit);
+                }
+            } catch (InterruptedException e) {
+                // fall through and cancel
+            } catch (TimeoutException e) {
+                // fall through and cancel
+            } catch (CancellationException e) {
+                return false;
+            } catch (ExecutionException e) {
+                final Throwable cause = e.getCause();
+                if (cause instanceof IOException) {
+                    return false;
+                } else if (cause instanceof UnsupportedOperationException) {
+                    return false;
+                } else if (cause instanceof AuthenticatorException) {
+                    return false;
+                } else if (cause instanceof RuntimeException) {
+                    throw (RuntimeException) cause;
+                } else if (cause instanceof Error) {
+                    throw (Error) cause;
+                } else {
+                    throw new IllegalStateException(cause);
+                }
+            } finally {
+                cancel(true /* interrupt if running */);
+            }
+            return false;
+        }
+
+        public Boolean getResult() throws OperationCanceledException {
+            return internalGetResult(null, null);
+        }
+
+        public Boolean getResult(long timeout, TimeUnit unit) throws OperationCanceledException {
+            return internalGetResult(timeout, unit);
+        }
+
+        private class Response extends IAccountManagerResponse.Stub {
+            public void onResult(Bundle bundle) {
+                try {
+                    if (bundle.containsKey(Constants.BOOLEAN_RESULT_KEY)) {
+                        set(bundle.getBoolean(Constants.BOOLEAN_RESULT_KEY));
+                        return;
+                    }
+                } catch (ClassCastException e) {
+                    // we will set the exception below
+                }
+                onError(Constants.ERROR_CODE_INVALID_RESPONSE, "no result in response");
+            }
+
+            public void onError(int code, String message) {
+                if (code == Constants.ERROR_CODE_CANCELED) {
+                    cancel(true /* mayInterruptIfRunning */);
+                    return;
+                }
+                setException(convertErrorToException(code, message));
+            }
+        }
+
+    }
+
+    private Exception convertErrorToException(int code, String message) {
+        if (code == Constants.ERROR_CODE_NETWORK_ERROR) {
+            return new IOException(message);
+        }
+
+        if (code == Constants.ERROR_CODE_UNSUPPORTED_OPERATION) {
+            return new UnsupportedOperationException(message);
+        }
+
+        if (code == Constants.ERROR_CODE_INVALID_RESPONSE) {
+            return new AuthenticatorException(message);
+        }
+
+        if (code == Constants.ERROR_CODE_BAD_ARGUMENTS) {
+            return new IllegalArgumentException(message);
+        }
+
+        return new AuthenticatorException(message);
+    }
+
+    private class GetAuthTokenByTypeAndFeaturesTask extends AmsTask implements Future2Callback {
+        GetAuthTokenByTypeAndFeaturesTask(final String accountType, final String authTokenType,
+                final String[] features, Activity activityForPrompting,
+                final Bundle addAccountOptions, final Bundle loginOptions,
+                Future2Callback callback, Handler handler) {
+            super(activityForPrompting, handler, callback);
+            if (accountType == null) throw new IllegalArgumentException("account type is null");
+            mAccountType = accountType;
+            mAuthTokenType = authTokenType;
+            mFeatures = features;
+            mAddAccountOptions = addAccountOptions;
+            mLoginOptions = loginOptions;
+            mMyCallback = this;
+        }
+        volatile Future2 mFuture = null;
+        final String mAccountType;
+        final String mAuthTokenType;
+        final String[] mFeatures;
+        final Bundle mAddAccountOptions;
+        final Bundle mLoginOptions;
+        final Future2Callback mMyCallback;
+
+        public void doWork() throws RemoteException {
+            getAccountsWithTypeAndFeatures(mAccountType, mFeatures, new Future2Callback() {
+                public void run(Future2 future) {
+                    Bundle getAccountsResult;
+                    try {
+                        getAccountsResult = future.getResult();
+                    } catch (OperationCanceledException e) {
+                        setException(e);
+                        return;
+                    } catch (IOException e) {
+                        setException(e);
+                        return;
+                    } catch (AuthenticatorException e) {
+                        setException(e);
+                        return;
+                    }
+
+                    Parcelable[] accounts =
+                            getAccountsResult.getParcelableArray(Constants.ACCOUNTS_KEY);
+                    if (accounts.length == 0) {
+                        if (mActivity != null) {
+                            // no accounts, add one now. pretend that the user directly
+                            // made this request
+                            mFuture = addAccount(mAccountType, mAuthTokenType, mFeatures,
+                                    mAddAccountOptions, mActivity, mMyCallback, mHandler);
+                        } else {
+                            // send result since we can't prompt to add an account
+                            Bundle result = new Bundle();
+                            result.putString(Constants.ACCOUNT_NAME_KEY, null);
+                            result.putString(Constants.ACCOUNT_TYPE_KEY, null);
+                            result.putString(Constants.AUTHTOKEN_KEY, null);
+                            try {
+                                mResponse.onResult(result);
+                            } catch (RemoteException e) {
+                                // this will never happen
+                            }
+                            // we are done
+                        }
+                    } else if (accounts.length == 1) {
+                        // have a single account, return an authtoken for it
+                        if (mActivity == null) {
+                            mFuture = getAuthToken((Account) accounts[0], mAuthTokenType,
+                                    false /* notifyAuthFailure */, mMyCallback, mHandler);
+                        } else {
+                            mFuture = getAuthToken((Account) accounts[0],
+                                    mAuthTokenType, mLoginOptions,
+                                    mActivity, mMyCallback, mHandler);
+                        }
+                    } else {
+                        if (mActivity != null) {
+                            IAccountManagerResponse chooseResponse =
+                                    new IAccountManagerResponse.Stub() {
+                                public void onResult(Bundle value) throws RemoteException {
+                                    Account account = new Account(
+                                            value.getString(Constants.ACCOUNT_NAME_KEY),
+                                            value.getString(Constants.ACCOUNT_TYPE_KEY));
+                                    mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions,
+                                            mActivity, mMyCallback, mHandler);
+                                }
+
+                                public void onError(int errorCode, String errorMessage)
+                                        throws RemoteException {
+                                    mResponse.onError(errorCode, errorMessage);
+                                }
+                            };
+                            // have many accounts, launch the chooser
+                            Intent intent = new Intent();
+                            intent.setClassName("android",
+                                    "android.accounts.ChooseAccountActivity");
+                            intent.putExtra(Constants.ACCOUNTS_KEY, accounts);
+                            intent.putExtra(Constants.ACCOUNT_MANAGER_RESPONSE_KEY,
+                                    new AccountManagerResponse(chooseResponse));
+                            mActivity.startActivity(intent);
+                            // the result will arrive via the IAccountManagerResponse
+                        } else {
+                            // send result since we can't prompt to select an account
+                            Bundle result = new Bundle();
+                            result.putString(Constants.ACCOUNTS_KEY, null);
+                            try {
+                                mResponse.onResult(result);
+                            } catch (RemoteException e) {
+                                // this will never happen
+                            }
+                            // we are done
+                        }
+                    }
+                }}, mHandler);
+        }
+
+
+
+        // TODO(fredq) pass through the calls to our implemention of Future2 to the underlying
+        // future that we create. We need to do things like have cancel cancel the mFuture, if set
+        // or to cause this to be canceled if mFuture isn't set.
+        // Once this is done then getAuthTokenByFeatures can be changed to return a Future2.
+
+        public void run(Future2 future) {
+            try {
+                set(future.get());
+            } catch (InterruptedException e) {
+                cancel(true);
+            } catch (CancellationException e) {
+                cancel(true);
+            } catch (ExecutionException e) {
+                setException(e.getCause());
+            }
+        }
+    }
+
+    public void getAuthTokenByFeatures(
+            final String accountType, final String authTokenType, final String[] features,
+            final Activity activityForPrompting, final Bundle addAccountOptions,
+            final Bundle loginOptions,
+            final Future2Callback callback, final Handler handler) {
+        if (accountType == null) throw new IllegalArgumentException("account type is null");
+        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
+        new GetAuthTokenByTypeAndFeaturesTask(accountType, authTokenType,  features,
+                activityForPrompting, addAccountOptions, loginOptions, callback, handler).start();
+    }
+
+    private final HashMap<OnAccountsUpdatedListener, Handler> mAccountsUpdatedListeners =
+            Maps.newHashMap();
+
+    // These variable are only used from the LOGIN_ACCOUNTS_CHANGED_ACTION BroadcastReceiver
+    // and its getAccounts() callback which are both invoked only on the main thread. As a
+    // result we don't need to protect against concurrent accesses and any changes are guaranteed
+    // to be visible when used. Basically, these two variables are thread-confined.
+    private Future1<Account[]> mAccountsLookupFuture = null;
+    private boolean mAccountLookupPending = false;
+
+    /**
+     * BroadcastReceiver that listens for the LOGIN_ACCOUNTS_CHANGED_ACTION intent
+     * so that it can read the updated list of accounts and send them to the listener
+     * in mAccountsUpdatedListeners.
+     */
+    private final BroadcastReceiver mAccountsChangedBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(final Context context, final Intent intent) {
+            if (mAccountsLookupFuture != null) {
+                // an accounts lookup is already in progress,
+                // don't bother starting another request
+                mAccountLookupPending = true;
+                return;
+            }
+            // initiate a read of the accounts
+            mAccountsLookupFuture = getAccounts(new Future1Callback<Account[]>() {
+                public void run(Future1<Account[]> future) {
+                    // clear the future so that future receives will try the lookup again
+                    mAccountsLookupFuture = null;
+
+                    // get the accounts array
+                    Account[] accounts;
+                    try {
+                        accounts = future.getResult();
+                    } catch (OperationCanceledException e) {
+                        // this should never happen, but if it does pretend we got another
+                        // accounts changed broadcast
+                        if (Config.LOGD) {
+                            Log.d(TAG, "the accounts lookup for listener notifications was "
+                                    + "canceled, try again by simulating the receipt of "
+                                    + "a LOGIN_ACCOUNTS_CHANGED_ACTION broadcast");
+                        }
+                        onReceive(context, intent);
+                        return;
+                    }
+
+                    // send the result to the listeners
+                    synchronized (mAccountsUpdatedListeners) {
+                        for (Map.Entry<OnAccountsUpdatedListener, Handler> entry :
+                                mAccountsUpdatedListeners.entrySet()) {
+                            Account[] accountsCopy = new Account[accounts.length];
+                            // send the listeners a copy to make sure that one doesn't
+                            // change what another sees
+                            System.arraycopy(accounts, 0, accountsCopy, 0, accountsCopy.length);
+                            postToHandler(entry.getValue(), entry.getKey(), accountsCopy);
+                        }
+                    }
+
+                    // If mAccountLookupPending was set when the account lookup finished it
+                    // means that we had previously ignored a LOGIN_ACCOUNTS_CHANGED_ACTION
+                    // intent because a lookup was already in progress. Now that we are done
+                    // with this lookup and notification pretend that another intent
+                    // was received by calling onReceive() directly.
+                    if (mAccountLookupPending) {
+                        mAccountLookupPending = false;
+                        onReceive(context, intent);
+                        return;
+                    }
+                }
+            }, mMainHandler);
+        }
+    };
+
+    /**
+     * Add a {@link OnAccountsUpdatedListener} to this instance of the {@link AccountManager}.
+     * The listener is guaranteed to be invoked on the thread of the Handler that is passed
+     * in or the main thread's Handler if handler is null.
+     * @param listener the listener to add
+     * @param handler the Handler whose thread will be used to invoke the listener. If null
+     * the AccountManager context's main thread will be used.
+     * @param updateImmediately if true then the listener will be invoked as a result of this
+     * call.
+     * @throws IllegalArgumentException if listener is null
+     * @throws IllegalStateException if listener was already added
+     */
+    public void addOnAccountsUpdatedListener(final OnAccountsUpdatedListener listener,
+            Handler handler, boolean updateImmediately) {
+        if (listener == null) {
+            throw new IllegalArgumentException("the listener is null");
+        }
+        synchronized (mAccountsUpdatedListeners) {
+            if (mAccountsUpdatedListeners.containsKey(listener)) {
+                throw new IllegalStateException("this listener is already added");
+            }
+            final boolean wasEmpty = mAccountsUpdatedListeners.isEmpty();
+
+            mAccountsUpdatedListeners.put(listener, handler);
+
+            if (wasEmpty) {
+                // Register a broadcast receiver to monitor account changes
+                IntentFilter intentFilter = new IntentFilter();
+                intentFilter.addAction(Constants.LOGIN_ACCOUNTS_CHANGED_ACTION);
+                mContext.registerReceiver(mAccountsChangedBroadcastReceiver, intentFilter);
+            }
+        }
+
+        if (updateImmediately) {
+            getAccounts(new Future1Callback<Account[]>() {
+                public void run(Future1<Account[]> future) {
+                    try {
+                        listener.onAccountsUpdated(future.getResult());
+                    } catch (OperationCanceledException e) {
+                        // ignore
+                    }
+                }
+            }, handler);
+        }
+    }
+
+    /**
+     * Remove an {@link OnAccountsUpdatedListener} that was previously registered with
+     * {@link #addOnAccountsUpdatedListener}.
+     * @param listener the listener to remove
+     * @throws IllegalArgumentException if listener is null
+     * @throws IllegalStateException if listener was not already added
+     */
+    public void removeOnAccountsUpdatedListener(OnAccountsUpdatedListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("the listener is null");
+        }
+        synchronized (mAccountsUpdatedListeners) {
+            if (mAccountsUpdatedListeners.remove(listener) == null) {
+                throw new IllegalStateException("this listener was not previously added");
+            }
+            if (mAccountsUpdatedListeners.isEmpty()) {
+                mContext.unregisterReceiver(mAccountsChangedBroadcastReceiver);
+            }
+        }
+    }
+}
diff --git a/core/java/android/accounts/AccountManagerResponse.java b/core/java/android/accounts/AccountManagerResponse.java
new file mode 100644
index 0000000..25371fd
--- /dev/null
+++ b/core/java/android/accounts/AccountManagerResponse.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+/**
+ * Object that wraps calls to an {@link android.accounts.IAccountManagerResponse} object.
+ * @hide
+ */
+public class AccountManagerResponse implements Parcelable {
+    private IAccountManagerResponse mResponse;
+
+    public AccountManagerResponse(IAccountManagerResponse response) {
+        mResponse = response;
+    }
+
+    public AccountManagerResponse(Parcel parcel) {
+        mResponse =
+                IAccountManagerResponse.Stub.asInterface(parcel.readStrongBinder());
+    }
+
+    public void onResult(Bundle result) {
+        try {
+            mResponse.onResult(result);
+        } catch (RemoteException e) {
+            // this should never happen
+        }
+    }
+
+    public void onError(int errorCode, String errorMessage) {
+        try {
+            mResponse.onError(errorCode, errorMessage);
+        } catch (RemoteException e) {
+            // this should never happen
+        }
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStrongBinder(mResponse.asBinder());
+    }
+
+    public static final Creator<AccountManagerResponse> CREATOR =
+            new Creator<AccountManagerResponse>() {
+        public AccountManagerResponse createFromParcel(Parcel source) {
+            return new AccountManagerResponse(source);
+        }
+
+        public AccountManagerResponse[] newArray(int size) {
+            return new AccountManagerResponse[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
new file mode 100644
index 0000000..4f617c4
--- /dev/null
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -0,0 +1,1186 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.app.PendingIntent;
+import android.app.NotificationManager;
+import android.app.Notification;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.R;
+
+/**
+ * A system service that provides  account, password, and authtoken management for all
+ * accounts on the device. Some of these calls are implemented with the help of the corresponding
+ * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
+ * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
+ *    AccountManager accountManager =
+ *      (AccountManager)context.getSystemService(Context.ACCOUNT_SERVICE)
+ * @hide
+ */
+public class AccountManagerService extends IAccountManager.Stub {
+    private static final String TAG = "AccountManagerService";
+
+    private static final int TIMEOUT_DELAY_MS = 1000 * 60;
+    private static final String DATABASE_NAME = "accounts.db";
+    private static final int DATABASE_VERSION = 2;
+
+    private final Context mContext;
+
+    private HandlerThread mMessageThread;
+    private final MessageHandler mMessageHandler;
+
+    // Messages that can be sent on mHandler
+    private static final int MESSAGE_TIMED_OUT = 3;
+    private static final int MESSAGE_CONNECTED = 7;
+    private static final int MESSAGE_DISCONNECTED = 8;
+
+    private final AccountAuthenticatorCache mAuthenticatorCache;
+    private final AuthenticatorBindHelper mBindHelper;
+    private final DatabaseHelper mOpenHelper;
+    private final SimWatcher mSimWatcher;
+
+    private static final String TABLE_ACCOUNTS = "accounts";
+    private static final String ACCOUNTS_ID = "_id";
+    private static final String ACCOUNTS_NAME = "name";
+    private static final String ACCOUNTS_TYPE = "type";
+    private static final String ACCOUNTS_PASSWORD = "password";
+
+    private static final String TABLE_AUTHTOKENS = "authtokens";
+    private static final String AUTHTOKENS_ID = "_id";
+    private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
+    private static final String AUTHTOKENS_TYPE = "type";
+    private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
+
+    private static final String TABLE_EXTRAS = "extras";
+    private static final String EXTRAS_ID = "_id";
+    private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
+    private static final String EXTRAS_KEY = "key";
+    private static final String EXTRAS_VALUE = "value";
+
+    private static final String TABLE_META = "meta";
+    private static final String META_KEY = "key";
+    private static final String META_VALUE = "value";
+
+    private static final String[] ACCOUNT_NAME_TYPE_PROJECTION =
+            new String[]{ACCOUNTS_ID, ACCOUNTS_NAME, ACCOUNTS_TYPE};
+    private static final Intent ACCOUNTS_CHANGED_INTENT =
+            new Intent(Constants.LOGIN_ACCOUNTS_CHANGED_ACTION);
+
+    private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
+    private static final int NOTIFICATION_ID = 234;
+
+    public class AuthTokenKey {
+        public final Account mAccount;
+        public final String mAuthTokenType;
+        private final int mHashCode;
+
+        public AuthTokenKey(Account account, String authTokenType) {
+            mAccount = account;
+            mAuthTokenType = authTokenType;
+            mHashCode = computeHashCode();
+        }
+
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (!(o instanceof AuthTokenKey)) {
+                return false;
+            }
+            AuthTokenKey other = (AuthTokenKey)o;
+            if (!mAccount.equals(other.mAccount)) {
+                return false;
+            }
+            return (mAuthTokenType == null)
+                    ? other.mAuthTokenType == null
+                    : mAuthTokenType.equals(other.mAuthTokenType);
+        }
+
+        private int computeHashCode() {
+            int result = 17;
+            result = 31 * result + mAccount.hashCode();
+            result = 31 * result + ((mAuthTokenType == null) ? 0 : mAuthTokenType.hashCode());
+            return result;
+        }
+
+        public int hashCode() {
+            return mHashCode;
+        }
+    }
+
+    public AccountManagerService(Context context) {
+        mContext = context;
+
+        mOpenHelper = new DatabaseHelper(mContext);
+
+        mMessageThread = new HandlerThread("AccountManagerService");
+        mMessageThread.start();
+        mMessageHandler = new MessageHandler(mMessageThread.getLooper());
+
+        mAuthenticatorCache = new AccountAuthenticatorCache(mContext);
+        mBindHelper = new AuthenticatorBindHelper(mContext, mAuthenticatorCache, mMessageHandler,
+                MESSAGE_CONNECTED, MESSAGE_DISCONNECTED);
+
+        mSimWatcher = new SimWatcher(mContext);
+    }
+
+    public String getPassword(Account account) {
+        long identityToken = clearCallingIdentity();
+        try {
+            SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+            Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
+                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+                    new String[]{account.mName, account.mType}, null, null, null);
+            try {
+                if (cursor.moveToNext()) {
+                    return cursor.getString(0);
+                }
+                return null;
+            } finally {
+                cursor.close();
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public String getUserData(Account account, String key) {
+        long identityToken = clearCallingIdentity();
+        try {
+            SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+            db.beginTransaction();
+            try {
+                long accountId = getAccountId(db, account);
+                if (accountId < 0) {
+                    return null;
+                }
+                Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_VALUE},
+                        EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
+                        new String[]{key}, null, null, null);
+                try {
+                    if (cursor.moveToNext()) {
+                        return cursor.getString(0);
+                    }
+                    return null;
+                } finally {
+                    cursor.close();
+                }
+            } finally {
+                db.setTransactionSuccessful();
+                db.endTransaction();
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public AuthenticatorDescription[] getAuthenticatorTypes() {
+        long identityToken = clearCallingIdentity();
+        try {
+            Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
+                    authenticatorCollection = mAuthenticatorCache.getAllServices();
+            AuthenticatorDescription[] types =
+                    new AuthenticatorDescription[authenticatorCollection.size()];
+            int i = 0;
+            for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
+                    : authenticatorCollection) {
+                types[i] = authenticator.type;
+                i++;
+            }
+            return types;
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public Account[] getAccounts() {
+        long identityToken = clearCallingIdentity();
+        try {
+            return getAccountsByType(null);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public Account[] getAccountsByType(String accountType) {
+        long identityToken = clearCallingIdentity();
+        try {
+            SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+
+            final String selection = accountType == null ? null : (ACCOUNTS_TYPE + "=?");
+            final String[] selectionArgs = accountType == null ? null : new String[]{accountType};
+            Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_NAME_TYPE_PROJECTION,
+                    selection, selectionArgs, null, null, null);
+            try {
+                int i = 0;
+                Account[] accounts = new Account[cursor.getCount()];
+                while (cursor.moveToNext()) {
+                    accounts[i] = new Account(cursor.getString(1), cursor.getString(2));
+                    i++;
+                }
+                return accounts;
+            } finally {
+                cursor.close();
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public boolean addAccount(Account account, String password, Bundle extras) {
+        // fails if the account already exists
+        long identityToken = clearCallingIdentity();
+        try {
+            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+            db.beginTransaction();
+            try {
+                long numMatches = DatabaseUtils.longForQuery(db,
+                        "select count(*) from " + TABLE_ACCOUNTS
+                                + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+                        new String[]{account.mName, account.mType});
+                if (numMatches > 0) {
+                    return false;
+                }
+                ContentValues values = new ContentValues();
+                values.put(ACCOUNTS_NAME, account.mName);
+                values.put(ACCOUNTS_TYPE, account.mType);
+                values.put(ACCOUNTS_PASSWORD, password);
+                long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
+                if (accountId < 0) {
+                    return false;
+                }
+                if (extras != null) {
+                    for (String key : extras.keySet()) {
+                        final String value = extras.getString(key);
+                        if (insertExtra(db, accountId, key, value) < 0) {
+                            return false;
+                        }
+                    }
+                }
+                db.setTransactionSuccessful();
+                sendAccountsChangedBroadcast();
+                return true;
+            } finally {
+                db.endTransaction();
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    private long insertExtra(SQLiteDatabase db, long accountId, String key, String value) {
+        ContentValues values = new ContentValues();
+        values.put(EXTRAS_KEY, key);
+        values.put(EXTRAS_ACCOUNTS_ID, accountId);
+        values.put(EXTRAS_VALUE, value);
+        return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
+    }
+
+    public void removeAccount(Account account) {
+        long identityToken = clearCallingIdentity();
+        try {
+            final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+            db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+                    new String[]{account.mName, account.mType});
+            sendAccountsChangedBroadcast();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public void invalidateAuthToken(String accountType, String authToken) {
+        long identityToken = clearCallingIdentity();
+        try {
+            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+            db.beginTransaction();
+            try {
+                invalidateAuthToken(db, accountType, authToken);
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    private void invalidateAuthToken(SQLiteDatabase db, String accountType, String authToken) {
+        Cursor cursor = db.rawQuery(
+                "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
+                        + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
+                        + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
+                        + " FROM " + TABLE_ACCOUNTS
+                        + " JOIN " + TABLE_AUTHTOKENS
+                        + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
+                        + " = " + AUTHTOKENS_ACCOUNTS_ID
+                        + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
+                        + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
+                new String[]{authToken, accountType});
+        try {
+            while (cursor.moveToNext()) {
+                long authTokenId = cursor.getLong(0);
+                String accountName = cursor.getString(1);
+                String authTokenType = cursor.getString(2);
+                db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
+            }
+        } finally {
+            cursor.close();
+        }
+    }
+
+    private boolean saveAuthTokenToDatabase(Account account, String type, String authToken) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        db.beginTransaction();
+        try {
+            long accountId = getAccountId(db, account);
+            if (accountId < 0) {
+                return false;
+            }
+            db.delete(TABLE_AUTHTOKENS,
+                    AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
+                    new String[]{type});
+            ContentValues values = new ContentValues();
+            values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
+            values.put(AUTHTOKENS_TYPE, type);
+            values.put(AUTHTOKENS_AUTHTOKEN, authToken);
+            if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
+                db.setTransactionSuccessful();
+                return true;
+            }
+            return false;
+        } finally {
+            db.endTransaction();
+        }
+    }
+
+    public String readAuthTokenFromDatabase(Account account, String authTokenType) {
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        db.beginTransaction();
+        try {
+            long accountId = getAccountId(db, account);
+            if (accountId < 0) {
+                return null;
+            }
+            return getAuthToken(db, accountId, authTokenType);
+        } finally {
+            db.setTransactionSuccessful();
+            db.endTransaction();
+        }
+    }
+
+    public String peekAuthToken(Account account, String authTokenType) {
+        long identityToken = clearCallingIdentity();
+        try {
+            return readAuthTokenFromDatabase(account, authTokenType);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public void setAuthToken(Account account, String authTokenType, String authToken) {
+        long identityToken = clearCallingIdentity();
+        try {
+            cacheAuthToken(account, authTokenType, authToken);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public void setPassword(Account account, String password) {
+        long identityToken = clearCallingIdentity();
+        try {
+            ContentValues values = new ContentValues();
+            values.put(ACCOUNTS_PASSWORD, password);
+            mOpenHelper.getWritableDatabase().update(TABLE_ACCOUNTS, values,
+                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+                    new String[]{account.mName, account.mType});
+            sendAccountsChangedBroadcast();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    private void sendAccountsChangedBroadcast() {
+        mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
+    }
+
+    public void clearPassword(Account account) {
+        long identityToken = clearCallingIdentity();
+        try {
+            setPassword(account, null);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public void setUserData(Account account, String key, String value) {
+        long identityToken = clearCallingIdentity();
+        try {
+            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+            db.beginTransaction();
+            try {
+                long accountId = getAccountId(db, account);
+                if (accountId < 0) {
+                    return;
+                }
+                long extrasId = getExtrasId(db, accountId, key);
+                if (extrasId < 0 ) {
+                    extrasId = insertExtra(db, accountId, key, value);
+                    if (extrasId < 0) {
+                        return;
+                    }
+                } else {
+                    ContentValues values = new ContentValues();
+                    values.put(EXTRAS_VALUE, value);
+                    if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
+                        return;
+                    }
+
+                }
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public void getAuthToken(IAccountManagerResponse response, final Account account,
+            final String authTokenType, final boolean notifyOnAuthFailure,
+            final boolean expectActivityLaunch, final Bundle loginOptions) {
+        long identityToken = clearCallingIdentity();
+        try {
+            String authToken = readAuthTokenFromDatabase(account, authTokenType);
+            if (authToken != null) {
+                try {
+                    Bundle result = new Bundle();
+                    result.putString(Constants.AUTHTOKEN_KEY, authToken);
+                    result.putString(Constants.ACCOUNT_NAME_KEY, account.mName);
+                    result.putString(Constants.ACCOUNT_TYPE_KEY, account.mType);
+                    response.onResult(result);
+                } catch (RemoteException e) {
+                    // if the caller is dead then there is no one to care about remote exceptions
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "failure while notifying response", e);
+                    }
+                }
+                return;
+            }
+
+            new Session(response, account.mType, expectActivityLaunch) {
+                protected String toDebugString(long now) {
+                    if (loginOptions != null) loginOptions.keySet();
+                    return super.toDebugString(now) + ", getAuthToken"
+                            + ", " + account
+                            + ", authTokenType " + authTokenType
+                            + ", loginOptions " + loginOptions
+                            + ", notifyOnAuthFailure " + notifyOnAuthFailure;
+                }
+
+                public void run() throws RemoteException {
+                    mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
+                }
+
+                public void onResult(Bundle result) {
+                    if (result != null) {
+                        String authToken = result.getString(Constants.AUTHTOKEN_KEY);
+                        if (authToken != null) {
+                            String name = result.getString(Constants.ACCOUNT_NAME_KEY);
+                            String type = result.getString(Constants.ACCOUNT_TYPE_KEY);
+                            if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
+                                onError(Constants.ERROR_CODE_INVALID_RESPONSE,
+                                        "the type and name should not be empty");
+                                return;
+                            }
+                            cacheAuthToken(new Account(name, type), authTokenType, authToken);
+                        }
+
+                        Intent intent = result.getParcelable(Constants.INTENT_KEY);
+                        if (intent != null && notifyOnAuthFailure) {
+                            doNotification(result.getString(Constants.AUTH_FAILED_MESSAGE_KEY),
+                                    intent);
+                        }
+                    }
+                    super.onResult(result);
+                }
+            }.bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+
+    public void addAcount(final IAccountManagerResponse response, final String accountType,
+            final String authTokenType, final String[] requiredFeatures,
+            final boolean expectActivityLaunch, final Bundle options) {
+        long identityToken = clearCallingIdentity();
+        try {
+            new Session(response, accountType, expectActivityLaunch) {
+                public void run() throws RemoteException {
+                    mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, 
+                            options);
+                }
+
+                protected String toDebugString(long now) {
+                    return super.toDebugString(now) + ", addAccount"
+                            + ", accountType " + accountType
+                            + ", requiredFeatures "
+                            + (requiredFeatures != null
+                              ? TextUtils.join(",", requiredFeatures)
+                              : null);
+                }
+            }.bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public void confirmCredentials(IAccountManagerResponse response,
+            final Account account, final boolean expectActivityLaunch) {
+        long identityToken = clearCallingIdentity();
+        try {
+            new Session(response, account.mType, expectActivityLaunch) {
+                public void run() throws RemoteException {
+                    mAuthenticator.confirmCredentials(this, account);
+                }
+                protected String toDebugString(long now) {
+                    return super.toDebugString(now) + ", confirmCredentials"
+                            + ", " + account;
+                }
+            }.bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public void confirmPassword(IAccountManagerResponse response, final Account account,
+            final String password) {
+        long identityToken = clearCallingIdentity();
+        try {
+            new Session(response, account.mType, false /* expectActivityLaunch */) {
+                public void run() throws RemoteException {
+                    mAuthenticator.confirmPassword(this, account, password);
+                }
+                protected String toDebugString(long now) {
+                    return super.toDebugString(now) + ", confirmPassword"
+                            + ", " + account;
+                }
+            }.bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public void updateCredentials(IAccountManagerResponse response, final Account account,
+            final String authTokenType, final boolean expectActivityLaunch,
+            final Bundle loginOptions) {
+        long identityToken = clearCallingIdentity();
+        try {
+            new Session(response, account.mType, expectActivityLaunch) {
+                public void run() throws RemoteException {
+                    mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
+                }
+                protected String toDebugString(long now) {
+                    if (loginOptions != null) loginOptions.keySet();
+                    return super.toDebugString(now) + ", updateCredentials"
+                            + ", " + account
+                            + ", authTokenType " + authTokenType
+                            + ", loginOptions " + loginOptions;
+                }
+            }.bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public void editProperties(IAccountManagerResponse response, final String accountType,
+            final boolean expectActivityLaunch) {
+        long identityToken = clearCallingIdentity();
+        try {
+            new Session(response, accountType, expectActivityLaunch) {
+                public void run() throws RemoteException {
+                    mAuthenticator.editProperties(this, mAccountType);
+                }
+                protected String toDebugString(long now) {
+                    return super.toDebugString(now) + ", editProperties"
+                            + ", accountType " + accountType;
+                }
+            }.bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    private class GetAccountsByTypeAndFeatureSession extends Session {
+        private final String[] mFeatures;
+        private volatile Account[] mAccountsOfType = null;
+        private volatile ArrayList<Account> mAccountsWithFeatures = null;
+        private volatile int mCurrentAccount = 0;
+
+        public GetAccountsByTypeAndFeatureSession(IAccountManagerResponse response,
+            String type, String[] features) {
+            super(response, type, false /* expectActivityLaunch */);
+            mFeatures = features;
+        }
+
+        public void run() throws RemoteException {
+            mAccountsOfType = getAccountsByType(mAccountType);
+            // check whether each account matches the requested features
+            mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
+            mCurrentAccount = 0;
+
+            checkAccount();
+        }
+
+        public void checkAccount() {
+            if (mCurrentAccount >= mAccountsOfType.length) {
+                sendResult();
+                return;
+            }
+
+            try {
+                mAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
+            } catch (RemoteException e) {
+                onError(Constants.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
+            }
+        }
+
+        public void onResult(Bundle result) {
+            mNumResults++;
+            if (result == null) {
+                onError(Constants.ERROR_CODE_INVALID_RESPONSE, "null bundle");
+                return;
+            }
+            if (result.getBoolean(Constants.BOOLEAN_RESULT_KEY, false)) {
+                mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
+            }
+            mCurrentAccount++;
+            checkAccount();
+        }
+
+        public void sendResult() {
+            IAccountManagerResponse response = getResponseAndClose();
+            if (response != null) {
+                try {
+                    Account[] accounts = new Account[mAccountsWithFeatures.size()];
+                    for (int i = 0; i < accounts.length; i++) {
+                        accounts[i] = mAccountsWithFeatures.get(i);
+                    }
+                    Bundle result = new Bundle();
+                    result.putParcelableArray(Constants.ACCOUNTS_KEY, accounts);
+                    response.onResult(result);
+                } catch (RemoteException e) {
+                    // if the caller is dead then there is no one to care about remote exceptions
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "failure while notifying response", e);
+                    }
+                }
+            }
+        }
+
+
+        protected String toDebugString(long now) {
+            return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
+                    + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
+        }
+    }
+    public void getAccountsByTypeAndFeatures(IAccountManagerResponse response,
+            String type, String[] features) {
+        if (type == null) {
+            if (response != null) {
+                try {
+                    response.onError(Constants.ERROR_CODE_BAD_ARGUMENTS, "type is null");
+                } catch (RemoteException e) {
+                    // ignore this
+                }
+            }
+            return;
+        }
+        long identityToken = clearCallingIdentity();
+        try {
+            new GetAccountsByTypeAndFeatureSession(response, type, features).bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    private boolean cacheAuthToken(Account account, String authTokenType, String authToken) {
+        return saveAuthTokenToDatabase(account, authTokenType, authToken);
+    }
+
+    private long getAccountId(SQLiteDatabase db, Account account) {
+        Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
+                "name=? AND type=?", new String[]{account.mName, account.mType}, null, null, null);
+        try {
+            if (cursor.moveToNext()) {
+                return cursor.getLong(0);
+            }
+            return -1;
+        } finally {
+            cursor.close();
+        }
+    }
+
+    private long getExtrasId(SQLiteDatabase db, long accountId, String key) {
+        Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
+                EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
+                new String[]{key}, null, null, null);
+        try {
+            if (cursor.moveToNext()) {
+                return cursor.getLong(0);
+            }
+            return -1;
+        } finally {
+            cursor.close();
+        }
+    }
+
+    private String getAuthToken(SQLiteDatabase db, long accountId, String authTokenType) {
+        Cursor cursor = db.query(TABLE_AUTHTOKENS, new String[]{AUTHTOKENS_AUTHTOKEN},
+                AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
+                new String[]{authTokenType},
+                null, null, null);
+        try {
+            if (cursor.moveToNext()) {
+                return cursor.getString(0);
+            }
+            return null;
+        } finally {
+            cursor.close();
+        }
+    }
+
+    private abstract class Session extends IAccountAuthenticatorResponse.Stub
+            implements AuthenticatorBindHelper.Callback, IBinder.DeathRecipient {
+        IAccountManagerResponse mResponse;
+        final String mAccountType;
+        final boolean mExpectActivityLaunch;
+        final long mCreationTime;
+
+        public int mNumResults = 0;
+        private int mNumRequestContinued = 0;
+        private int mNumErrors = 0;
+
+
+        IAccountAuthenticator mAuthenticator = null;
+
+        public Session(IAccountManagerResponse response, String accountType,
+                boolean expectActivityLaunch) {
+            super();
+            if (response == null) throw new IllegalArgumentException("response is null");
+            if (accountType == null) throw new IllegalArgumentException("accountType is null");
+            mResponse = response;
+            mAccountType = accountType;
+            mExpectActivityLaunch = expectActivityLaunch;
+            mCreationTime = SystemClock.elapsedRealtime();
+            synchronized (mSessions) {
+                mSessions.put(toString(), this);
+            }
+            try {
+                response.asBinder().linkToDeath(this, 0 /* flags */);
+            } catch (RemoteException e) {
+                mResponse = null;
+                binderDied();
+            }
+        }
+
+        IAccountManagerResponse getResponseAndClose() {
+            if (mResponse == null) {
+                // this session has already been closed
+                return null;
+            }
+            IAccountManagerResponse response = mResponse;
+            close(); // this clears mResponse so we need to save the response before this call
+            return response;
+        }
+
+        private void close() {
+            synchronized (mSessions) {
+                if (mSessions.remove(toString()) == null) {
+                    // the session was already closed, so bail out now
+                    return;
+                }
+            }
+            if (mResponse != null) {
+                // stop listening for response deaths
+                mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
+
+                // clear this so that we don't accidentally send any further results
+                mResponse = null;
+            }
+            cancelTimeout();
+            unbind();
+        }
+
+        public void binderDied() {
+            mResponse = null;
+            close();
+        }
+
+        protected String toDebugString() {
+            return toDebugString(SystemClock.elapsedRealtime());
+        }
+
+        protected String toDebugString(long now) {
+            return "Session: expectLaunch " + mExpectActivityLaunch
+                    + ", connected " + (mAuthenticator != null)
+                    + ", stats (" + mNumResults + "/" + mNumRequestContinued
+                    + "/" + mNumErrors + ")"
+                    + ", lifetime " + ((now - mCreationTime) / 1000.0);
+        }
+
+        void bind() {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
+            }
+            if (!mBindHelper.bind(mAccountType, this)) {
+                Log.d(TAG, "bind attempt failed for " + toDebugString());
+                onError(Constants.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
+            }
+        }
+
+        private void unbind() {
+            if (mAuthenticator != null) {
+                mAuthenticator = null;
+                mBindHelper.unbind(this);
+            }
+        }
+
+        public void scheduleTimeout() {
+            mMessageHandler.sendMessageDelayed(
+                    mMessageHandler.obtainMessage(MESSAGE_TIMED_OUT, this), TIMEOUT_DELAY_MS);
+        }
+
+        public void cancelTimeout() {
+            mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
+        }
+
+        public void onConnected(IBinder service) {
+            mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
+            try {
+                run();
+            } catch (RemoteException e) {
+                onError(Constants.ERROR_CODE_REMOTE_EXCEPTION,
+                        "remote exception");
+            }
+        }
+
+        public abstract void run() throws RemoteException;
+
+        public void onDisconnected() {
+            mAuthenticator = null;
+            IAccountManagerResponse response = getResponseAndClose();
+            if (response != null) {
+                onError(Constants.ERROR_CODE_REMOTE_EXCEPTION,
+                        "disconnected");
+            }
+        }
+
+        public void onTimedOut() {
+            IAccountManagerResponse response = getResponseAndClose();
+            if (response != null) {
+                onError(Constants.ERROR_CODE_REMOTE_EXCEPTION,
+                        "timeout");
+            }
+        }
+
+        public void onResult(Bundle result) {
+            mNumResults++;
+            if (result != null && !TextUtils.isEmpty(result.getString(Constants.AUTHTOKEN_KEY))) {
+                cancelNotification();
+            }
+            IAccountManagerResponse response;
+            if (mExpectActivityLaunch && result != null
+                    && result.containsKey(Constants.INTENT_KEY)) {
+                response = mResponse;
+            } else {
+                response = getResponseAndClose();
+            }
+            if (response != null) {
+                try {
+                    if (result == null) {
+                        response.onError(Constants.ERROR_CODE_INVALID_RESPONSE,
+                                "null bundle returned");
+                    } else {
+                        response.onResult(result);
+                    }
+                } catch (RemoteException e) {
+                    // if the caller is dead then there is no one to care about remote exceptions
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "failure while notifying response", e);
+                    }
+                }
+            }
+        }
+
+        public void onRequestContinued() {
+            mNumRequestContinued++;
+        }
+
+        public void onError(int errorCode, String errorMessage) {
+            mNumErrors++;
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "Session.onError: " + errorCode + ", " + errorMessage);
+            }
+            IAccountManagerResponse response = getResponseAndClose();
+            if (response != null) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "Session.onError: responding");
+                }
+                try {
+                    response.onError(errorCode, errorMessage);
+                } catch (RemoteException e) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
+                    }
+                }
+            } else {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "Session.onError: already closed");
+                }
+            }
+        }
+    }
+
+    private class MessageHandler extends Handler {
+        MessageHandler(Looper looper) {
+            super(looper);
+        }
+        
+        public void handleMessage(Message msg) {
+            if (mBindHelper.handleMessage(msg)) {
+                return;
+            }
+            switch (msg.what) {
+                case MESSAGE_TIMED_OUT:
+                    Session session = (Session)msg.obj;
+                    session.onTimedOut();
+                    break;
+
+                default:
+                    throw new IllegalStateException("unhandled message: " + msg.what);
+            }
+        }
+    }
+
+    private class DatabaseHelper extends SQLiteOpenHelper {
+        public DatabaseHelper(Context context) {
+            super(context, DATABASE_NAME, null, DATABASE_VERSION);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
+                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
+                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
+                    + ACCOUNTS_PASSWORD + " TEXT, "
+                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
+
+            db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " (  "
+                    + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,  "
+                    + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
+                    + AUTHTOKENS_TYPE + " TEXT NOT NULL,  "
+                    + AUTHTOKENS_AUTHTOKEN + " TEXT,  "
+                    + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
+
+            db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
+                    + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+                    + EXTRAS_ACCOUNTS_ID + " INTEGER, "
+                    + EXTRAS_KEY + " TEXT NOT NULL, "
+                    + EXTRAS_VALUE + " TEXT, "
+                    + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
+
+            db.execSQL("CREATE TABLE " + TABLE_META + " ( "
+                    + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
+                    + META_VALUE + " TEXT)");
+
+            db.execSQL(""
+                    + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
+                    + " BEGIN"
+                    + "   DELETE FROM " + TABLE_AUTHTOKENS
+                    + "     WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+                    + "   DELETE FROM " + TABLE_EXTRAS
+                    + "     WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+                    + " END");
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
+
+            if (oldVersion == 1) {
+                db.execSQL(""
+                        + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
+                        + " BEGIN"
+                        + "   DELETE FROM " + TABLE_AUTHTOKENS
+                        + "     WHERE " + AUTHTOKENS_ACCOUNTS_ID + " =OLD." + ACCOUNTS_ID + " ;"
+                        + "   DELETE FROM " + TABLE_EXTRAS
+                        + "     WHERE " + EXTRAS_ACCOUNTS_ID + " =OLD." + ACCOUNTS_ID + " ;"
+                        + " END");
+                oldVersion++;
+            }
+        }
+
+        @Override
+        public void onOpen(SQLiteDatabase db) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
+        }
+    }
+
+    private void setMetaValue(String key, String value) {
+        ContentValues values = new ContentValues();
+        values.put(META_KEY, key);
+        values.put(META_VALUE, value);
+        mOpenHelper.getWritableDatabase().replace(TABLE_META, META_KEY, values);
+    }
+
+    private String getMetaValue(String key) {
+        Cursor c = mOpenHelper.getReadableDatabase().query(TABLE_META,
+                new String[]{META_VALUE}, META_KEY + "=?", new String[]{key}, null, null, null);
+        try {
+            if (c.moveToNext()) {
+                return c.getString(0);
+            }
+            return null;
+        } finally {
+            c.close();
+        }
+    }
+
+    private class SimWatcher extends BroadcastReceiver {
+        public SimWatcher(Context context) {
+            // Re-scan the SIM card when the SIM state changes, and also if
+            // the disk recovers from a full state (we may have failed to handle
+            // things properly while the disk was full).
+            final IntentFilter filter = new IntentFilter();
+            filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+            filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
+            context.registerReceiver(this, filter);
+        }
+        
+        /**
+         * Compare the IMSI to the one stored in the login service's
+         * database.  If they differ, erase all passwords and
+         * authtokens (and store the new IMSI).
+         */
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // Check IMSI on every update; nothing happens if the IMSI is missing or unchanged.
+            String imsi = ((TelephonyManager) context.getSystemService(
+                    Context.TELEPHONY_SERVICE)).getSubscriberId();
+            if (TextUtils.isEmpty(imsi)) return;
+
+            String storedImsi = getMetaValue("imsi");
+
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "current IMSI=" + imsi + "; stored IMSI=" + storedImsi);
+            }
+
+            if (!imsi.equals(storedImsi) && !"initial".equals(storedImsi)) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "wiping all passwords and authtokens");
+                }
+                SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+                db.beginTransaction();
+                try {
+                    db.execSQL("DELETE from " + TABLE_AUTHTOKENS);
+                    db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_PASSWORD + " = ''");
+                    sendAccountsChangedBroadcast();
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                }
+            }
+            setMetaValue("imsi", imsi);
+        }
+    }
+
+    public IBinder onBind(Intent intent) {
+        return asBinder();
+    }
+
+    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+        synchronized (mSessions) {
+            final long now = SystemClock.elapsedRealtime();
+            fout.println("AccountManagerService: " + mSessions.size() + " sessions");
+            for (Session session : mSessions.values()) {
+                fout.println("  " + session.toDebugString(now));
+            }
+        }
+
+        fout.println();
+
+        mAuthenticatorCache.dump(fd, fout, args);
+    }
+
+    private void doNotification(CharSequence message, Intent intent) {
+        long identityToken = clearCallingIdentity();
+        try {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "doNotification: " + message + " intent:" + intent);
+            }
+
+            Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
+                    0 /* when */);
+            n.setLatestEventInfo(mContext, mContext.getText(R.string.notification_title), message,
+                PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
+            ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
+                    .notify(NOTIFICATION_ID, n);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    private void cancelNotification() {
+        long identityToken = clearCallingIdentity();
+        try {
+            ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
+                .cancel(NOTIFICATION_ID);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+}
diff --git a/core/java/android/accounts/AccountMonitor.java b/core/java/android/accounts/AccountMonitor.java
deleted file mode 100644
index f21385e..0000000
--- a/core/java/android/accounts/AccountMonitor.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accounts;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.database.SQLException;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * A helper class that calls back on the provided
- * AccountMonitorListener with the set of current accounts both when
- * it gets created and whenever the set changes. It does this by
- * binding to the AccountsService and registering to receive the
- * intent broadcast when the set of accounts is changed.  The
- * connection to the accounts service is only made when it needs to
- * fetch the current list of accounts (that is, when the
- * AccountMonitor is first created, and when the intent is received).
- */
-public class AccountMonitor extends BroadcastReceiver implements ServiceConnection {
-    private final Context mContext;
-    private final AccountMonitorListener mListener;
-    private boolean mClosed = false;
-    private int pending = 0;
-
-    // This thread runs in the background and runs the code to update accounts
-    // in the listener.
-    private class AccountUpdater extends Thread {
-        private IBinder mService;
-
-        public AccountUpdater(IBinder service) {
-            mService = service;
-        }
-
-        @Override
-        public void run() {
-            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-            IAccountsService accountsService = IAccountsService.Stub.asInterface(mService);
-            String[] accounts = null;
-            do {
-                try {
-                    accounts = accountsService.getAccounts();
-                } catch (RemoteException e) {
-                    // if the service was killed then the system will restart it and when it does we
-                    // will get another onServiceConnected, at which point we will do a notify.
-                    Log.w("AccountMonitor", "Remote exception when getting accounts", e);
-                    return;
-                }
-
-                synchronized (AccountMonitor.this) {
-                    --pending;
-                    if (pending == 0) {
-                        break;
-                    }
-                }
-            } while (true);
-
-            mContext.unbindService(AccountMonitor.this);
-
-            try {
-                mListener.onAccountsUpdated(accounts);
-            } catch (SQLException e) {
-                // Better luck next time.  If the problem was disk-full,
-                // the STORAGE_OK intent will re-trigger the update.
-                Log.e("AccountMonitor", "Can't update accounts", e);
-            }
-        }
-    }
-
-    /**
-     * Initializes the AccountMonitor and initiates a bind to the
-     * AccountsService to get the initial account list.  For 1.0,
-     * the "list" is always a single account.
-     *
-     * @param context the context we are running in
-     * @param listener the user to notify when the account set changes
-     */
-    public AccountMonitor(Context context, AccountMonitorListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener is null");
-        }
-
-        mContext = context;
-        mListener = listener;
-
-        // Register a broadcast receiver to monitor account changes
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(AccountsServiceConstants.LOGIN_ACCOUNTS_CHANGED_ACTION);
-        intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);  // To recover from disk-full.
-        mContext.registerReceiver(this, intentFilter);
-
-        // Send the listener the initial state now.
-        notifyListener();
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        notifyListener();
-    }
-
-    public void onServiceConnected(ComponentName className, IBinder service) {
-        // Create a background thread to update the accounts.
-        new AccountUpdater(service).start();
-    }
-
-    public void onServiceDisconnected(ComponentName className) {
-    }
-
-    private synchronized void notifyListener() {
-        if (pending == 0) {
-            // initiate the bind
-            if (!mContext.bindService(AccountsServiceConstants.SERVICE_INTENT,
-                                      this, Context.BIND_AUTO_CREATE)) {
-                // This is normal if GLS isn't part of this build.
-                Log.w("AccountMonitor",
-                      "Couldn't connect to "  +
-                      AccountsServiceConstants.SERVICE_INTENT +
-                      " (Missing service?)");
-            }
-        } else {
-            // already bound.  bindService will not trigger another
-            // call to onServiceConnected, so instead we make sure
-            // that the existing background thread will call
-            // getAccounts() after this function returns, by
-            // incrementing pending.
-            //
-            // Yes, this else clause contains only a comment.
-        }
-        ++pending;
-    }
-
-    /**
-     * calls close()
-     * @throws Throwable
-     */
-    @Override
-    protected void finalize() throws Throwable {
-        close();
-        super.finalize();
-    }
-
-    /**
-     * Unregisters the account receiver.  Consecutive calls to this
-     * method are harmless, but also do nothing.  Once this call is
-     * made no more notifications will occur.
-     */
-    public synchronized void close() {
-        if (!mClosed) {
-            mContext.unregisterReceiver(this);
-            mClosed = true;
-        }
-    }
-}
diff --git a/core/java/android/accounts/AccountsServiceConstants.java b/core/java/android/accounts/AccountsServiceConstants.java
deleted file mode 100644
index b882e7b..0000000
--- a/core/java/android/accounts/AccountsServiceConstants.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accounts;
-
-import android.content.Intent;
-
-/**
- * Miscellaneous constants used by the AccountsService and its
- * clients.
- */
-// TODO: These constants *could* come directly from the
-// IAccountsService interface, but that's not possible since the
-// aidl compiler doesn't let you define constants (yet.)
-public class AccountsServiceConstants {
-    /** This class is never instantiated. */
-    private AccountsServiceConstants() {
-    }
-
-    /**
-     * Action sent as a broadcast Intent by the AccountsService
-     * when accounts are added to and/or removed from the device's
-     * database, or when the primary account is changed.
-     */
-    public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
-        "android.accounts.LOGIN_ACCOUNTS_CHANGED";
-
-    /**
-     * Action sent as a broadcast Intent by the AccountsService
-     * when it starts up and no accounts are available (so some should be added).
-     */
-    public static final String LOGIN_ACCOUNTS_MISSING_ACTION =
-        "android.accounts.LOGIN_ACCOUNTS_MISSING";
-
-    /**
-     * Action on the intent used to bind to the IAccountsService interface. This
-     * is used for services that have multiple interfaces (allowing
-     * them to differentiate the interface intended, and return the proper
-     * Binder.)
-     */
-    private static final String ACCOUNTS_SERVICE_ACTION = "android.accounts.IAccountsService";
-
-    /*
-     * The intent uses a component in addition to the action to ensure the actual
-     * accounts service is bound to (a malicious third-party app could
-     * theoretically have a service with the same action).
-     */
-    /** The intent used to bind to the accounts service. */
-    public static final Intent SERVICE_INTENT =
-        new Intent()
-            .setClassName("com.google.android.googleapps",
-                    "com.google.android.googleapps.GoogleLoginService")
-            .setAction(ACCOUNTS_SERVICE_ACTION);
-
-    /**
-     * Checks whether the intent is to bind to the accounts service.
-     * 
-     * @param bindIntent The Intent used to bind to the service. 
-     * @return Whether the intent is to bind to the accounts service.
-     */
-    public static final boolean isForAccountsService(Intent bindIntent) {
-        String otherAction = bindIntent.getAction();
-        return otherAction != null && otherAction.equals(ACCOUNTS_SERVICE_ACTION);
-    }
-}
diff --git a/core/java/android/accounts/AuthenticatorBindHelper.java b/core/java/android/accounts/AuthenticatorBindHelper.java
new file mode 100644
index 0000000..91e23ab
--- /dev/null
+++ b/core/java/android/accounts/AuthenticatorBindHelper.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+/**
+ * A helper object that simplifies binding to Account Authenticators. It uses the
+ * {@link AccountAuthenticatorCache} to find the component name of the authenticators,
+ * allowing the user to bind by account name. It also allows multiple, simultaneous binds
+ * to the same authenticator, with each bind call guaranteed to return either
+ * {@link Callback#onConnected} or {@link Callback#onDisconnected} if the bind() call
+ * itself succeeds, even if the authenticator is already bound internally.
+ * @hide
+ */
+public class AuthenticatorBindHelper {
+    private static final String TAG = "Accounts";
+    private final Handler mHandler;
+    private final Context mContext;
+    private final int mMessageWhatConnected;
+    private final int mMessageWhatDisconnected;
+    private final Map<String, MyServiceConnection> mServiceConnections = Maps.newHashMap();
+    private final Map<String, ArrayList<Callback>> mServiceUsers = Maps.newHashMap();
+    private final AccountAuthenticatorCache mAuthenticatorCache;
+
+    public AuthenticatorBindHelper(Context context,
+            AccountAuthenticatorCache authenticatorCache, Handler handler,
+            int messageWhatConnected, int messageWhatDisconnected) {
+        mContext = context;
+        mHandler = handler;
+        mAuthenticatorCache = authenticatorCache;
+        mMessageWhatConnected = messageWhatConnected;
+        mMessageWhatDisconnected = messageWhatDisconnected;
+    }
+
+    public interface Callback {
+        void onConnected(IBinder service);
+        void onDisconnected();
+    }
+
+    public boolean bind(String authenticatorType, Callback callback) {
+        // if the authenticator is connecting or connected then return true
+        synchronized (mServiceConnections) {
+            if (mServiceConnections.containsKey(authenticatorType)) {
+                MyServiceConnection connection = mServiceConnections.get(authenticatorType);
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "service connection already exists for " + authenticatorType);
+                }
+                mServiceUsers.get(authenticatorType).add(callback);
+                if (connection.mService != null) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "the service is connected, scheduling a connected message for "
+                                + authenticatorType);
+                    }
+                    connection.scheduleCallbackConnectedMessage(callback);
+                } else {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "the service is *not* connected, waiting for for "
+                                + authenticatorType);
+                    }
+                }
+                return true;
+            }
+
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "there is no service connection for " + authenticatorType);
+            }
+
+            // otherwise find the component name for the authenticator and initiate a bind
+            // if no authenticator or the bind fails then return false, otherwise return true
+            AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
+                    mAuthenticatorCache.getServiceInfo(
+                            AuthenticatorDescription.newKey(authenticatorType));
+            if (authenticatorInfo == null) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "there is no authenticator for " + authenticatorType
+                            + ", bailing out");
+                }
+                return false;
+            }
+
+            MyServiceConnection connection = new MyServiceConnection(authenticatorType);
+
+            Intent intent = new Intent();
+            intent.setAction("android.accounts.AccountAuthenticator");
+            intent.setComponent(authenticatorInfo.componentName);
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
+            }
+            if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
+                }
+                return false;
+            }
+
+            mServiceConnections.put(authenticatorType, connection);
+            mServiceUsers.put(authenticatorType, Lists.newArrayList(callback));
+            return true;
+        }
+    }
+
+    public void unbind(Callback callbackToUnbind) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "unbinding callback " + callbackToUnbind);
+        }
+        synchronized (mServiceConnections) {
+            for (Map.Entry<String, ArrayList<Callback>> entry : mServiceUsers.entrySet()) {
+                final String authenticatorType = entry.getKey();
+                final ArrayList<Callback> serviceUsers = entry.getValue();
+                for (Callback callback : serviceUsers) {
+                    if (callback == callbackToUnbind) {
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "found callback in service" + authenticatorType);
+                        }
+                        serviceUsers.remove(callbackToUnbind);
+                        if (serviceUsers.isEmpty()) {
+                            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                                Log.v(TAG, "there are no more callbacks for service "
+                                        + authenticatorType + ", unbinding service");
+                            }
+                            unbindFromService(authenticatorType);
+                        } else {
+                            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                                Log.v(TAG, "leaving service " + authenticatorType
+                                        + " around since there are still callbacks using it");
+                            }
+                        }
+                        return;
+                    }
+                }
+            }
+            Log.e(TAG, "did not find callback " + callbackToUnbind + " in any of the services");
+        }
+    }
+
+    private void unbindFromService(String authenticatorType) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "unbindService from " + authenticatorType);
+        }
+        mContext.unbindService(mServiceConnections.get(authenticatorType));
+        mServiceUsers.remove(authenticatorType);
+        mServiceConnections.remove(authenticatorType);
+    }
+
+    private class ConnectedMessagePayload {
+        public final IBinder mService;
+        public final Callback mCallback;
+        public ConnectedMessagePayload(IBinder service, Callback callback) {
+            mService = service;
+            mCallback = callback;
+        }
+    }
+
+    private class MyServiceConnection implements ServiceConnection {
+        private final String mAuthenticatorType;
+        private IBinder mService = null;
+
+        public MyServiceConnection(String authenticatorType) {
+            mAuthenticatorType = authenticatorType;
+        }
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "onServiceConnected for account type " + mAuthenticatorType);
+            }
+            // post a message for each service user to tell them that the service is connected
+            synchronized (mServiceConnections) {
+                mService = service;
+                for (Callback callback : mServiceUsers.get(mAuthenticatorType)) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "the service became connected, scheduling a connected "
+                                + "message for " + mAuthenticatorType);
+                    }
+                    scheduleCallbackConnectedMessage(callback);
+                }
+            }
+        }
+
+        private void scheduleCallbackConnectedMessage(Callback callback) {
+            final ConnectedMessagePayload payload =
+                    new ConnectedMessagePayload(mService, callback);
+            mHandler.obtainMessage(mMessageWhatConnected, payload).sendToTarget();
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "onServiceDisconnected for account type " + mAuthenticatorType);
+            }
+            // post a message for each service user to tell them that the service is disconnected,
+            // and unbind from the service.
+            synchronized (mServiceConnections) {
+                for (Callback callback : mServiceUsers.get(mAuthenticatorType)) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "the service became disconnected, scheduling a "
+                                + "disconnected message for "
+                                + mAuthenticatorType);
+                    }
+                    mHandler.obtainMessage(mMessageWhatDisconnected, callback).sendToTarget();
+                }
+                unbindFromService(mAuthenticatorType);
+            }
+        }
+    }
+
+    boolean handleMessage(Message message) {
+        if (message.what == mMessageWhatConnected) {
+            ConnectedMessagePayload payload = (ConnectedMessagePayload)message.obj;
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "notifying callback " + payload.mCallback + " that it is connected");
+            }
+            payload.mCallback.onConnected(payload.mService);
+            return true;
+        } else if (message.what == mMessageWhatDisconnected) {
+            Callback callback = (Callback)message.obj;
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "notifying callback " + callback + " that it is disconnected");
+            }
+            callback.onDisconnected();
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/include/utils/executablepath.h b/core/java/android/accounts/AuthenticatorDescription.aidl
similarity index 65%
copy from include/utils/executablepath.h
copy to core/java/android/accounts/AuthenticatorDescription.aidl
index c979432..136361c 100644
--- a/include/utils/executablepath.h
+++ b/core/java/android/accounts/AuthenticatorDescription.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,6 @@
  * limitations under the License.
  */
 
-#ifndef _UTILS_EXECUTABLEPATH_H
-#define _UTILS_EXECUTABLEPATH_H
+package android.accounts;
 
-#include <limits.h>
-
-// returns the path to this executable
-#if __cplusplus
-extern "C"
-#endif
-void executablepath(char s[PATH_MAX]);
-
-#endif // _UTILS_EXECUTABLEPATH_H
+parcelable AuthenticatorDescription;
diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java
new file mode 100644
index 0000000..f896bf8
--- /dev/null
+++ b/core/java/android/accounts/AuthenticatorDescription.java
@@ -0,0 +1,69 @@
+package android.accounts;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+public class AuthenticatorDescription implements Parcelable {
+    final public String type;
+    final public int labelId;
+    final public int iconId;
+    final public String packageName;
+
+    public AuthenticatorDescription(String type, String packageName, int labelId, int iconId) {
+        this.type = type;
+        this.packageName = packageName;
+        this.labelId = labelId;
+        this.iconId = iconId;
+    }
+
+    public static AuthenticatorDescription newKey(String type) {
+        return new AuthenticatorDescription(type);
+    }
+
+    private AuthenticatorDescription(String type) {
+        this.type = type;
+        this.packageName = null;
+        this.labelId = 0;
+        this.iconId = 0;
+    }
+
+    private AuthenticatorDescription(Parcel source) {
+        this.type = source.readString();
+        this.packageName = source.readString();
+        this.labelId = source.readInt();
+        this.iconId = source.readInt();
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public int hashCode() {
+        return type.hashCode();
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (!(o instanceof AuthenticatorDescription)) return false;
+        final AuthenticatorDescription other = (AuthenticatorDescription) o;
+        return type.equals(other.type);
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(type);
+        dest.writeString(packageName);
+        dest.writeInt(labelId);
+        dest.writeInt(iconId);
+    }
+
+    public static final Creator<AuthenticatorDescription> CREATOR =
+            new Creator<AuthenticatorDescription>() {
+        public AuthenticatorDescription createFromParcel(Parcel source) {
+            return new AuthenticatorDescription(source);
+        }
+
+        public AuthenticatorDescription[] newArray(int size) {
+            return new AuthenticatorDescription[size];
+        }
+    };
+}
diff --git a/core/java/android/accounts/AuthenticatorException.java b/core/java/android/accounts/AuthenticatorException.java
new file mode 100644
index 0000000..4023494
--- /dev/null
+++ b/core/java/android/accounts/AuthenticatorException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+public class AuthenticatorException extends Exception {
+    public AuthenticatorException() {
+        super();
+    }
+    public AuthenticatorException(String message) {
+        super(message);
+    }
+    public AuthenticatorException(String message, Throwable cause) {
+        super(message, cause);
+    }
+    public AuthenticatorException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/core/java/android/accounts/ChooseAccountActivity.java b/core/java/android/accounts/ChooseAccountActivity.java
new file mode 100644
index 0000000..83377f3
--- /dev/null
+++ b/core/java/android/accounts/ChooseAccountActivity.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+import android.app.ListActivity;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.view.View;
+import android.util.Log;
+
+public class ChooseAccountActivity extends ListActivity {
+    private static final String TAG = "AccountManager";
+    private Parcelable[] mAccounts = null;
+    private AccountManagerResponse mAccountManagerResponse = null;
+    private Bundle mResult;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState == null) {
+            mAccounts = getIntent().getParcelableArrayExtra(Constants.ACCOUNTS_KEY);
+            mAccountManagerResponse =
+                    getIntent().getParcelableExtra(Constants.ACCOUNT_MANAGER_RESPONSE_KEY);
+        } else {
+            mAccounts = savedInstanceState.getParcelableArray(Constants.ACCOUNTS_KEY);
+            mAccountManagerResponse =
+                    savedInstanceState.getParcelable(Constants.ACCOUNT_MANAGER_RESPONSE_KEY);
+        }
+
+        String[] mAccountNames = new String[mAccounts.length];
+        for (int i = 0; i < mAccounts.length; i++) {
+            mAccountNames[i] = ((Account) mAccounts[i]).mName;
+        }
+
+        // Use an existing ListAdapter that will map an array
+        // of strings to TextViews
+        setListAdapter(new ArrayAdapter<String>(this,
+                android.R.layout.simple_list_item_1, mAccountNames));
+        getListView().setTextFilterEnabled(true);
+    }
+
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        Account account = (Account) mAccounts[position];
+        Log.d(TAG, "selected account " + account);
+        Bundle bundle = new Bundle();
+        bundle.putString(Constants.ACCOUNT_NAME_KEY, account.mName);
+        bundle.putString(Constants.ACCOUNT_TYPE_KEY, account.mType);
+        mResult = bundle;
+        finish();
+    }
+
+    public void finish() {
+        if (mAccountManagerResponse != null) {
+            if (mResult != null) {
+                mAccountManagerResponse.onResult(mResult);
+            } else {
+                mAccountManagerResponse.onError(Constants.ERROR_CODE_CANCELED, "canceled");
+            }
+        }
+        super.finish();
+    }
+}
diff --git a/core/java/android/accounts/Constants.java b/core/java/android/accounts/Constants.java
new file mode 100644
index 0000000..fde920e
--- /dev/null
+++ b/core/java/android/accounts/Constants.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+public class Constants {
+    // this should never be instantiated
+    private Constants() {}
+
+    public static final int ERROR_CODE_REMOTE_EXCEPTION = 1;
+    public static final int ERROR_CODE_NETWORK_ERROR = 3;
+    public static final int ERROR_CODE_CANCELED = 4;
+    public static final int ERROR_CODE_INVALID_RESPONSE = 5;
+    public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6;
+    public static final int ERROR_CODE_BAD_ARGUMENTS = 7;
+    public static final int ERROR_CODE_BAD_REQUEST = 8;
+
+    public static final String ACCOUNTS_KEY = "accounts";
+    public static final String AUTHENTICATOR_TYPES_KEY = "authenticator_types";
+    public static final String PASSWORD_KEY = "password";
+    public static final String USERDATA_KEY = "userdata";
+    public static final String AUTHTOKEN_KEY = "authtoken";
+    public static final String ACCOUNT_NAME_KEY = "authAccount";
+    public static final String ACCOUNT_TYPE_KEY = "accountType";
+    public static final String ERROR_CODE_KEY = "errorCode";
+    public static final String ERROR_MESSAGE_KEY = "errorMessage";
+    public static final String INTENT_KEY = "intent";
+    public static final String BOOLEAN_RESULT_KEY = "booleanResult";
+    public static final String ACCOUNT_AUTHENTICATOR_RESPONSE_KEY = "accountAuthenticatorResponse";
+    public static final String ACCOUNT_MANAGER_RESPONSE_KEY = "accountManagerResponse";
+    public static final String AUTH_FAILED_MESSAGE_KEY = "authFailedMessage";
+    /**
+     * Action sent as a broadcast Intent by the AccountsService
+     * when accounts are added to and/or removed from the device's
+     * database, or when the primary account is changed.
+     */
+    public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
+        "android.accounts.LOGIN_ACCOUNTS_CHANGED";
+}
diff --git a/core/java/android/accounts/Future1.java b/core/java/android/accounts/Future1.java
new file mode 100644
index 0000000..386cb6e
--- /dev/null
+++ b/core/java/android/accounts/Future1.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An extension of {@link Future} that provides wrappers for {@link #get()} that handle the various
+ * exceptions that  {@link #get()} may return and rethrows them as exceptions specific to
+ * {@link AccountManager}.
+ */
+public interface Future1<V> extends Future<V> {
+    /**
+     * Wrapper for {@link Future#get()}. If the get() throws {@link InterruptedException} then the
+     * {@link Future1} is canceled and {@link OperationCanceledException} is thrown.
+     * @return the {@link android.os.Bundle} that is returned by get()
+     * @throws OperationCanceledException if get() throws the unchecked CancellationException
+     * or if the Future was interrupted.
+     */
+    V getResult() throws OperationCanceledException;
+
+    /**
+     * Wrapper for {@link Future#get()}. If the get() throws {@link InterruptedException} then the
+     * {@link Future1} is canceled and {@link OperationCanceledException} is thrown.
+     * @param timeout the maximum time to wait
+     * @param unit the time unit of the timeout argument
+     * @return the {@link android.os.Bundle} that is returned by {@link Future#get()}
+     * @throws OperationCanceledException if get() throws the unchecked
+     * {@link java.util.concurrent.CancellationException} or if the {@link Future1} was interrupted.
+     */
+    V getResult(long timeout, TimeUnit unit) throws OperationCanceledException;
+}
\ No newline at end of file
diff --git a/include/utils/executablepath.h b/core/java/android/accounts/Future1Callback.java
similarity index 65%
copy from include/utils/executablepath.h
copy to core/java/android/accounts/Future1Callback.java
index c979432..886671b 100644
--- a/include/utils/executablepath.h
+++ b/core/java/android/accounts/Future1Callback.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,16 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.accounts;
 
-#ifndef _UTILS_EXECUTABLEPATH_H
-#define _UTILS_EXECUTABLEPATH_H
-
-#include <limits.h>
-
-// returns the path to this executable
-#if __cplusplus
-extern "C"
-#endif
-void executablepath(char s[PATH_MAX]);
-
-#endif // _UTILS_EXECUTABLEPATH_H
+public interface Future1Callback<V> {
+    void run(Future1<V> future);
+}
diff --git a/core/java/android/accounts/Future2.java b/core/java/android/accounts/Future2.java
new file mode 100644
index 0000000..b2ea84f
--- /dev/null
+++ b/core/java/android/accounts/Future2.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+import android.os.Bundle;
+
+import java.io.IOException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An extension of {@link Future} that provides wrappers for {@link #get()} that handle the various
+ * exceptions that  {@link #get()} may return and rethrows them as exceptions specific to
+ * {@link AccountManager}.
+ */
+public interface Future2 extends Future<Bundle> {
+    /**
+     * Wrapper for {@link Future#get()}. If the get() throws {@link InterruptedException} then the
+     * {@link Future2} is canceled and {@link OperationCanceledException} is thrown.
+     * @return the {@link android.os.Bundle} that is returned by {@link Future#get()}
+     * @throws OperationCanceledException if get() throws the unchecked
+     * {@link java.util.concurrent.CancellationException} or if the {@link Future2} was interrupted.
+     * @throws IOException if the request was unable to complete due to a network error
+     * @throws AuthenticatorException if there was an error communicating with the
+     * {@link AbstractAccountAuthenticator}.
+     */
+    Bundle getResult()
+            throws OperationCanceledException, IOException, AuthenticatorException;
+
+    /**
+     * Wrapper for {@link Future#get()}. If the get() throws {@link InterruptedException} then the
+     * {@link Future2} is canceled and {@link OperationCanceledException} is thrown.
+     * @param timeout the maximum time to wait
+     * @param unit the time unit of the timeout argument
+     * @return the {@link android.os.Bundle} that is returned by {@link Future#get()}
+     * @throws OperationCanceledException if get() throws the unchecked
+     * {@link java.util.concurrent.CancellationException} or if the {@link Future2} was interrupted.
+     * @throws IOException if the request was unable to complete due to a network error
+     * @throws AuthenticatorException if there was an error communicating with the
+     * {@link AbstractAccountAuthenticator}.
+     */
+    Bundle getResult(long timeout, TimeUnit unit)
+            throws OperationCanceledException, IOException, AuthenticatorException;
+}
diff --git a/include/utils/executablepath.h b/core/java/android/accounts/Future2Callback.java
similarity index 65%
copy from include/utils/executablepath.h
copy to core/java/android/accounts/Future2Callback.java
index c979432..7ef0c94 100644
--- a/include/utils/executablepath.h
+++ b/core/java/android/accounts/Future2Callback.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,16 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.accounts;
 
-#ifndef _UTILS_EXECUTABLEPATH_H
-#define _UTILS_EXECUTABLEPATH_H
-
-#include <limits.h>
-
-// returns the path to this executable
-#if __cplusplus
-extern "C"
-#endif
-void executablepath(char s[PATH_MAX]);
-
-#endif // _UTILS_EXECUTABLEPATH_H
+public interface Future2Callback {
+    void run(Future2 future);
+}
\ No newline at end of file
diff --git a/core/java/android/accounts/IAccountAuthenticator.aidl b/core/java/android/accounts/IAccountAuthenticator.aidl
new file mode 100644
index 0000000..46a7144
--- /dev/null
+++ b/core/java/android/accounts/IAccountAuthenticator.aidl
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.accounts.IAccountAuthenticatorResponse;
+import android.accounts.Account;
+import android.os.Bundle;
+
+/**
+ * Service that allows the interaction with an authentication server.
+ */
+oneway interface IAccountAuthenticator {
+    /**
+     * prompts the user for account information and adds the result to the IAccountManager
+     */
+    void addAccount(in IAccountAuthenticatorResponse response, String accountType,
+        String authTokenType, in String[] requiredFeatures, in Bundle options);
+
+    /**
+     * Checks that the account/password combination is valid.
+     * @deprecated
+     */
+    void confirmPassword(in IAccountAuthenticatorResponse response,
+        in Account account, String password);
+
+    /**
+     * prompts the user for the credentials of the account
+     */
+    void confirmCredentials(in IAccountAuthenticatorResponse response, in Account account);
+
+    /**
+     * gets the password by either prompting the user or querying the IAccountManager
+     */
+    void getAuthToken(in IAccountAuthenticatorResponse response, in Account account,
+        String authTokenType, in Bundle options);
+
+    /**
+     * prompts the user for a new password and writes it to the IAccountManager
+     */
+    void updateCredentials(in IAccountAuthenticatorResponse response, in Account account,
+        String authTokenType, in Bundle options);
+
+    /**
+     * launches an activity that lets the user edit and set the properties for an authenticator
+     */
+    void editProperties(in IAccountAuthenticatorResponse response, String accountType);
+
+    /**
+     * returns a Bundle where the boolean value BOOLEAN_RESULT_KEY is set if the account has the
+     * specified features
+     */
+    void hasFeatures(in IAccountAuthenticatorResponse response, in Account account, 
+        in String[] features);
+}
diff --git a/libs/utils/executablepath_linux.cpp b/core/java/android/accounts/IAccountAuthenticatorResponse.aidl
similarity index 60%
copy from libs/utils/executablepath_linux.cpp
copy to core/java/android/accounts/IAccountAuthenticatorResponse.aidl
index b8d2a3d..a9ac2f1 100644
--- a/libs/utils/executablepath_linux.cpp
+++ b/core/java/android/accounts/IAccountAuthenticatorResponse.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,17 +14,14 @@
  * limitations under the License.
  */
 
-#include <utils/executablepath.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <limits.h>
-#include <stdio.h>
+package android.accounts;
+import android.os.Bundle;
 
-void executablepath(char exe[PATH_MAX])
-{
-    char proc[100];
-    sprintf(proc, "/proc/%d/exe", getpid());
-    
-    int err = readlink(proc, exe, PATH_MAX);
+/**
+ * The interface used to return responses from an {@link IAccountAuthenticator}
+ */
+oneway interface IAccountAuthenticatorResponse {
+    void onResult(in Bundle value);
+    void onRequestContinued();
+    void onError(int errorCode, String errorMessage);
 }
-
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
new file mode 100644
index 0000000..15ab4e8
--- /dev/null
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.accounts.IAccountManagerResponse;
+import android.accounts.Account;
+import android.accounts.AuthenticatorDescription;
+import android.os.Bundle;
+
+/**
+ * Central application service that provides account management.
+ * @hide
+ */
+interface IAccountManager {
+    String getPassword(in Account account);
+    String getUserData(in Account account, String key);
+    AuthenticatorDescription[] getAuthenticatorTypes();
+    Account[] getAccounts();
+    Account[] getAccountsByType(String accountType);
+    boolean addAccount(in Account account, String password, in Bundle extras);
+    void removeAccount(in Account account);
+    void invalidateAuthToken(String accountType, String authToken);
+    String peekAuthToken(in Account account, String authTokenType);
+    void setAuthToken(in Account account, String authTokenType, String authToken);
+    void setPassword(in Account account, String password);
+    void clearPassword(in Account account);
+    void setUserData(in Account account, String key, String value);
+
+    void getAuthToken(in IAccountManagerResponse response, in Account account,
+        String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch,
+        in Bundle options);
+    void addAcount(in IAccountManagerResponse response, String accountType,
+        String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch, 
+        in Bundle options);
+    void updateCredentials(in IAccountManagerResponse response, in Account account,
+        String authTokenType, boolean expectActivityLaunch, in Bundle options);
+    void editProperties(in IAccountManagerResponse response, String accountType,
+        boolean expectActivityLaunch);
+    void confirmCredentials(in IAccountManagerResponse response, in Account account,
+        boolean expectActivityLaunch);
+    void getAccountsByTypeAndFeatures(in IAccountManagerResponse response, String accountType,
+        in String[] features);
+
+    /*
+     * @deprecated
+     */
+    void confirmPassword(in IAccountManagerResponse response, in Account account,
+        String password);
+}
diff --git a/libs/utils/executablepath_linux.cpp b/core/java/android/accounts/IAccountManagerResponse.aidl
similarity index 61%
copy from libs/utils/executablepath_linux.cpp
copy to core/java/android/accounts/IAccountManagerResponse.aidl
index b8d2a3d..ca1203d 100644
--- a/libs/utils/executablepath_linux.cpp
+++ b/core/java/android/accounts/IAccountManagerResponse.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,17 +14,14 @@
  * limitations under the License.
  */
 
-#include <utils/executablepath.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <limits.h>
-#include <stdio.h>
+package android.accounts;
+import android.os.Bundle;
 
-void executablepath(char exe[PATH_MAX])
-{
-    char proc[100];
-    sprintf(proc, "/proc/%d/exe", getpid());
-    
-    int err = readlink(proc, exe, PATH_MAX);
+/**
+ * The interface used to return responses for asynchronous calls to the {@link IAccountManager}
+ * @hide
+ */
+oneway interface IAccountManagerResponse {
+    void onResult(in Bundle value);
+    void onError(int errorCode, String errorMessage);
 }
-
diff --git a/core/java/android/accounts/IAccountsService.aidl b/core/java/android/accounts/IAccountsService.aidl
deleted file mode 100644
index dda513c..0000000
--- a/core/java/android/accounts/IAccountsService.aidl
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accounts;
-
-/**
- * Central application service that allows querying the list of accounts.
- */
-interface IAccountsService {
-    /**
-     * Gets the list of Accounts the user has previously logged
-     * in to.  Accounts are of the form "username@domain".
-     * <p>
-     * This method will return an empty array if the device doesn't
-     * know about any accounts (yet).
-     *
-     * @return The accounts.  The array will be zero-length if the
-     *         AccountsService doesn't know about any accounts yet.
-     */
-    String[] getAccounts();
-
-    /**
-     * This is an interim solution for bypassing a forgotten gesture on the
-     * unlock screen (it is hidden, please make sure it stays this way!). This
-     * will be *removed* when the unlock screen design supports additional
-     * authenticators.
-     * <p>
-     * The user will be presented with username and password fields that are
-     * called as parameters to this method. If true is returned, the user is
-     * able to define a new gesture and get back into the system. If false, the
-     * user can try again.
-     * 
-     * @param username The username entered.
-     * @param password The password entered.
-     * @return Whether to allow the user to bypass the lock screen and define a
-     *         new gesture.
-     * @hide (The package is already hidden, but just in case someone
-     *       unhides that, this should not be revealed.)
-     */
-    boolean shouldUnlock(String username, String password);
-}
diff --git a/core/java/android/accounts/NetworkErrorException.java b/core/java/android/accounts/NetworkErrorException.java
new file mode 100644
index 0000000..f855cc8
--- /dev/null
+++ b/core/java/android/accounts/NetworkErrorException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+public class NetworkErrorException extends Exception {
+    public NetworkErrorException() {
+        super();
+    }
+    public NetworkErrorException(String message) {
+        super(message);
+    }
+    public NetworkErrorException(String message, Throwable cause) {
+        super(message, cause);
+    }
+    public NetworkErrorException(Throwable cause) {
+        super(cause);
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/accounts/AccountMonitorListener.java b/core/java/android/accounts/OnAccountsUpdatedListener.java
similarity index 85%
rename from core/java/android/accounts/AccountMonitorListener.java
rename to core/java/android/accounts/OnAccountsUpdatedListener.java
index d0bd9a9..bd249d0 100644
--- a/core/java/android/accounts/AccountMonitorListener.java
+++ b/core/java/android/accounts/OnAccountsUpdatedListener.java
@@ -19,11 +19,11 @@
 /**
  * An interface that contains the callback used by the AccountMonitor
  */
-public interface AccountMonitorListener {
+public interface OnAccountsUpdatedListener {
     /**
      * This invoked when the AccountMonitor starts up and whenever the account
      * set changes.
-     * @param currentAccounts the current accounts
+     * @param accounts the current accounts
      */
-    void onAccountsUpdated(String[] currentAccounts);
+    void onAccountsUpdated(Account[] accounts);
 }
diff --git a/core/java/android/accounts/OperationCanceledException.java b/core/java/android/accounts/OperationCanceledException.java
new file mode 100644
index 0000000..2f2c164
--- /dev/null
+++ b/core/java/android/accounts/OperationCanceledException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+public class OperationCanceledException extends Exception {
+    public OperationCanceledException() {
+        super();
+    }
+    public OperationCanceledException(String message) {
+        super(message);
+    }
+    public OperationCanceledException(String message, Throwable cause) {
+        super(message, cause);
+    }
+    public OperationCanceledException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/core/java/android/accounts/package.html b/core/java/android/accounts/package.html
deleted file mode 100755
index c9f96a6..0000000
--- a/core/java/android/accounts/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-
-{@hide}
-
-</body>
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index 00b0593..8ec1445 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -94,6 +94,8 @@
 import android.view.WindowManagerImpl;
 import android.view.accessibility.AccessibilityManager;
 import android.view.inputmethod.InputMethodManager;
+import android.accounts.AccountManager;
+import android.accounts.IAccountManager;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -152,6 +154,7 @@
     private final static boolean DEBUG_ICONS = false;
 
     private static final Object sSync = new Object();
+    private static AccountManager sAccountManager;
     private static AlarmManager sAlarmManager;
     private static PowerManager sPowerManager;
     private static ConnectivityManager sConnectivityManager;
@@ -896,6 +899,8 @@
             return getActivityManager();
         } else if (ALARM_SERVICE.equals(name)) {
             return getAlarmManager();
+        } else if (ACCOUNT_SERVICE.equals(name)) {
+            return getAccountManager();
         } else if (POWER_SERVICE.equals(name)) {
             return getPowerManager();
         } else if (CONNECTIVITY_SERVICE.equals(name)) {
@@ -938,6 +943,17 @@
         return null;
     }
 
+    private AccountManager getAccountManager() {
+        synchronized (sSync) {
+            if (sAccountManager == null) {
+                IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);
+                IAccountManager service = IAccountManager.Stub.asInterface(b);
+                sAccountManager = new AccountManager(this, service);
+            }
+        }
+        return sAccountManager;
+    }
+
     private ActivityManager getActivityManager() {
         synchronized (mSync) {
             if (mActivityManager == null) {
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 6b17236..aeae5f9 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -171,6 +171,11 @@
         public String throwMethodName;
 
         /**
+         * Line number the exception was thrown from.
+         */
+        public int throwLineNumber;
+
+        /**
          * Stack trace.
          */
         public String stackTrace;
@@ -190,6 +195,7 @@
             throwFileName = in.readString();
             throwClassName = in.readString();
             throwMethodName = in.readString();
+            throwLineNumber = in.readInt();
             stackTrace = in.readString();
         }
 
@@ -202,6 +208,7 @@
             dest.writeString(throwFileName);
             dest.writeString(throwClassName);
             dest.writeString(throwMethodName);
+            dest.writeInt(throwLineNumber);
             dest.writeString(stackTrace);
         }
 
@@ -214,6 +221,7 @@
             pw.println(prefix + "throwFileName: " + throwFileName);
             pw.println(prefix + "throwClassName: " + throwClassName);
             pw.println(prefix + "throwMethodName: " + throwMethodName);
+            pw.println(prefix + "throwLineNumber: " + throwLineNumber);
             pw.println(prefix + "stackTrace: " + stackTrace);
         }
     }
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 951b4b0..c942a27 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -75,7 +75,7 @@
     public static final int UNBOND_REASON_REMOVED = 6;
 
     private static final String TAG = "BluetoothDevice";
-    
+
     private final IBluetoothDevice mService;
     /**
      * @hide - hide this because it takes a parameter of type
@@ -180,31 +180,6 @@
         return false;
     }
 
-    public String getVersion() {
-        try {
-            return mService.getVersion();
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return null;
-    }
-    public String getRevision() {
-        try {
-            return mService.getRevision();
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return null;
-    }
-    public String getManufacturer() {
-        try {
-            return mService.getManufacturer();
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return null;
-    }
-    public String getCompany() {
-        try {
-            return mService.getCompany();
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return null;
-    }
-
     /**
      * Get the current scan mode.
      * Used to determine if the local device is connectable and/or discoverable
@@ -241,11 +216,8 @@
     }
 
     public boolean startDiscovery() {
-        return startDiscovery(true);
-    }
-    public boolean startDiscovery(boolean resolveNames) {
         try {
-            return mService.startDiscovery(resolveNames);
+            return mService.startDiscovery();
         } catch (RemoteException e) {Log.e(TAG, "", e);}
         return false;
     }
@@ -263,88 +235,17 @@
         return false;
     }
 
-    public boolean startPeriodicDiscovery() {
-        try {
-            return mService.startPeriodicDiscovery();
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return false;
-    }
-    public boolean stopPeriodicDiscovery() {
-        try {
-            return mService.stopPeriodicDiscovery();
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return false;
-    }
-    public boolean isPeriodicDiscovery() {
-        try {
-            return mService.isPeriodicDiscovery();
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return false;
-    }
-
-    public String[] listRemoteDevices() {
-        try {
-            return mService.listRemoteDevices();
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return null;
-    }
-
     /**
-     * List remote devices that have a low level (ACL) connection.
-     *
-     * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have
-     * an ACL connection even when not paired - this is common for SDP queries
-     * or for in-progress pairing requests.
-     *
-     * In most cases you probably want to test if a higher level protocol is
-     * connected, rather than testing ACL connections.
-     *
-     * @return bluetooth hardware addresses of remote devices with a current
-     *         ACL connection. Array size is 0 if no devices have a
-     *         connection. Null on error.
-     */
-    public String[] listAclConnections() {
-        try {
-            return mService.listAclConnections();
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return null;
-    }
-
-    /**
-     * Check if a specified remote device has a low level (ACL) connection.
-     *
-     * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have
-     * an ACL connection even when not paired - this is common for SDP queries
-     * or for in-progress pairing requests.
-     *
-     * In most cases you probably want to test if a higher level protocol is
-     * connected, rather than testing ACL connections.
-     *
-     * @param address the Bluetooth hardware address you want to check.
-     * @return true if there is an ACL connection, false otherwise and on
-     *         error.
-     */
-    public boolean isAclConnected(String address) {
-        try {
-            return mService.isAclConnected(address);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return false;
-    }
-
-    /**
-     * Perform a low level (ACL) disconnection of a remote device.
-     *
-     * This forcably disconnects the ACL layer connection to a remote device,
-     * which will cause all RFCOMM, SDP and L2CAP connections to this remote
-     * device to close.
+     * Removes the remote device and the pairing information associated
+     * with it.
      *
      * @param address the Bluetooth hardware address you want to disconnect.
      * @return true if the device was disconnected, false otherwise and on
      *         error.
      */
-    public boolean disconnectRemoteDeviceAcl(String address) {
+    public boolean removeBond(String address) {
         try {
-            return mService.disconnectRemoteDeviceAcl(address);
+            return mService.removeBond(address);
         } catch (RemoteException e) {Log.e(TAG, "", e);}
         return false;
     }
@@ -377,16 +278,6 @@
     }
 
     /**
-     * Remove an already exisiting bonding (delete the link key).
-     */
-    public boolean removeBond(String address) {
-        try {
-            return mService.removeBond(address);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return false;
-    }
-
-    /**
      * List remote devices that are bonded (paired) to the local device.
      *
      * Bonding (pairing) is the process by which the user enters a pin code for
@@ -440,78 +331,25 @@
         return null;
     }
 
-    public String getRemoteVersion(String address) {
-        try {
-            return mService.getRemoteVersion(address);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return null;
-    }
-    public String getRemoteRevision(String address) {
-        try {
-            return mService.getRemoteRevision(address);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return null;
-    }
-    public String getRemoteManufacturer(String address) {
-        try {
-            return mService.getRemoteManufacturer(address);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return null;
-    }
-    public String getRemoteCompany(String address) {
-        try {
-            return mService.getRemoteCompany(address);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return null;
-    }
-
-    /**
-     * Returns the RFCOMM channel associated with the 16-byte UUID on
-     * the remote Bluetooth address.
-     *
-     * Performs a SDP ServiceSearchAttributeRequest transaction. The provided
-     * uuid is verified in the returned record. If there was a problem, or the
-     * specified uuid does not exist, -1 is returned.
-     */
-    public boolean getRemoteServiceChannel(String address, short uuid16,
-            IBluetoothDeviceCallback callback) {
-        try {
-            return mService.getRemoteServiceChannel(address, uuid16, callback);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return false;
-    }
-
-    /**
-     * Get the major, minor and servics classes of a remote device.
-     * These classes are encoded as a 32-bit integer. See BluetoothClass.
-     * @param address remote device
-     * @return 32-bit class suitable for use with BluetoothClass, or
-     *         BluetoothClass.ERROR on error
-     */
     public int getRemoteClass(String address) {
         try {
             return mService.getRemoteClass(address);
         } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return BluetoothClass.ERROR;
+        return BluetoothError.ERROR_IPC;
     }
 
-    public byte[] getRemoteFeatures(String address) {
+     public String[] getRemoteUuids(String address) {
         try {
-            return mService.getRemoteFeatures(address);
+            return mService.getRemoteUuids(address);
         } catch (RemoteException e) {Log.e(TAG, "", e);}
         return null;
     }
-    public String lastSeen(String address) {
-        try {
-            return mService.lastSeen(address);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return null;
-    }
-    public String lastUsed(String address) {
-        try {
-            return mService.lastUsed(address);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return null;
+
+    public int getRemoteServiceChannel(String address, String uuid) {
+         try {
+             return mService.getRemoteServiceChannel(address, uuid);
+         } catch (RemoteException e) {Log.e(TAG, "", e);}
+         return BluetoothError.ERROR_IPC;
     }
 
     public boolean setPin(String address, byte[] pin) {
diff --git a/core/java/android/bluetooth/BluetoothInputStream.java b/core/java/android/bluetooth/BluetoothInputStream.java
new file mode 100644
index 0000000..c060f32
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothInputStream.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * BluetoothInputStream.
+ *
+ * Used to write to a Bluetooth socket.
+ *
+ * @hide
+ */
+/*package*/ final class BluetoothInputStream extends InputStream {
+    private BluetoothSocket mSocket;
+
+    /*package*/ BluetoothInputStream(BluetoothSocket s) {
+        mSocket = s;
+    }
+
+    /**
+     * Return number of bytes available before this stream will block.
+     */
+    public int available() throws IOException {
+        return mSocket.availableNative();
+    }
+
+    public void close() throws IOException {
+        mSocket.close();
+    }
+
+    /**
+     * Reads a single byte from this stream and returns it as an integer in the
+     * range from 0 to 255. Returns -1 if the end of the stream has been
+     * reached. Blocks until one byte has been read, the end of the source
+     * stream is detected or an exception is thrown.
+     *
+     * @return the byte read or -1 if the end of stream has been reached.
+     * @throws IOException
+     *             if the stream is closed or another IOException occurs.
+     * @since Android 1.5
+     */
+    public int read() throws IOException {
+        byte b[] = new byte[1];
+        int ret = mSocket.readNative(b, 0, 1);
+        if (ret == 1) {
+            return (int)b[0] & 0xff;
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * Reads at most {@code length} bytes from this stream and stores them in
+     * the byte array {@code b} starting at {@code offset}.
+     *
+     * @param b
+     *            the byte array in which to store the bytes read.
+     * @param offset
+     *            the initial position in {@code buffer} to store the bytes
+     *            read from this stream.
+     * @param length
+     *            the maximum number of bytes to store in {@code b}.
+     * @return the number of bytes actually read or -1 if the end of the stream
+     *         has been reached.
+     * @throws IndexOutOfBoundsException
+     *             if {@code offset < 0} or {@code length < 0}, or if
+     *             {@code offset + length} is greater than the length of
+     *             {@code b}.
+     * @throws IOException
+     *             if the stream is closed or another IOException occurs.
+     * @since Android 1.5
+     */
+    public int read(byte[] b, int offset, int length) throws IOException {
+        if (b == null) {
+            throw new NullPointerException("byte array is null");
+        }
+        if ((offset | length) < 0 || length > b.length - offset) {
+            throw new ArrayIndexOutOfBoundsException("invalid offset or length");
+        }
+        return mSocket.readNative(b, offset, length);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothOutputStream.java b/core/java/android/bluetooth/BluetoothOutputStream.java
new file mode 100644
index 0000000..7e2ead4
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothOutputStream.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * BluetoothOutputStream.
+ *
+ * Used to read from a Bluetooth socket.
+ *
+ * @hide
+ */
+/*package*/ final class BluetoothOutputStream extends OutputStream {
+    private BluetoothSocket mSocket;
+
+    /*package*/ BluetoothOutputStream(BluetoothSocket s) {
+        mSocket = s;
+    }
+
+    /**
+     * Close this output stream and the socket associated with it.
+     */
+    public void close() throws IOException {
+        mSocket.close();
+    }
+
+    /**
+     * Writes a single byte to this stream. Only the least significant byte of
+     * the integer {@code oneByte} is written to the stream.
+     *
+     * @param oneByte
+     *            the byte to be written.
+     * @throws IOException
+     *             if an error occurs while writing to this stream.
+     * @since Android 1.0
+     */
+    public void write(int oneByte) throws IOException {
+        byte b[] = new byte[1];
+        b[0] = (byte)oneByte;
+        mSocket.writeNative(b, 0, 1);
+    }
+
+    /**
+     * Writes {@code count} bytes from the byte array {@code buffer} starting
+     * at position {@code offset} to this stream.
+     *
+     * @param b
+     *            the buffer to be written.
+     * @param offset
+     *            the start position in {@code buffer} from where to get bytes.
+     * @param count
+     *            the number of bytes from {@code buffer} to write to this
+     *            stream.
+     * @throws IOException
+     *             if an error occurs while writing to this stream.
+     * @throws IndexOutOfBoundsException
+     *             if {@code offset < 0} or {@code count < 0}, or if
+     *             {@code offset + count} is bigger than the length of
+     *             {@code buffer}.
+     * @since Android 1.0
+     */
+    public void write(byte[] b, int offset, int count) throws IOException {
+        if (b == null) {
+            throw new NullPointerException("buffer is null");
+        }
+        if ((offset | count) < 0 || count > b.length - offset) {
+            throw new IndexOutOfBoundsException("invalid offset or length");
+        }
+        mSocket.writeNative(b, offset, count);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
new file mode 100644
index 0000000..f3baeab
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Server (listening) Bluetooth Socket.
+ *
+ * Currently only supports RFCOMM sockets.
+ *
+ * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is
+ * also known as the Serial Port Profile (SPP).
+ *
+ * TODO: Consider exposing L2CAP sockets.
+ * TODO: Clean up javadoc grammer and formatting.
+ * TODO: Remove @hide
+ * @hide
+ */
+public final class BluetoothServerSocket implements Closeable {
+    private final BluetoothSocket mSocket;
+
+    /**
+     * Construct a listening, secure RFCOMM server socket.
+     * The remote device connecting to this socket will be authenticated and
+     * communication on this socket will be encrypted.
+     * Call #accept to retrieve connections to this socket.
+     * @return An RFCOMM BluetoothServerSocket
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     */
+    public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException {
+        BluetoothServerSocket socket = new BluetoothServerSocket(
+                BluetoothSocket.TYPE_RFCOMM, true, true, port);
+        try {
+            socket.mSocket.bindListenNative();
+        } catch (IOException e) {
+            try {
+                socket.close();
+            } catch (IOException e2) { }
+            throw e;
+        }
+        return socket;
+    }
+
+    /**
+     * Construct an unencrypted, unauthenticated, RFCOMM server socket.
+     * Call #accept to retrieve connections to this socket.
+     * @return An RFCOMM BluetoothServerSocket
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     */
+    public static BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
+        BluetoothServerSocket socket = new BluetoothServerSocket(
+                BluetoothSocket.TYPE_RFCOMM, false, false, port);
+        try {
+            socket.mSocket.bindListenNative();
+        } catch (IOException e) {
+            try {
+                socket.close();
+            } catch (IOException e2) { }
+            throw e;
+        }
+        return socket;
+    }
+
+    /**
+     * Construct a SCO server socket.
+     * Call #accept to retrieve connections to this socket.
+     * @return A SCO BluetoothServerSocket
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     */
+    public static BluetoothServerSocket listenUsingScoOn() throws IOException {
+        BluetoothServerSocket socket = new BluetoothServerSocket(
+                BluetoothSocket.TYPE_SCO, false, false, -1);
+        try {
+            socket.mSocket.bindListenNative();
+        } catch (IOException e) {
+            try {
+                socket.close();
+            } catch (IOException e2) { }
+            throw e;
+        }
+        return socket;
+    }
+
+    /**
+     * Construct a socket for incoming connections.
+     * @param type    type of socket
+     * @param auth    require the remote device to be authenticated
+     * @param encrypt require the connection to be encrypted
+     * @param port    remote port
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient priveleges
+     */
+    private BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
+            throws IOException {
+        mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port);
+    }
+
+    /**
+     * Block until a connection is established.
+     * Returns a connected #BluetoothSocket. This server socket can be reused
+     * for subsequent incoming connections by calling #accept repeatedly.
+     * #close can be used to abort this call from another thread.
+     * @return A connected #BluetoothSocket
+     * @throws IOException On error, for example this call was aborted
+     */
+    public BluetoothSocket accept() throws IOException {
+        return accept(-1);
+    }
+
+    /**
+     * Block until a connection is established, with timeout.
+     * Returns a connected #BluetoothSocket. This server socket can be reused
+     * for subsequent incoming connections by calling #accept repeatedly.
+     * #close can be used to abort this call from another thread.
+     * @return A connected #BluetoothSocket
+     * @throws IOException On error, for example this call was aborted, or
+     *                     timeout
+     */
+    public BluetoothSocket accept(int timeout) throws IOException {
+        return mSocket.acceptNative(timeout);
+    }
+
+    /**
+     * Closes this socket.
+     * This will cause other blocking calls on this socket to immediately
+     * throw an IOException.
+     */
+    public void close() throws IOException {
+        mSocket.closeNative();
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
new file mode 100644
index 0000000..de1f326
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Represents a connected or connecting Bluetooth Socket.
+ *
+ * Currently only supports RFCOMM sockets.
+ *
+ * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is
+ * also known as the Serial Port Profile (SPP).
+ *
+ * TODO: Consider exposing L2CAP sockets.
+ * TODO: Clean up javadoc grammer and formatting.
+ * TODO: Remove @hide
+ * @hide
+ */
+public final class BluetoothSocket implements Closeable {
+    /** Keep TYPE_RFCOMM etc in sync with BluetoothSocket.cpp */
+    /*package*/ static final int TYPE_RFCOMM = 1;
+    /*package*/ static final int TYPE_SCO = 2;
+    /*package*/ static final int TYPE_L2CAP = 3;
+
+    private final int mType;  /* one of TYPE_RFCOMM etc */
+    private final int mPort;  /* RFCOMM channel or L2CAP psm */
+    private final String mAddress;    /* remote address */
+    private final boolean mAuth;
+    private final boolean mEncrypt;
+    private final BluetoothInputStream mInputStream;
+    private final BluetoothOutputStream mOutputStream;
+
+    private int mSocketData;    /* used by native code only */
+
+    /**
+     * Construct a secure RFCOMM socket ready to start an outgoing connection.
+     * Call #connect on the returned #BluetoothSocket to begin the connection.
+     * The remote device will be authenticated and communication on this socket
+     * will be encrypted.
+     * @param address remote Bluetooth address that this socket can connect to
+     * @param port    remote port
+     * @return an RFCOMM BluetoothSocket
+     * @throws IOException on error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     */
+    public static BluetoothSocket createRfcommSocket(String address, int port)
+            throws IOException {
+        return new BluetoothSocket(TYPE_RFCOMM, -1, true, true, address, port);
+    }
+
+    /**
+     * Construct an insecure RFCOMM socket ready to start an outgoing
+     * connection.
+     * Call #connect on the returned #BluetoothSocket to begin the connection.
+     * The remote device will not be authenticated and communication on this
+     * socket will not be encrypted.
+     * @param address remote Bluetooth address that this socket can connect to
+     * @param port    remote port
+     * @return An RFCOMM BluetoothSocket
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     */
+    public static BluetoothSocket createInsecureRfcommSocket(String address, int port)
+            throws IOException {
+        return new BluetoothSocket(TYPE_RFCOMM, -1, false, false, address, port);
+    }
+
+    /**
+     * Construct a SCO socket ready to start an outgoing connection.
+     * Call #connect on the returned #BluetoothSocket to begin the connection.
+     * @param address remote Bluetooth address that this socket can connect to
+     * @return a SCO BluetoothSocket
+     * @throws IOException on error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     */
+    public static BluetoothSocket createScoSocket(String address, int port)
+            throws IOException {
+        return new BluetoothSocket(TYPE_SCO, -1, true, true, address, port);
+    }
+
+    /**
+     * Construct a Bluetooth.
+     * @param type    type of socket
+     * @param fd      fd to use for connected socket, or -1 for a new socket
+     * @param auth    require the remote device to be authenticated
+     * @param encrypt require the connection to be encrypted
+     * @param address remote Bluetooth address that this socket can connect to
+     * @param port    remote port
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient priveleges
+     */
+    /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
+            int port) throws IOException {
+        mType = type;
+        mAuth = auth;
+        mEncrypt = encrypt;
+        mAddress = address;
+        mPort = port;
+        if (fd == -1) {
+            initSocketNative();
+        } else {
+            initSocketFromFdNative(fd);
+        }
+        mInputStream = new BluetoothInputStream(this);
+        mOutputStream = new BluetoothOutputStream(this);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Attempt to connect to a remote device.
+     * This method will block until a connection is made or the connection
+     * fails. If this method returns without an exception then this socket
+     * is now connected. #close can be used to abort this call from another
+     * thread.
+     * @throws IOException On error, for example connection failure
+     */
+    public void connect() throws IOException {
+        connectNative();
+    }
+
+    /**
+     * Closes this socket.
+     * This will cause other blocking calls on this socket to immediately
+     * throw an IOException.
+     */
+    public void close() throws IOException {
+        closeNative();
+    }
+
+    /**
+     * Return the address we are connecting, or connected, to.
+     * @return Bluetooth address, or null if this socket has not yet attempted
+     *         or established a connection.
+     */
+    public String getAddress() {
+        return mAddress;
+    }
+
+    /**
+     * Get the input stream associated with this socket.
+     * The input stream will be returned even if the socket is not yet
+     * connected, but operations on that stream will throw IOException until
+     * the associated socket is connected.
+     * @return InputStream
+     */
+    public InputStream getInputStream() throws IOException {
+        return mInputStream;
+    }
+
+    /**
+     * Get the output stream associated with this socket.
+     * The output stream will be returned even if the socket is not yet
+     * connected, but operations on that stream will throw IOException until
+     * the associated socket is connected.
+     * @return OutputStream
+     */
+    public OutputStream getOutputStream() throws IOException {
+        return mOutputStream;
+    }
+
+    private native void initSocketNative() throws IOException;
+    private native void initSocketFromFdNative(int fd) throws IOException;
+    private native void connectNative() throws IOException;
+    /*package*/ native void bindListenNative() throws IOException;
+    /*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException;
+    /*package*/ native int availableNative() throws IOException;
+    /*package*/ native int readNative(byte[] b, int offset, int length) throws IOException;
+    /*package*/ native int writeNative(byte[] b, int offset, int length) throws IOException;
+    /*package*/ native void closeNative() throws IOException;
+    private native void destroyNative() throws IOException;
+}
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
new file mode 100644
index 0000000..96b93f9
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.util.UUID;
+
+/**
+* Static helper methods and constants to decode the UUID of remote devices.
+*  @hide
+*/
+public final class BluetoothUuid {
+
+    /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
+     * for the various services.
+     *
+     * The following 128 bit values are calculated as:
+     *  uuid * 2^96 + BASE_UUID
+     */
+    public static final UUID AudioSink = UUID.fromString("0000110A-0000-1000-8000-00805F9B34FB");
+    public static final UUID AudioSource = UUID.fromString("0000110B-0000-1000-8000-00805F9B34FB");
+    public static final UUID AdvAudioDist = UUID.fromString("0000110D-0000-1000-8000-00805F9B34FB");
+    public static final UUID HSP       = UUID.fromString("00001108-0000-1000-8000-00805F9B34FB");
+    public static final UUID HeadsetHS = UUID.fromString("00001131-0000-1000-8000-00805F9B34FB");
+    public static final UUID Handsfree  = UUID.fromString("0000111e-0000-1000-8000-00805F9B34FB");
+    public static final UUID HandsfreeAudioGateway
+                                          = UUID.fromString("0000111f-0000-1000-8000-00805F9B34FB");
+
+    public static boolean isAudioSource(UUID uuid) {
+        return uuid.equals(AudioSource);
+    }
+
+    public static boolean isAudioSink(UUID uuid) {
+        return uuid.equals(AudioSink);
+    }
+
+    public static boolean isAdvAudioDist(UUID uuid) {
+        return uuid.equals(AdvAudioDist);
+    }
+
+    public static boolean isHandsfree(UUID uuid) {
+        return uuid.equals(Handsfree) || uuid.equals(HandsfreeAudioGateway);
+    }
+
+    public static boolean isHeadset(UUID uuid) {
+        return uuid.equals(HSP) || uuid.equals(HeadsetHS);
+    }
+}
+
diff --git a/core/java/android/bluetooth/Database.java b/core/java/android/bluetooth/Database.java
deleted file mode 100644
index fef641a..0000000
--- a/core/java/android/bluetooth/Database.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.bluetooth.RfcommSocket;
-
-import android.util.Log;
-
-import java.io.*;
-import java.util.*;
-
-/**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- * 
- * A low-level API to the Service Discovery Protocol (SDP) Database.
- *
- * Allows service records to be added to the local SDP database. Once added,
- * these services will be advertised to remote devices when they make SDP
- * queries on this device.
- *
- * Currently this API is a thin wrapper to the bluez SDP Database API. See:
- * http://wiki.bluez.org/wiki/Database
- * http://wiki.bluez.org/wiki/HOWTO/ManagingServiceRecords
- * @hide
- */
-public final class Database {
-    private static Database mInstance;
-
-    private static final String sLogName = "android.bluetooth.Database";
-
-    /**
-     * Class load time initialization
-     */
-    static {
-        classInitNative();
-    }
-    private native static void classInitNative();
-
-    /**
-     * Private to enforce singleton property
-     */
-    private Database() {
-        initializeNativeDataNative();
-    }
-    private native void initializeNativeDataNative();
-
-    protected void finalize() throws Throwable {
-        try {
-            cleanupNativeDataNative();
-        } finally {
-            super.finalize();
-        }
-    }
-    private native void cleanupNativeDataNative();
-
-    /**
-     * Singelton accessor
-     * @return The singleton instance of Database
-     */
-    public static synchronized Database getInstance() {
-        if (mInstance == null) {
-            mInstance = new Database();
-        }
-        return mInstance;
-    }
-
-    /**
-     * Advertise a service with an RfcommSocket.
-     *
-     * This adds the service the SDP Database with the following attributes
-     * set: Service Name, Protocol Descriptor List, Service Class ID List
-     * TODO: Construct a byte[] record directly, rather than via XML.
-     * @param       socket The rfcomm socket to advertise (by channel).
-     * @param       serviceName A short name for this service
-     * @param       uuid
-     *                  Unique identifier for this service, by which clients
-     *                  can search for your service
-     * @return      Handle to the new service record
-     */
-    public int advertiseRfcommService(RfcommSocket socket,
-                                      String serviceName,
-                                      UUID uuid) throws IOException {
-        String xmlRecord =
-                "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
-                "<record>\n" +
-                "  <attribute id=\"0x0001\">\n" + // ServiceClassIDList
-                "    <sequence>\n" +
-                "      <uuid value=\""
-                        + uuid.toString() +       // UUID for this service
-                        "\"/>\n" +
-                "    </sequence>\n" +
-                "  </attribute>\n" +
-                "  <attribute id=\"0x0004\">\n" + // ProtocolDescriptorList
-                "    <sequence>\n" +
-                "      <sequence>\n" +
-                "        <uuid value=\"0x0100\"/>\n" +  // L2CAP
-                "      </sequence>\n" +
-                "      <sequence>\n" +
-                "        <uuid value=\"0x0003\"/>\n" +  // RFCOMM
-                "        <uint8 value=\"" +
-                        socket.getPort() +              // RFCOMM port
-                        "\" name=\"channel\"/>\n" +
-                "      </sequence>\n" +
-                "    </sequence>\n" +
-                "  </attribute>\n" +
-                "  <attribute id=\"0x0100\">\n" + // ServiceName
-                "    <text value=\"" + serviceName + "\"/>\n" +
-                "  </attribute>\n" +
-                "</record>\n";
-        Log.i(sLogName, xmlRecord);
-        return addServiceRecordFromXml(xmlRecord);
-    }
-
-
-    /**
-     * Add a new service record.
-     * @param record The byte[] record
-     * @return       A handle to the new record
-     */
-    public synchronized int addServiceRecord(byte[] record) throws IOException {
-        int handle = addServiceRecordNative(record);
-        Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle));
-        return handle;
-    }
-    private native int addServiceRecordNative(byte[] record)
-            throws IOException;
-
-    /**
-     * Add a new service record, using XML.
-     * @param record The record as an XML string
-     * @return       A handle to the new record
-     */
-    public synchronized int addServiceRecordFromXml(String record) throws IOException {
-        int handle = addServiceRecordFromXmlNative(record);
-        Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle));
-        return handle;
-    }
-    private native int addServiceRecordFromXmlNative(String record)
-            throws IOException;
-
-    /**
-     * Update an exisiting service record.
-     * @param handle Handle to exisiting record
-     * @param record The updated byte[] record
-     */
-    public synchronized void updateServiceRecord(int handle, byte[] record) {
-        try {
-            updateServiceRecordNative(handle, record);
-        } catch (IOException e) {
-            Log.e(getClass().toString(), e.getMessage());
-        }
-    }
-    private native void updateServiceRecordNative(int handle, byte[] record)
-            throws IOException;
-
-    /**
-     * Update an exisiting record, using XML.
-     * @param handle Handle to exisiting record
-     * @param record The record as an XML string.
-     */
-    public synchronized void updateServiceRecordFromXml(int handle, String record) {
-        try {
-            updateServiceRecordFromXmlNative(handle, record);
-        } catch (IOException e) {
-            Log.e(getClass().toString(), e.getMessage());
-        }
-    }
-    private native void updateServiceRecordFromXmlNative(int handle, String record)
-            throws IOException;
-
-    /**
-     * Remove a service record.
-     * It is only possible to remove service records that were added by the
-     * current connection.
-     * @param handle Handle to exisiting record to be removed
-     */
-    public synchronized void removeServiceRecord(int handle) {
-        try {
-            removeServiceRecordNative(handle);
-        } catch (IOException e) {
-            Log.e(getClass().toString(), e.getMessage());
-        }
-    }
-    private native void removeServiceRecordNative(int handle) throws IOException;
-}
diff --git a/core/java/android/bluetooth/IBluetoothDevice.aidl b/core/java/android/bluetooth/IBluetoothDevice.aidl
index 6cd792e..c249c81 100644
--- a/core/java/android/bluetooth/IBluetoothDevice.aidl
+++ b/core/java/android/bluetooth/IBluetoothDevice.aidl
@@ -16,8 +16,6 @@
 
 package android.bluetooth;
 
-import android.bluetooth.IBluetoothDeviceCallback;
-
 /**
  * System private API for talking with the Bluetooth service.
  *
@@ -33,10 +31,6 @@
     String getAddress();
     String getName();
     boolean setName(in String name);
-    String getVersion();
-    String getRevision();
-    String getManufacturer();
-    String getCompany();
 
     int getScanMode();
     boolean setScanMode(int mode);
@@ -44,17 +38,9 @@
     int getDiscoverableTimeout();
     boolean setDiscoverableTimeout(int timeout);
 
-    boolean startDiscovery(boolean resolveNames);
+    boolean startDiscovery();
     boolean cancelDiscovery();
     boolean isDiscovering();
-    boolean startPeriodicDiscovery();
-    boolean stopPeriodicDiscovery();
-    boolean isPeriodicDiscovery();
-    String[] listRemoteDevices();
-
-    String[] listAclConnections();
-    boolean isAclConnected(in String address);
-    boolean disconnectRemoteDeviceAcl(in String address);
 
     boolean createBond(in String address);
     boolean cancelBondProcess(in String address);
@@ -63,15 +49,9 @@
     int getBondState(in String address);
 
     String getRemoteName(in String address);
-    String getRemoteVersion(in String address);
-    String getRemoteRevision(in String address);
     int getRemoteClass(in String address);
-    String getRemoteManufacturer(in String address);
-    String getRemoteCompany(in String address);
-    boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback);
-    byte[] getRemoteFeatures(in String adddress);
-    String lastSeen(in String address);
-    String lastUsed(in String address);
+    String[] getRemoteUuids(in String address);
+    int getRemoteServiceChannel(in String address, String uuid);
 
     boolean setPin(in String address, in byte[] pin);
     boolean cancelPin(in String address);
diff --git a/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl b/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl
deleted file mode 100644
index d057093..0000000
--- a/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-/**
- * {@hide}
- */
-oneway interface IBluetoothDeviceCallback
-{
-    void onGetRemoteServiceChannelResult(in String address, int channel);
-}
diff --git a/core/java/android/bluetooth/RfcommSocket.java b/core/java/android/bluetooth/RfcommSocket.java
deleted file mode 100644
index a33263f..0000000
--- a/core/java/android/bluetooth/RfcommSocket.java
+++ /dev/null
@@ -1,674 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import java.io.IOException;
-import java.io.FileOutputStream;
-import java.io.FileInputStream;
-import java.io.OutputStream;
-import java.io.InputStream;
-import java.io.FileDescriptor;
-
-/**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- * 
- * This class implements an API to the Bluetooth RFCOMM layer. An RFCOMM socket
- * is similar to a normal socket in that it takes an address and a port number.
- * The difference is of course that the address is a Bluetooth-device address,
- * and the port number is an RFCOMM channel. The API allows for the
- * establishment of listening sockets via methods
- * {@link #bind(String, int) bind}, {@link #listen(int) listen}, and
- * {@link #accept(RfcommSocket, int) accept}, as well as for the making of
- * outgoing connections with {@link #connect(String, int) connect},
- * {@link #connectAsync(String, int) connectAsync}, and
- * {@link #waitForAsyncConnect(int) waitForAsyncConnect}.
- * 
- * After constructing a socket, you need to {@link #create() create} it and then
- * {@link #destroy() destroy} it when you are done using it. Both
- * {@link #create() create} and {@link #accept(RfcommSocket, int) accept} return
- * a {@link java.io.FileDescriptor FileDescriptor} for the actual data.
- * Alternatively, you may call {@link #getInputStream() getInputStream} and
- * {@link #getOutputStream() getOutputStream} to retrieve the respective streams
- * without going through the FileDescriptor.
- *
- * @hide
- */
-public class RfcommSocket {
-
-    /**
-     * Used by the native implementation of the class.
-     */
-    private int mNativeData;
-
-    /**
-     * Used by the native implementation of the class.
-     */
-    private int mPort;
-
-    /**
-     * Used by the native implementation of the class.
-     */
-    private String mAddress;
-
-    /**
-     * We save the return value of {@link #create() create} and
-     * {@link #accept(RfcommSocket,int) accept} in this variable, and use it to
-     * retrieve the I/O streams.
-     */
-    private FileDescriptor mFd;
-
-    /**
-     * After a call to {@link #waitForAsyncConnect(int) waitForAsyncConnect},
-     * if the return value is zero, then, the the remaining time left to wait is
-     * written into this variable (by the native implementation). It is possible
-     * that {@link #waitForAsyncConnect(int) waitForAsyncConnect} returns before
-     * the user-specified timeout expires, which is why we save the remaining
-     * time in this member variable for the user to retrieve by calling method
-     * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs}.
-     */
-    private int mTimeoutRemainingMs;
-
-    /**
-     * Set to true when an asynchronous (nonblocking) connect is in progress.
-     * {@see #connectAsync(String,int)}.
-     */
-    private boolean mIsConnecting;
-
-    /**
-     * Set to true after a successful call to {@link #bind(String,int) bind} and
-     * used for error checking in {@link #listen(int) listen}. Reset to false
-     * on {@link #destroy() destroy}.
-     */
-    private boolean mIsBound = false;
-
-    /**
-     * Set to true after a successful call to {@link #listen(int) listen} and
-     * used for error checking in {@link #accept(RfcommSocket,int) accept}.
-     * Reset to false on {@link #destroy() destroy}.
-     */
-    private boolean mIsListening = false;
-
-    /**
-     * Used to store the remaining time after an accept with a non-negative
-     * timeout returns unsuccessfully. It is possible that a blocking
-     * {@link #accept(int) accept} may wait for less than the time specified by
-     * the user, which is why we store the remainder in this member variable for
-     * it to be retrieved with method
-     * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs}.
-     */
-    private int mAcceptTimeoutRemainingMs;
-
-    /**
-     * Maintained by {@link #getInputStream() getInputStream}.
-     */
-    protected FileInputStream mInputStream;
-
-    /**
-     * Maintained by {@link #getOutputStream() getOutputStream}.
-     */
-    protected FileOutputStream mOutputStream;
-
-    private native void initializeNativeDataNative();
-
-    /**
-     * Constructor.
-     */
-    public RfcommSocket() {
-        initializeNativeDataNative();
-    }
-
-    private native void cleanupNativeDataNative();
-
-    /**
-     * Called by the GC to clean up the native data that we set up when we
-     * construct the object.
-     */
-    protected void finalize() throws Throwable {
-        try {
-            cleanupNativeDataNative();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    private native static void classInitNative();
-
-    static {
-        classInitNative();
-    }
-
-    /**
-     * Creates a socket. You need to call this method before performing any
-     * other operation on a socket.
-     * 
-     * @return FileDescriptor for the data stream.
-     * @throws IOException
-     * @see #destroy()
-     */
-    public FileDescriptor create() throws IOException {
-        if (mFd == null) {
-            mFd = createNative();
-        }
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-        return mFd;
-    }
-
-    private native FileDescriptor createNative();
-
-    /**
-     * Destroys a socket created by {@link #create() create}. Call this
-     * function when you no longer use the socket in order to release the
-     * underlying OS resources.
-     * 
-     * @see #create()
-     */
-    public void destroy() {
-        synchronized (this) {
-            destroyNative();
-            mFd = null;
-            mIsBound = false;
-            mIsListening = false;
-        }
-    }
-
-    private native void destroyNative();
-
-    /**
-     * Returns the {@link java.io.FileDescriptor FileDescriptor} of the socket.
-     * 
-     * @return the FileDescriptor
-     * @throws IOException
-     *             when the socket has not been {@link #create() created}.
-     */
-    public FileDescriptor getFileDescriptor() throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-        return mFd;
-    }
-
-    /**
-     * Retrieves the input stream from the socket. Alternatively, you can do
-     * that from the FileDescriptor returned by {@link #create() create} or
-     * {@link #accept(RfcommSocket, int) accept}.
-     * 
-     * @return InputStream
-     * @throws IOException
-     *             if you have not called {@link #create() create} on the
-     *             socket.
-     */
-    public InputStream getInputStream() throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-
-        synchronized (this) {
-            if (mInputStream == null) {
-                mInputStream = new FileInputStream(mFd);
-            }
-
-            return mInputStream;
-        }
-    }
-
-    /**
-     * Retrieves the output stream from the socket. Alternatively, you can do
-     * that from the FileDescriptor returned by {@link #create() create} or
-     * {@link #accept(RfcommSocket, int) accept}.
-     * 
-     * @return OutputStream
-     * @throws IOException
-     *             if you have not called {@link #create() create} on the
-     *             socket.
-     */
-    public OutputStream getOutputStream() throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-
-        synchronized (this) {
-            if (mOutputStream == null) {
-                mOutputStream = new FileOutputStream(mFd);
-            }
-
-            return mOutputStream;
-        }
-    }
-
-    /**
-     * Starts a blocking connect to a remote RFCOMM socket. It takes the address
-     * of a device and the RFCOMM channel (port) to which to connect.
-     * 
-     * @param address
-     *            is the Bluetooth address of the remote device.
-     * @param port
-     *            is the RFCOMM channel
-     * @return true on success, false on failure
-     * @throws IOException
-     *             if {@link #create() create} has not been called.
-     * @see #connectAsync(String, int)
-     */
-    public boolean connect(String address, int port) throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            return connectNative(address, port);
-        }
-    }
-
-    private native boolean connectNative(String address, int port);
-
-    /**
-     * Starts an asynchronous (nonblocking) connect to a remote RFCOMM socket.
-     * It takes the address of the device to connect to, as well as the RFCOMM
-     * channel (port). On successful return (return value is true), you need to
-     * call method {@link #waitForAsyncConnect(int) waitForAsyncConnect} to
-     * block for up to a specified number of milliseconds while waiting for the
-     * asyncronous connect to complete.
-     * 
-     * @param address
-     *            of remote device
-     * @param port
-     *            the RFCOMM channel
-     * @return true when the asynchronous connect has successfully started,
-     *         false if there was an error.
-     * @throws IOException
-     *             is you have not called {@link #create() create}
-     * @see #waitForAsyncConnect(int)
-     * @see #getRemainingAsyncConnectWaitingTimeMs()
-     * @see #connect(String, int)
-     */
-    public boolean connectAsync(String address, int port) throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            mIsConnecting = connectAsyncNative(address, port);
-            return mIsConnecting;
-        }
-    }
-
-    private native boolean connectAsyncNative(String address, int port);
-
-    /**
-     * Interrupts an asynchronous connect in progress. This method does nothing
-     * when there is no asynchronous connect in progress.
-     * 
-     * @throws IOException
-     *             if you have not called {@link #create() create}.
-     * @see #connectAsync(String, int)
-     */
-    public void interruptAsyncConnect() throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            if (mIsConnecting) {
-                mIsConnecting = !interruptAsyncConnectNative();
-            }
-        }
-    }
-
-    private native boolean interruptAsyncConnectNative();
-
-    /**
-     * Tells you whether there is an asynchronous connect in progress. This
-     * method returns an undefined value when there is a synchronous connect in
-     * progress.
-     * 
-     * @return true if there is an asyc connect in progress, false otherwise
-     * @see #connectAsync(String, int)
-     */
-    public boolean isConnecting() {
-        return mIsConnecting;
-    }
-
-    /**
-     * Blocks for a specified amount of milliseconds while waiting for an
-     * asynchronous connect to complete. Returns an integer value to indicate
-     * one of the following: the connect succeeded, the connect is still in
-     * progress, or the connect failed. It is possible for this method to block
-     * for less than the time specified by the user, and still return zero
-     * (i.e., async connect is still in progress.) For this reason, if the
-     * return value is zero, you need to call method
-     * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs}
-     * to retrieve the remaining time.
-     * 
-     * @param timeoutMs
-     *            the time to block while waiting for the async connect to
-     *            complete.
-     * @return a positive value if the connect succeeds; zero, if the connect is
-     *         still in progress, and a negative value if the connect failed.
-     * 
-     * @throws IOException
-     * @see #getRemainingAsyncConnectWaitingTimeMs()
-     * @see #connectAsync(String, int)
-     */
-    public int waitForAsyncConnect(int timeoutMs) throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            int ret = waitForAsyncConnectNative(timeoutMs);
-            if (ret != 0) {
-                mIsConnecting = false;
-            }
-            return ret;
-        }
-    }
-
-    private native int waitForAsyncConnectNative(int timeoutMs);
-
-    /**
-     * Returns the number of milliseconds left to wait after the last call to
-     * {@link #waitForAsyncConnect(int) waitForAsyncConnect}.
-     * 
-     * It is possible that waitForAsyncConnect() waits for less than the time
-     * specified by the user, and still returns zero (i.e., async connect is
-     * still in progress.) For this reason, if the return value is zero, you
-     * need to call this method to retrieve the remaining time before you call
-     * waitForAsyncConnect again.
-     * 
-     * @return the remaining timeout in milliseconds.
-     * @see #waitForAsyncConnect(int)
-     * @see #connectAsync(String, int)
-     */
-    public int getRemainingAsyncConnectWaitingTimeMs() {
-        return mTimeoutRemainingMs;
-    }
-
-    /**
-     * Shuts down both directions on a socket.
-     * 
-     * @return true on success, false on failure; if the return value is false,
-     *         the socket might be left in a patially shut-down state (i.e. one
-     *         direction is shut down, but the other is still open.) In this
-     *         case, you should {@link #destroy() destroy} and then
-     *         {@link #create() create} the socket again.
-     * @throws IOException
-     *             is you have not caled {@link #create() create}.
-     * @see #shutdownInput()
-     * @see #shutdownOutput()
-     */
-    public boolean shutdown() throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            if (shutdownNative(true)) {
-                return shutdownNative(false);
-            }
-
-            return false;
-        }
-    }
-
-    /**
-     * Shuts down the input stream of the socket, but leaves the output stream
-     * in its current state.
-     * 
-     * @return true on success, false on failure
-     * @throws IOException
-     *             is you have not called {@link #create() create}
-     * @see #shutdown()
-     * @see #shutdownOutput()
-     */
-    public boolean shutdownInput() throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            return shutdownNative(true);
-        }
-    }
-
-    /**
-     * Shut down the output stream of the socket, but leaves the input stream in
-     * its current state.
-     * 
-     * @return true on success, false on failure
-     * @throws IOException
-     *             is you have not called {@link #create() create}
-     * @see #shutdown()
-     * @see #shutdownInput()
-     */
-    public boolean shutdownOutput() throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            return shutdownNative(false);
-        }
-    }
-
-    private native boolean shutdownNative(boolean shutdownInput);
-
-    /**
-     * Tells you whether a socket is connected to another socket. This could be
-     * for input or output or both.
-     * 
-     * @return true if connected, false otherwise.
-     * @see #isInputConnected()
-     * @see #isOutputConnected()
-     */
-    public boolean isConnected() {
-        return isConnectedNative() > 0;
-    }
-
-    /**
-     * Determines whether input is connected (i.e., whether you can receive data
-     * on this socket.)
-     * 
-     * @return true if input is connected, false otherwise.
-     * @see #isConnected()
-     * @see #isOutputConnected()
-     */
-    public boolean isInputConnected() {
-        return (isConnectedNative() & 1) != 0;
-    }
-
-    /**
-     * Determines whether output is connected (i.e., whether you can send data
-     * on this socket.)
-     * 
-     * @return true if output is connected, false otherwise.
-     * @see #isConnected()
-     * @see #isInputConnected()
-     */
-    public boolean isOutputConnected() {
-        return (isConnectedNative() & 2) != 0;
-    }
-
-    private native int isConnectedNative();
-
-    /**
-     * Binds a listening socket to the local device, or a non-listening socket
-     * to a remote device. The port is automatically selected as the first
-     * available port in the range 12 to 30.
-     *
-     * NOTE: Currently we ignore the device parameter and always bind the socket
-     * to the local device, assuming that it is a listening socket.
-     *
-     * TODO: Use bind(0) in native code to have the kernel select an unused
-     * port.
-     *
-     * @param device
-     *            Bluetooth address of device to bind to (currently ignored).
-     * @return true on success, false on failure
-     * @throws IOException
-     *             if you have not called {@link #create() create}
-     * @see #listen(int)
-     * @see #accept(RfcommSocket,int)
-     */
-    public boolean bind(String device) throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-        for (int port = 12; port <= 30; port++) {
-            if (bindNative(device, port)) {
-                mIsBound = true;
-                return true;
-            }
-        }
-        mIsBound = false;
-        return false;
-    }
-
-    /**
-     * Binds a listening socket to the local device, or a non-listening socket
-     * to a remote device.
-     *
-     * NOTE: Currently we ignore the device parameter and always bind the socket
-     * to the local device, assuming that it is a listening socket.
-     *
-     * @param device
-     *            Bluetooth address of device to bind to (currently ignored).
-     * @param port
-     *            RFCOMM channel to bind socket to.
-     * @return true on success, false on failure
-     * @throws IOException
-     *             if you have not called {@link #create() create}
-     * @see #listen(int)
-     * @see #accept(RfcommSocket,int)
-     */
-    public boolean bind(String device, int port) throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-        mIsBound = bindNative(device, port);
-        return mIsBound;
-    }
-
-    private native boolean bindNative(String device, int port);
-
-    /**
-     * Starts listening for incoming connections on this socket, after it has
-     * been bound to an address and RFCOMM channel with
-     * {@link #bind(String,int) bind}.
-     * 
-     * @param backlog
-     *            the number of pending incoming connections to queue for
-     *            {@link #accept(RfcommSocket, int) accept}.
-     * @return true on success, false on failure
-     * @throws IOException
-     *             if you have not called {@link #create() create} or if the
-     *             socket has not been bound to a device and RFCOMM channel.
-     */
-    public boolean listen(int backlog) throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-        if (!mIsBound) {
-            throw new IOException("socket not bound");
-        }
-        mIsListening = listenNative(backlog);
-        return mIsListening;
-    }
-
-    private native boolean listenNative(int backlog);
-
-    /**
-     * Accepts incoming-connection requests for a listening socket bound to an
-     * RFCOMM channel. The user may provide a time to wait for an incoming
-     * connection.
-     * 
-     * Note that this method may return null (i.e., no incoming connection)
-     * before the user-specified timeout expires. For this reason, on a null
-     * return value, you need to call
-     * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs}
-     * in order to see how much time is left to wait, before you call this
-     * method again.
-     * 
-     * @param newSock
-     *            is set to the new socket that is created as a result of a
-     *            successful accept.
-     * @param timeoutMs
-     *            time (in milliseconds) to block while waiting to an
-     *            incoming-connection request. A negative value is an infinite
-     *            wait.
-     * @return FileDescriptor of newSock on success, null on failure. Failure
-     *         occurs if the timeout expires without a successful connect.
-     * @throws IOException
-     *             if the socket has not been {@link #create() create}ed, is
-     *             not bound, or is not a listening socket.
-     * @see #bind(String, int)
-     * @see #listen(int)
-     * @see #getRemainingAcceptWaitingTimeMs()
-     */
-    public FileDescriptor accept(RfcommSocket newSock, int timeoutMs)
-            throws IOException {
-        synchronized (newSock) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            if (mIsListening == false) {
-                throw new IOException("not listening on socket");
-            }
-            newSock.mFd = acceptNative(newSock, timeoutMs);
-            return newSock.mFd;
-        }
-    }
-
-    /**
-     * Returns the number of milliseconds left to wait after the last call to
-     * {@link #accept(RfcommSocket, int) accept}.
-     * 
-     * Since accept() may return null (i.e., no incoming connection) before the
-     * user-specified timeout expires, you need to call this method in order to
-     * see how much time is left to wait, and wait for that amount of time
-     * before you call accept again.
-     * 
-     * @return the remaining time, in milliseconds.
-     */
-    public int getRemainingAcceptWaitingTimeMs() {
-        return mAcceptTimeoutRemainingMs;
-    }
-
-    private native FileDescriptor acceptNative(RfcommSocket newSock,
-            int timeoutMs);
-
-    /**
-     * Get the port (rfcomm channel) associated with this socket.
-     *
-     * This is only valid if the port has been set via a successful call to
-     * {@link #bind(String, int)}, {@link #connect(String, int)}
-     * or {@link #connectAsync(String, int)}. This can be checked
-     * with {@link #isListening()} and {@link #isConnected()}.
-     * @return Port (rfcomm channel)
-     */
-    public int getPort() throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-        if (!mIsListening && !isConnected()) {
-            throw new IOException("not listening or connected on socket");
-        }
-        return mPort;
-    }
-
-    /**
-     * Return true if this socket is listening ({@link #listen(int)}
-     * has been called successfully).
-     */
-    public boolean isListening() {
-        return mIsListening;
-    }
-}
diff --git a/core/java/android/content/AbstractCursorEntityIterator.java b/core/java/android/content/AbstractCursorEntityIterator.java
new file mode 100644
index 0000000..bf3c4de
--- /dev/null
+++ b/core/java/android/content/AbstractCursorEntityIterator.java
@@ -0,0 +1,112 @@
+package android.content;
+
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.RemoteException;
+
+/**
+ * An abstract class that makes it easy to implement an EntityIterator over a cursor.
+ * The user must implement {@link #newEntityFromCursorLocked}, which runs inside of a
+ * database transaction.
+ */
+public abstract class AbstractCursorEntityIterator implements EntityIterator {
+    private final Cursor mEntityCursor;
+    private final SQLiteDatabase mDb;
+    private volatile Entity mNextEntity;
+    private volatile boolean mIsClosed;
+
+    public AbstractCursorEntityIterator(SQLiteDatabase db, Cursor entityCursor) {
+        mEntityCursor = entityCursor;
+        mDb = db;
+        mNextEntity = null;
+        mIsClosed = false;
+    }
+
+    /**
+     * If there are entries left in the cursor then advance the cursor and use the new row to
+     * populate mNextEntity. If the cursor is at the end or if advancing it causes the cursor
+     * to become at the end then set mEntityCursor to null. If newEntityFromCursor returns null
+     * then continue advancing until it either returns a non-null Entity or the cursor reaches
+     * the end.
+     */
+    private void fillEntityIfAvailable() {
+        while (mNextEntity == null) {
+            if (!mEntityCursor.moveToNext()) {
+                // the cursor is at then end, bail out
+                return;
+            }
+            // This may return null if newEntityFromCursor is not able to create an entity
+            // from the current cursor position. In that case this method will loop and try
+            // the next cursor position
+            mNextEntity = newEntityFromCursorLocked(mEntityCursor);
+        }
+        mDb.beginTransaction();
+        try {
+            int position = mEntityCursor.getPosition();
+            mNextEntity = newEntityFromCursorLocked(mEntityCursor);
+            int newPosition = mEntityCursor.getPosition();
+            if (newPosition != position) {
+                throw new IllegalStateException("the cursor position changed during the call to"
+                        + "newEntityFromCursorLocked, from " + position + " to " + newPosition);
+            }
+        } finally {
+            mDb.endTransaction();
+        }
+    }
+
+    /**
+     * Checks if there are more Entities accessible via this iterator. This may not be called
+     * if the iterator is already closed.
+     * @return true if the call to next() will return an Entity.
+     */
+    public boolean hasNext() {
+        if (mIsClosed) {
+            throw new IllegalStateException("calling hasNext() when the iterator is closed");
+        }
+        fillEntityIfAvailable();
+        return mNextEntity != null;
+    }
+
+    /**
+     * Returns the next Entity that is accessible via this iterator. This may not be called
+     * if the iterator is already closed.
+     * @return the next Entity that is accessible via this iterator
+     */
+    public Entity next() {
+        if (mIsClosed) {
+            throw new IllegalStateException("calling next() when the iterator is closed");
+        }
+        if (!hasNext()) {
+            throw new IllegalStateException("you may only call next() if hasNext() is true");
+        }
+
+        try {
+            return mNextEntity;
+        } finally {
+            mNextEntity = null;
+        }
+    }
+
+    /**
+     * Closes this iterator making it invalid. If is invalid for the user to call any public
+     * method on the iterator once it has been closed.
+     */
+    public void close() {
+        if (mIsClosed) {
+            throw new IllegalStateException("closing when already closed");
+        }
+        mIsClosed = true;
+        mEntityCursor.close();
+    }
+
+    /**
+     * Returns a new Entity from the current cursor position. This is called from within a
+     * database transaction. If a new entity cannot be created from this cursor position (e.g.
+     * if the row that is referred to no longer exists) then this may return null. The cursor
+     * is guaranteed to be pointing to a valid row when this call is made. The implementation
+     * of newEntityFromCursorLocked is not allowed to change the position of the cursor.
+     * @param cursor from where to read the data for the Entity
+     * @return an Entity that corresponds to the current cursor position or null
+     */
+    public abstract Entity newEntityFromCursorLocked(Cursor cursor);
+}
diff --git a/core/java/android/content/AbstractSyncableContentProvider.java b/core/java/android/content/AbstractSyncableContentProvider.java
index 249d9ba..db73dd5 100644
--- a/core/java/android/content/AbstractSyncableContentProvider.java
+++ b/core/java/android/content/AbstractSyncableContentProvider.java
@@ -4,8 +4,9 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.Cursor;
 import android.net.Uri;
-import android.accounts.AccountMonitor;
-import android.accounts.AccountMonitorListener;
+import android.accounts.OnAccountsUpdatedListener;
+import android.accounts.Account;
+import android.accounts.AccountManager;
 import android.provider.SyncConstValue;
 import android.util.Config;
 import android.util.Log;
@@ -14,9 +15,12 @@
 
 import java.util.Collections;
 import java.util.Map;
-import java.util.HashMap;
 import java.util.Vector;
 import java.util.ArrayList;
+import java.util.Set;
+import java.util.HashSet;
+
+import com.google.android.collect.Maps;
 
 /**
  * A specialization of the ContentProvider that centralizes functionality
@@ -32,26 +36,30 @@
     private final String mDatabaseName;
     private final int mDatabaseVersion;
     private final Uri mContentUri;
-    private AccountMonitor mAccountMonitor;
 
     /** the account set in the last call to onSyncStart() */
-    private String mSyncingAccount;
+    private Account mSyncingAccount;
 
     private SyncStateContentProviderHelper mSyncState = null;
 
-    private static final String[] sAccountProjection = new String[] {SyncConstValue._SYNC_ACCOUNT};
+    private static final String[] sAccountProjection =
+            new String[] {SyncConstValue._SYNC_ACCOUNT, SyncConstValue._SYNC_ACCOUNT_TYPE};
 
     private boolean mIsTemporary;
 
     private AbstractTableMerger mCurrentMerger = null;
     private boolean mIsMergeCancelled = false;
 
-    private static final String SYNC_ACCOUNT_WHERE_CLAUSE = SyncConstValue._SYNC_ACCOUNT + "=?";
+    private static final String SYNC_ACCOUNT_WHERE_CLAUSE =
+            SyncConstValue._SYNC_ACCOUNT + "=? AND " + SyncConstValue._SYNC_ACCOUNT_TYPE + "=?";
 
     protected boolean isTemporary() {
         return mIsTemporary;
     }
 
+    private final ThreadLocal<Boolean> mApplyingBatch = new ThreadLocal<Boolean>();
+    private final ThreadLocal<Set<Uri>> mPendingBatchNotifications = new ThreadLocal<Set<Uri>>();
+
     /**
      * Indicates whether or not this ContentProvider contains a full
      * set of data or just diffs. This knowledge comes in handy when
@@ -133,7 +141,8 @@
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
             if (!upgradeDatabase(db, oldVersion, newVersion)) {
                 mSyncState.discardSyncData(db, null /* all accounts */);
-                getContext().getContentResolver().startSync(mContentUri, new Bundle());
+                ContentResolver.requestSync(null /* all accounts */,
+                        mContentUri.getAuthority(), new Bundle());
             }
         }
 
@@ -150,19 +159,19 @@
         mOpenHelper = new AbstractSyncableContentProvider.DatabaseHelper(getContext(),
                 mDatabaseName);
         mSyncState = new SyncStateContentProviderHelper(mOpenHelper);
-
-        AccountMonitorListener listener = new AccountMonitorListener() {
-            public void onAccountsUpdated(String[] accounts) {
-                // Some providers override onAccountsChanged(); give them a database to work with.
-                mDb = mOpenHelper.getWritableDatabase();
-                onAccountsChanged(accounts);
-                TempProviderSyncAdapter syncAdapter = (TempProviderSyncAdapter)getSyncAdapter();
-                if (syncAdapter != null) {
-                    syncAdapter.onAccountsChanged(accounts);
-                }
-            }
-        };
-        mAccountMonitor = new AccountMonitor(getContext(), listener);
+        AccountManager.get(getContext()).addOnAccountsUpdatedListener(
+                new OnAccountsUpdatedListener() {
+                    public void onAccountsUpdated(Account[] accounts) {
+                        // Some providers override onAccountsChanged(); give them a database to
+                        // work with.
+                        mDb = mOpenHelper.getWritableDatabase();
+                        onAccountsChanged(accounts);
+                        TempProviderSyncAdapter syncAdapter = getTempProviderSyncAdapter();
+                        if (syncAdapter != null) {
+                            syncAdapter.onAccountsChanged(accounts);
+                        }
+                    }
+                }, null /* handler */, true /* updateImmediately */);
 
         return true;
     }
@@ -236,147 +245,117 @@
         return Collections.emptyList();
     }
 
-    /**
-     * <p>
-     * Call mOpenHelper.getWritableDatabase() and mDb.beginTransaction().
-     * {@link #endTransaction} MUST be called after calling this method.
-     * Those methods should be used like this:
-     * </p>
-     *
-     * <pre class="prettyprint">
-     * boolean successful = false;
-     * beginTransaction();
-     * try {
-     *     // Do something related to mDb
-     *     successful = true;
-     *     return ret;
-     * } finally {
-     *     endTransaction(successful);
-     * }
-     * </pre>
-     *
-     * @hide This method is dangerous from the view of database manipulation, though using
-     * this makes batch insertion/update/delete much faster.
-     */
-    public final void beginTransaction() {
+    @Override
+    public final int update(final Uri url, final ContentValues values,
+            final String selection, final String[] selectionArgs) {
         mDb = mOpenHelper.getWritableDatabase();
-        mDb.beginTransaction();
-    }
-
-    /**
-     * <p>
-     * Call mDb.endTransaction(). If successful is true, try to call
-     * mDb.setTransactionSuccessful() before calling mDb.endTransaction().
-     * This method MUST be used with {@link #beginTransaction()}.
-     * </p>
-     *
-     * @hide This method is dangerous from the view of database manipulation, though using
-     * this makes batch insertion/update/delete much faster.
-     */
-    public final void endTransaction(boolean successful) {
+        final boolean notApplyingBatch = !applyingBatch();
+        if (notApplyingBatch) {
+            mDb.beginTransaction();
+        }
         try {
-            if (successful) {
-                // setTransactionSuccessful() must be called just once during opening the
-                // transaction.
+            if (isTemporary() && mSyncState.matches(url)) {
+                int numRows = mSyncState.asContentProvider().update(
+                        url, values, selection, selectionArgs);
+                if (notApplyingBatch) {
+                    mDb.setTransactionSuccessful();
+                }
+                return numRows;
+            }
+
+            int result = updateInternal(url, values, selection, selectionArgs);
+            if (notApplyingBatch) {
                 mDb.setTransactionSuccessful();
             }
-        } finally {
-            mDb.endTransaction();
-        }
-    }
-
-    @Override
-    public final int update(final Uri uri, final ContentValues values,
-            final String selection, final String[] selectionArgs) {
-        boolean successful = false;
-        beginTransaction();
-        try {
-            int ret = nonTransactionalUpdate(uri, values, selection, selectionArgs);
-            successful = true;
-            return  ret;
-        } finally {
-            endTransaction(successful);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public final int nonTransactionalUpdate(final Uri uri, final ContentValues values,
-            final String selection, final String[] selectionArgs) {
-        if (isTemporary() && mSyncState.matches(uri)) {
-            int numRows = mSyncState.asContentProvider().update(
-                    uri, values, selection, selectionArgs);
-            return numRows;
-        }
-
-        int result = updateInternal(uri, values, selection, selectionArgs);
-        if (!isTemporary() && result > 0) {
-            getContext().getContentResolver().notifyChange(uri, null /* observer */,
-                    changeRequiresLocalSync(uri));
-        }
-
-        return result;
-    }
-
-    @Override
-    public final int delete(final Uri uri, final String selection,
-            final String[] selectionArgs) {
-        boolean successful = false;
-        beginTransaction();
-        try {
-            int ret = nonTransactionalDelete(uri, selection, selectionArgs);
-            successful = true;
-            return ret;
-        } finally {
-            endTransaction(successful);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public final int nonTransactionalDelete(final Uri uri, final String selection,
-            final String[] selectionArgs) {
-        if (isTemporary() && mSyncState.matches(uri)) {
-            int numRows = mSyncState.asContentProvider().delete(uri, selection, selectionArgs);
-            return numRows;
-        }
-        int result = deleteInternal(uri, selection, selectionArgs);
-        if (!isTemporary() && result > 0) {
-            getContext().getContentResolver().notifyChange(uri, null /* observer */,
-                    changeRequiresLocalSync(uri));
-        }
-        return result;
-    }
-
-    @Override
-    public final Uri insert(final Uri uri, final ContentValues values) {
-        boolean successful = false;
-        beginTransaction();
-        try {
-            Uri ret = nonTransactionalInsert(uri, values);
-            successful = true;
-            return ret;
-        } finally {
-            endTransaction(successful);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public final Uri nonTransactionalInsert(final Uri uri, final ContentValues values) {
-        if (isTemporary() && mSyncState.matches(uri)) {
-            Uri result = mSyncState.asContentProvider().insert(uri, values);
+            if (!isTemporary() && result > 0) {
+                if (notApplyingBatch) {
+                    getContext().getContentResolver().notifyChange(url, null /* observer */,
+                            changeRequiresLocalSync(url));
+                } else {
+                    mPendingBatchNotifications.get().add(url);
+                }
+            }
             return result;
+        } finally {
+            if (notApplyingBatch) {
+                mDb.endTransaction();
+            }
         }
-        Uri result = insertInternal(uri, values);
-        if (!isTemporary() && result != null) {
-            getContext().getContentResolver().notifyChange(uri, null /* observer */,
-                    changeRequiresLocalSync(uri));
+    }
+
+    @Override
+    public final int delete(final Uri url, final String selection,
+            final String[] selectionArgs) {
+        mDb = mOpenHelper.getWritableDatabase();
+        final boolean notApplyingBatch = !applyingBatch();
+        if (notApplyingBatch) {
+            mDb.beginTransaction();
         }
-        return result;
+        try {
+            if (isTemporary() && mSyncState.matches(url)) {
+                int numRows = mSyncState.asContentProvider().delete(url, selection, selectionArgs);
+                if (notApplyingBatch) {
+                    mDb.setTransactionSuccessful();
+                }
+                return numRows;
+            }
+            int result = deleteInternal(url, selection, selectionArgs);
+            if (notApplyingBatch) {
+                mDb.setTransactionSuccessful();
+            }
+            if (!isTemporary() && result > 0) {
+                if (notApplyingBatch) {
+                    getContext().getContentResolver().notifyChange(url, null /* observer */,
+                            changeRequiresLocalSync(url));
+                } else {
+                    mPendingBatchNotifications.get().add(url);
+                }
+            }
+            return result;
+        } finally {
+            if (notApplyingBatch) {
+                mDb.endTransaction();
+            }
+        }
+    }
+
+    private boolean applyingBatch() {
+        return mApplyingBatch.get() != null && mApplyingBatch.get();
+    }
+
+    @Override
+    public final Uri insert(final Uri url, final ContentValues values) {
+        mDb = mOpenHelper.getWritableDatabase();
+        final boolean notApplyingBatch = !applyingBatch();
+        if (notApplyingBatch) {
+            mDb.beginTransaction();
+        }
+        try {
+            if (isTemporary() && mSyncState.matches(url)) {
+                Uri result = mSyncState.asContentProvider().insert(url, values);
+                if (notApplyingBatch) {
+                    mDb.setTransactionSuccessful();
+                }
+                return result;
+            }
+            Uri result = insertInternal(url, values);
+            if (notApplyingBatch) {
+                mDb.setTransactionSuccessful();
+            }
+            if (!isTemporary() && result != null) {
+                if (notApplyingBatch) {
+                    getContext().getContentResolver().notifyChange(url, null /* observer */,
+                            changeRequiresLocalSync(url));
+                } else {
+                    mPendingBatchNotifications.get().add(url);
+                }
+            }
+            return result;
+        } finally {
+            if (notApplyingBatch) {
+                mDb.endTransaction();
+            }
+        }
     }
 
     @Override
@@ -411,6 +390,92 @@
     }
 
     /**
+     * <p>
+     * Start batch transaction. {@link #endTransaction} MUST be called after 
+     * calling this method. Those methods should be used like this:
+     * </p>
+     *
+     * <pre class="prettyprint">
+     * boolean successful = false;
+     * beginBatch()
+     * try {
+     *     // Do something related to mDb
+     *     successful = true;
+     *     return ret;
+     * } finally {
+     *     endBatch(successful);
+     * }
+     * </pre>
+     *
+     * @hide This method should be used only when {@link ContentProvider#applyBatch} is not enough and must be
+     * used with {@link #endBatch}.
+     * e.g. If returned value has to be used during one transaction, this method might be useful.
+     */
+    public final void beginBatch() {
+        // initialize if this is the first time this thread has applied a batch
+        if (mApplyingBatch.get() == null) {
+            mApplyingBatch.set(false);
+            mPendingBatchNotifications.set(new HashSet<Uri>());
+        }
+
+        if (applyingBatch()) {
+            throw new IllegalStateException(
+                    "applyBatch is not reentrant but mApplyingBatch is already set");
+        }
+        SQLiteDatabase db = getDatabase();
+        db.beginTransaction();
+        boolean successful = false;
+        try {
+            mApplyingBatch.set(true);
+            successful = true;
+        } finally {
+            if (!successful) {
+                // Something unexpected happened. We must call endTransaction() at least.
+                db.endTransaction();
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Finish batch transaction. If "successful" is true, try to call
+     * mDb.setTransactionSuccessful() before calling mDb.endTransaction().
+     * This method MUST be used with {@link #beginBatch()}.
+     * </p>
+     *
+     * @hide This method must be used with {@link #beginTransaction}
+     */
+    public final void endBatch(boolean successful) {
+        try {
+            if (successful) {
+                // setTransactionSuccessful() must be called just once during opening the
+                // transaction.
+                mDb.setTransactionSuccessful();
+            }
+        } finally {
+            mApplyingBatch.set(false);
+            getDatabase().endTransaction();
+            for (Uri url : mPendingBatchNotifications.get()) {
+                getContext().getContentResolver().notifyChange(url, null /* observer */,
+                        changeRequiresLocalSync(url));
+            }
+        }
+    }
+
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+            throws OperationApplicationException {
+        boolean successful = false;
+        beginBatch();
+        try {
+            ContentProviderResult[] results = super.applyBatch(operations);
+            successful = true;
+            return results;
+        } finally {
+            endBatch(successful);
+        }
+    }
+
+    /**
      * Check if changes to this URI can be syncable changes.
      * @param uri the URI of the resource that was changed
      * @return true if changes to this URI can be syncable changes, false otherwise
@@ -437,8 +502,8 @@
      * @param context the sync context for the operation
      * @param account
      */
-    public void onSyncStart(SyncContext context, String account) {
-        if (TextUtils.isEmpty(account)) {
+    public void onSyncStart(SyncContext context, Account account) {
+        if (account == null) {
             throw new IllegalArgumentException("you passed in an empty account");
         }
         mSyncingAccount = account;
@@ -457,7 +522,7 @@
      * The account of the most recent call to onSyncStart()
      * @return the account
      */
-    public String getSyncingAccount() {
+    public Account getSyncingAccount() {
         return mSyncingAccount;
     }
 
@@ -568,12 +633,11 @@
      * Make sure that there are no entries for accounts that no longer exist
      * @param accountsArray the array of currently-existing accounts
      */
-    protected void onAccountsChanged(String[] accountsArray) {
-        Map<String, Boolean> accounts = new HashMap<String, Boolean>();
-        for (String account : accountsArray) {
+    protected void onAccountsChanged(Account[] accountsArray) {
+        Map<Account, Boolean> accounts = Maps.newHashMap();
+        for (Account account : accountsArray) {
             accounts.put(account, false);
         }
-        accounts.put(SyncConstValue.NON_SYNCABLE_ACCOUNT, false);
 
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         Map<String, String> tableMap = db.getSyncedTables();
@@ -585,8 +649,7 @@
         try {
             mSyncState.onAccountsChanged(accountsArray);
             for (String table : tables) {
-                deleteRowsForRemovedAccounts(accounts, table,
-                        SyncConstValue._SYNC_ACCOUNT);
+                deleteRowsForRemovedAccounts(accounts, table);
             }
             db.setTransactionSuccessful();
         } finally {
@@ -601,23 +664,23 @@
      *
      * @param accounts a map of existing accounts
      * @param table the table to delete from
-     * @param accountColumnName the name of the column that is expected
-     * to hold the account.
      */
-    protected void deleteRowsForRemovedAccounts(Map<String, Boolean> accounts,
-            String table, String accountColumnName) {
+    protected void deleteRowsForRemovedAccounts(Map<Account, Boolean> accounts, String table) {
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         Cursor c = db.query(table, sAccountProjection, null, null,
-                accountColumnName, null, null);
+                "_sync_account, _sync_account_type", null, null);
         try {
             while (c.moveToNext()) {
-                String account = c.getString(0);
-                if (TextUtils.isEmpty(account)) {
+                String accountName = c.getString(0);
+                String accountType = c.getString(1);
+                if (TextUtils.isEmpty(accountName)) {
                     continue;
                 }
+                Account account = new Account(accountName, accountType);
                 if (!accounts.containsKey(account)) {
                     int numDeleted;
-                    numDeleted = db.delete(table, accountColumnName + "=?", new String[]{account});
+                    numDeleted = db.delete(table, "_sync_account=? AND _sync_account_type=?",
+                            new String[]{account.mName, account.mType});
                     if (Config.LOGV) {
                         Log.v(TAG, "deleted " + numDeleted
                                 + " records from table " + table
@@ -634,7 +697,7 @@
      * Called when the sync system determines that this provider should no longer
      * contain records for the specified account.
      */
-    public void wipeAccount(String account) {
+    public void wipeAccount(Account account) {
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         Map<String, String> tableMap = db.getSyncedTables();
         ArrayList<String> tables = new ArrayList<String>();
@@ -649,7 +712,8 @@
 
             // remove the data in the synced tables
             for (String table : tables) {
-                db.delete(table, SYNC_ACCOUNT_WHERE_CLAUSE, new String[]{account});
+                db.delete(table, SYNC_ACCOUNT_WHERE_CLAUSE,
+                        new String[]{account.mName, account.mType});
             }
             db.setTransactionSuccessful();
         } finally {
@@ -660,14 +724,14 @@
     /**
      * Retrieves the SyncData bytes for the given account. The byte array returned may be null.
      */
-    public byte[] readSyncDataBytes(String account) {
+    public byte[] readSyncDataBytes(Account account) {
         return mSyncState.readSyncDataBytes(mOpenHelper.getReadableDatabase(), account);
     }
 
     /**
      * Sets the SyncData bytes for the given account. The byte array may be null.
      */
-    public void writeSyncDataBytes(String account, byte[] data) {
+    public void writeSyncDataBytes(Account account, byte[] data) {
         mSyncState.writeSyncDataBytes(mOpenHelper.getWritableDatabase(), account, data);
     }
 }
diff --git a/core/java/android/content/AbstractTableMerger.java b/core/java/android/content/AbstractTableMerger.java
index 9c760d9..3266c07 100644
--- a/core/java/android/content/AbstractTableMerger.java
+++ b/core/java/android/content/AbstractTableMerger.java
@@ -25,6 +25,7 @@
 import static android.provider.SyncConstValue.*;
 import android.text.TextUtils;
 import android.util.Log;
+import android.accounts.Account;
 
 /**
  * @hide
@@ -55,15 +56,17 @@
 
     private volatile boolean mIsMergeCancelled;
 
-    private static final String SELECT_MARKED = _SYNC_MARK + "> 0 and " + _SYNC_ACCOUNT + "=?";
+    private static final String SELECT_MARKED = _SYNC_MARK + "> 0 and "
+            + _SYNC_ACCOUNT + "=? and " + _SYNC_ACCOUNT_TYPE + "=?";
 
     private static final String SELECT_BY_SYNC_ID_AND_ACCOUNT =
-            _SYNC_ID +"=? and " + _SYNC_ACCOUNT + "=?";
+            _SYNC_ID +"=? and " + _SYNC_ACCOUNT + "=? and " + _SYNC_ACCOUNT_TYPE + "=?";
     private static final String SELECT_BY_ID = BaseColumns._ID +"=?";
 
     private static final String SELECT_UNSYNCED =
-            "(" + _SYNC_ACCOUNT + " IS NULL OR " + _SYNC_ACCOUNT + "=?) AND "
-            + "(" + _SYNC_ID + " IS NULL OR (" + _SYNC_DIRTY + " > 0 AND "
+            "(" + _SYNC_ACCOUNT + " IS NULL OR ("
+                + _SYNC_ACCOUNT + "=? and " + _SYNC_ACCOUNT_TYPE + "=?)) and "
+            + "(" + _SYNC_ID + " IS NULL OR (" + _SYNC_DIRTY + " > 0 and "
                                               + _SYNC_VERSION + " IS NOT NULL))";
 
     public AbstractTableMerger(SQLiteDatabase database,
@@ -134,7 +137,7 @@
      * construct a temporary instance to hold them.
      */
     public void merge(final SyncContext context,
-            final String account,
+            final Account account,
             final SyncableContentProvider serverDiffs,
             TempProviderSyncResult result,
             SyncResult syncResult, SyncableContentProvider temporaryInstanceFactory) {
@@ -157,7 +160,7 @@
      * @hide this is public for testing purposes only
      */
     public void mergeServerDiffs(SyncContext context,
-            String account, SyncableContentProvider serverDiffs, SyncResult syncResult) {
+            Account account, SyncableContentProvider serverDiffs, SyncResult syncResult) {
         boolean diffsArePartial = serverDiffs.getContainsDiffs();
         // mark the current rows so that we can distinguish these from new
         // inserts that occur during the merge
@@ -166,339 +169,337 @@
             mDb.update(mDeletedTable, mSyncMarkValues, null, null);
         }
 
-        // load the local database entries, so we can merge them with the server
-        final String[] accountSelectionArgs = new String[]{account};
-        Cursor localCursor = mDb.query(mTable, syncDirtyProjection,
-                SELECT_MARKED, accountSelectionArgs, null, null,
-                mTable + "." + _SYNC_ID);
-        Cursor deletedCursor;
-        if (mDeletedTable != null) {
-            deletedCursor = mDb.query(mDeletedTable, syncIdAndVersionProjection,
+        Cursor localCursor = null;
+        Cursor deletedCursor = null;
+        Cursor diffsCursor = null;
+        try {
+            // load the local database entries, so we can merge them with the server
+            final String[] accountSelectionArgs = new String[]{account.mName, account.mType};
+            localCursor = mDb.query(mTable, syncDirtyProjection,
                     SELECT_MARKED, accountSelectionArgs, null, null,
-                    mDeletedTable + "." + _SYNC_ID);
-        } else {
-            deletedCursor =
-                    mDb.rawQuery("select 'a' as _sync_id, 'b' as _sync_version limit 0", null);
-        }
-
-        // Apply updates and insertions from the server
-        Cursor diffsCursor = serverDiffs.query(mTableURL,
-                null, null, null, mTable + "." + _SYNC_ID);
-        int deletedSyncIDColumn = deletedCursor.getColumnIndexOrThrow(_SYNC_ID);
-        int deletedSyncVersionColumn = deletedCursor.getColumnIndexOrThrow(_SYNC_VERSION);
-        int serverSyncIDColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_ID);
-        int serverSyncVersionColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_VERSION);
-        int serverSyncLocalIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_LOCAL_ID);
-
-        String lastSyncId = null;
-        int diffsCount = 0;
-        int localCount = 0;
-        localCursor.moveToFirst();
-        deletedCursor.moveToFirst();
-        while (diffsCursor.moveToNext()) {
-            if (mIsMergeCancelled) {
-                localCursor.close();
-                deletedCursor.close();
-                diffsCursor.close();
-                return;
-            }
-            mDb.yieldIfContended();
-            String serverSyncId = diffsCursor.getString(serverSyncIDColumn);
-            String serverSyncVersion = diffsCursor.getString(serverSyncVersionColumn);
-            long localRowId = 0;
-            String localSyncVersion = null;
-
-            diffsCount++;
-            context.setStatusText("Processing " + diffsCount + "/"
-                    + diffsCursor.getCount());
-            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "processing server entry " +
-                    diffsCount + ", " + serverSyncId);
-
-            if (TRACE) {
-                if (diffsCount == 10) {
-                    Debug.startMethodTracing("atmtrace");
-                }
-                if (diffsCount == 20) {
-                    Debug.stopMethodTracing();
-                }
+                    mTable + "." + _SYNC_ID);
+            if (mDeletedTable != null) {
+                deletedCursor = mDb.query(mDeletedTable, syncIdAndVersionProjection,
+                        SELECT_MARKED, accountSelectionArgs, null, null,
+                        mDeletedTable + "." + _SYNC_ID);
+            } else {
+                deletedCursor =
+                        mDb.rawQuery("select 'a' as _sync_id, 'b' as _sync_version limit 0", null);
             }
 
-            boolean conflict = false;
-            boolean update = false;
-            boolean insert = false;
+            // Apply updates and insertions from the server
+            diffsCursor = serverDiffs.query(mTableURL,
+                    null, null, null, mTable + "." + _SYNC_ID);
+            int deletedSyncIDColumn = deletedCursor.getColumnIndexOrThrow(_SYNC_ID);
+            int deletedSyncVersionColumn = deletedCursor.getColumnIndexOrThrow(_SYNC_VERSION);
+            int serverSyncIDColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_ID);
+            int serverSyncVersionColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_VERSION);
+            int serverSyncLocalIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_LOCAL_ID);
+
+            String lastSyncId = null;
+            int diffsCount = 0;
+            int localCount = 0;
+            localCursor.moveToFirst();
+            deletedCursor.moveToFirst();
+            while (diffsCursor.moveToNext()) {
+                if (mIsMergeCancelled) {
+                    return;
+                }
+                mDb.yieldIfContended();
+                String serverSyncId = diffsCursor.getString(serverSyncIDColumn);
+                String serverSyncVersion = diffsCursor.getString(serverSyncVersionColumn);
+                long localRowId = 0;
+                String localSyncVersion = null;
+
+                diffsCount++;
+                context.setStatusText("Processing " + diffsCount + "/"
+                        + diffsCursor.getCount());
+                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "processing server entry " +
+                        diffsCount + ", " + serverSyncId);
+
+                if (TRACE) {
+                    if (diffsCount == 10) {
+                        Debug.startMethodTracing("atmtrace");
+                    }
+                    if (diffsCount == 20) {
+                        Debug.stopMethodTracing();
+                    }
+                }
+
+                boolean conflict = false;
+                boolean update = false;
+                boolean insert = false;
+
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "found event with serverSyncID " + serverSyncId);
+                }
+                if (TextUtils.isEmpty(serverSyncId)) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.e(TAG, "server entry doesn't have a serverSyncID");
+                    }
+                    continue;
+                }
+
+                // It is possible that the sync adapter wrote the same record multiple times,
+                // e.g. if the same record came via multiple feeds. If this happens just ignore
+                // the duplicate records.
+                if (serverSyncId.equals(lastSyncId)) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "skipping record with duplicate remote server id " + lastSyncId);
+                    }
+                    continue;
+                }
+                lastSyncId = serverSyncId;
+
+                String localSyncID = null;
+                boolean localSyncDirty = false;
+
+                while (!localCursor.isAfterLast()) {
+                    if (mIsMergeCancelled) {
+                        return;
+                    }
+                    localCount++;
+                    localSyncID = localCursor.getString(2);
+
+                    // If the local record doesn't have a _sync_id then
+                    // it is new. Ignore it for now, we will send an insert
+                    // the the server later.
+                    if (TextUtils.isEmpty(localSyncID)) {
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "local record " +
+                                    localCursor.getLong(1) +
+                                    " has no _sync_id, ignoring");
+                        }
+                        localCursor.moveToNext();
+                        localSyncID = null;
+                        continue;
+                    }
+
+                    int comp = serverSyncId.compareTo(localSyncID);
+
+                    // the local DB has a record that the server doesn't have
+                    if (comp > 0) {
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "local record " +
+                                    localCursor.getLong(1) +
+                                    " has _sync_id " + localSyncID +
+                                    " that is < server _sync_id " + serverSyncId);
+                        }
+                        if (diffsArePartial) {
+                            localCursor.moveToNext();
+                        } else {
+                            deleteRow(localCursor);
+                            if (mDeletedTable != null) {
+                                mDb.delete(mDeletedTable, _SYNC_ID +"=?", new String[] {localSyncID});
+                            }
+                            syncResult.stats.numDeletes++;
+                            mDb.yieldIfContended();
+                        }
+                        localSyncID = null;
+                        continue;
+                    }
+
+                    // the server has a record that the local DB doesn't have
+                    if (comp < 0) {
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "local record " +
+                                    localCursor.getLong(1) +
+                                    " has _sync_id " + localSyncID +
+                                    " that is > server _sync_id " + serverSyncId);
+                        }
+                        localSyncID = null;
+                    }
+
+                    // the server and the local DB both have this record
+                    if (comp == 0) {
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "local record " +
+                                    localCursor.getLong(1) +
+                                    " has _sync_id " + localSyncID +
+                                    " that matches the server _sync_id");
+                        }
+                        localSyncDirty = localCursor.getInt(0) != 0;
+                        localRowId = localCursor.getLong(1);
+                        localSyncVersion = localCursor.getString(3);
+                        localCursor.moveToNext();
+                    }
+
+                    break;
+                }
+
+                // If this record is in the deleted table then update the server version
+                // in the deleted table, if necessary, and then ignore it here.
+                // We will send a deletion indication to the server down a
+                // little further.
+                if (findInCursor(deletedCursor, deletedSyncIDColumn, serverSyncId)) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "remote record " + serverSyncId + " is in the deleted table");
+                    }
+                    final String deletedSyncVersion = deletedCursor.getString(deletedSyncVersionColumn);
+                    if (!TextUtils.equals(deletedSyncVersion, serverSyncVersion)) {
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "setting version of deleted record " + serverSyncId + " to "
+                                    + serverSyncVersion);
+                        }
+                        ContentValues values = new ContentValues();
+                        values.put(_SYNC_VERSION, serverSyncVersion);
+                        mDb.update(mDeletedTable, values, "_sync_id=?", new String[]{serverSyncId});
+                    }
+                    continue;
+                }
+
+                // If the _sync_local_id is present in the diffsCursor
+                // then this record corresponds to a local record that was just
+                // inserted into the server and the _sync_local_id is the row id
+                // of the local record. Set these fields so that the next check
+                // treats this record as an update, which will allow the
+                // merger to update the record with the server's sync id
+                if (!diffsCursor.isNull(serverSyncLocalIdColumn)) {
+                    localRowId = diffsCursor.getLong(serverSyncLocalIdColumn);
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "the remote record with sync id " + serverSyncId
+                                + " has a local sync id, " + localRowId);
+                    }
+                    localSyncID = serverSyncId;
+                    localSyncDirty = false;
+                    localSyncVersion = null;
+                }
+
+                if (!TextUtils.isEmpty(localSyncID)) {
+                    // An existing server item has changed
+                    // If serverSyncVersion is null, there is no edit URL;
+                    // server won't let this change be written.
+                    // Just hold onto it, I guess, in case the server permissions
+                    // change later.
+                    if (serverSyncVersion != null) {
+                        boolean recordChanged = (localSyncVersion == null) ||
+                                !serverSyncVersion.equals(localSyncVersion);
+                        if (recordChanged) {
+                            if (localSyncDirty) {
+                                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                                    Log.v(TAG, "remote record " + serverSyncId
+                                            + " conflicts with local _sync_id " + localSyncID
+                                            + ", local _id " + localRowId);
+                                }
+                                conflict = true;
+                            } else {
+                                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                                    Log.v(TAG,
+                                            "remote record " +
+                                                    serverSyncId +
+                                            " updates local _sync_id " +
+                                            localSyncID + ", local _id " +
+                                            localRowId);
+                                }
+                                update = true;
+                            }
+                        }
+                    }
+                } else {
+                    // the local db doesn't know about this record so add it
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "remote record " + serverSyncId + " is new, inserting");
+                    }
+                    insert = true;
+                }
+
+                if (update) {
+                    updateRow(localRowId, serverDiffs, diffsCursor);
+                    syncResult.stats.numUpdates++;
+                } else if (conflict) {
+                    resolveRow(localRowId, serverSyncId, serverDiffs, diffsCursor);
+                    syncResult.stats.numUpdates++;
+                } else if (insert) {
+                    insertRow(serverDiffs, diffsCursor);
+                    syncResult.stats.numInserts++;
+                }
+            }
 
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Log.v(TAG, "found event with serverSyncID " + serverSyncId);
-            }
-            if (TextUtils.isEmpty(serverSyncId)) {
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.e(TAG, "server entry doesn't have a serverSyncID");
-                }
-                continue;
+                Log.v(TAG, "processed " + diffsCount + " server entries");
             }
 
-            // It is possible that the sync adapter wrote the same record multiple times,
-            // e.g. if the same record came via multiple feeds. If this happens just ignore
-            // the duplicate records.
-            if (serverSyncId.equals(lastSyncId)) {
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "skipping record with duplicate remote server id " + lastSyncId);
-                }
-                continue;
-            }
-            lastSyncId = serverSyncId;
-
-            String localSyncID = null;
-            boolean localSyncDirty = false;
-
-            while (!localCursor.isAfterLast()) {
-                if (mIsMergeCancelled) {
-                    localCursor.deactivate();
-                    deletedCursor.deactivate();
-                    diffsCursor.deactivate();
-                    return;
-                }
-                localCount++;
-                localSyncID = localCursor.getString(2);
-
-                // If the local record doesn't have a _sync_id then
-                // it is new. Ignore it for now, we will send an insert
-                // the the server later.
-                if (TextUtils.isEmpty(localSyncID)) {
+            // If tombstones aren't in use delete any remaining local rows that
+            // don't have corresponding server rows. Keep the rows that don't
+            // have a sync id since those were created locally and haven't been
+            // synced to the server yet.
+            if (!diffsArePartial) {
+                while (!localCursor.isAfterLast() && !TextUtils.isEmpty(localCursor.getString(2))) {
+                    if (mIsMergeCancelled) {
+                        return;
+                    }
+                    localCount++;
+                    final String localSyncId = localCursor.getString(2);
                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "local record " +
-                                localCursor.getLong(1) +
-                                " has no _sync_id, ignoring");
+                        Log.v(TAG,
+                                "deleting local record " +
+                                        localCursor.getLong(1) +
+                                        " _sync_id " + localSyncId);
                     }
-                    localCursor.moveToNext();
-                    localSyncID = null;
-                    continue;
+                    deleteRow(localCursor);
+                    if (mDeletedTable != null) {
+                        mDb.delete(mDeletedTable, _SYNC_ID + "=?", new String[] {localSyncId});
+                    }
+                    syncResult.stats.numDeletes++;
+                    mDb.yieldIfContended();
                 }
-
-                int comp = serverSyncId.compareTo(localSyncID);
-
-                // the local DB has a record that the server doesn't have
-                if (comp > 0) {
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "local record " +
-                                localCursor.getLong(1) +
-                                " has _sync_id " + localSyncID +
-                                " that is < server _sync_id " + serverSyncId);
-                    }
-                    if (diffsArePartial) {
-                        localCursor.moveToNext();
-                    } else {
-                        deleteRow(localCursor);
-                        if (mDeletedTable != null) {
-                            mDb.delete(mDeletedTable, _SYNC_ID +"=?", new String[] {localSyncID});
-                        }
-                        syncResult.stats.numDeletes++;
-                        mDb.yieldIfContended();
-                    }
-                    localSyncID = null;
-                    continue;
-                }
-
-                // the server has a record that the local DB doesn't have
-                if (comp < 0) {
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "local record " +
-                                localCursor.getLong(1) +
-                                " has _sync_id " + localSyncID +
-                                " that is > server _sync_id " + serverSyncId);
-                    }
-                    localSyncID = null;
-                }
-
-                // the server and the local DB both have this record
-                if (comp == 0) {
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "local record " +
-                                localCursor.getLong(1) +
-                                " has _sync_id " + localSyncID +
-                                " that matches the server _sync_id");
-                    }
-                    localSyncDirty = localCursor.getInt(0) != 0;
-                    localRowId = localCursor.getLong(1);
-                    localSyncVersion = localCursor.getString(3);
-                    localCursor.moveToNext();
-                }
-
-                break;
             }
-
-            // If this record is in the deleted table then update the server version
-            // in the deleted table, if necessary, and then ignore it here.
-            // We will send a deletion indication to the server down a
-            // little further.
-            if (findInCursor(deletedCursor, deletedSyncIDColumn, serverSyncId)) {
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "remote record " + serverSyncId + " is in the deleted table");
-                }
-                final String deletedSyncVersion = deletedCursor.getString(deletedSyncVersionColumn);
-                if (!TextUtils.equals(deletedSyncVersion, serverSyncVersion)) {
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "setting version of deleted record " + serverSyncId + " to "
-                                + serverSyncVersion);
-                    }
-                    ContentValues values = new ContentValues();
-                    values.put(_SYNC_VERSION, serverSyncVersion);
-                    mDb.update(mDeletedTable, values, "_sync_id=?", new String[]{serverSyncId});
-                }
-                continue;
-            }
-
-            // If the _sync_local_id is present in the diffsCursor
-            // then this record corresponds to a local record that was just
-            // inserted into the server and the _sync_local_id is the row id
-            // of the local record. Set these fields so that the next check
-            // treats this record as an update, which will allow the
-            // merger to update the record with the server's sync id
-            if (!diffsCursor.isNull(serverSyncLocalIdColumn)) {
-                localRowId = diffsCursor.getLong(serverSyncLocalIdColumn);
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "the remote record with sync id " + serverSyncId
-                            + " has a local sync id, " + localRowId);
-                }
-                localSyncID = serverSyncId;
-                localSyncDirty = false;
-                localSyncVersion = null;
-            }
-
-            if (!TextUtils.isEmpty(localSyncID)) {
-                // An existing server item has changed
-                // If serverSyncVersion is null, there is no edit URL;
-                // server won't let this change be written.
-                // Just hold onto it, I guess, in case the server permissions
-                // change later.
-                if (serverSyncVersion != null) {
-                    boolean recordChanged = (localSyncVersion == null) ||
-                            !serverSyncVersion.equals(localSyncVersion);
-                    if (recordChanged) {
-                        if (localSyncDirty) {
-                            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                                Log.v(TAG, "remote record " + serverSyncId
-                                        + " conflicts with local _sync_id " + localSyncID
-                                        + ", local _id " + localRowId);
-                            }
-                            conflict = true;
-                        } else {
-                            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                                Log.v(TAG,
-                                        "remote record " +
-                                                serverSyncId +
-                                                " updates local _sync_id " +
-                                                localSyncID + ", local _id " +
-                                                localRowId);
-                            }
-                            update = true;
-                        }
-                    }
-                }
-            } else {
-                // the local db doesn't know about this record so add it
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "remote record " + serverSyncId + " is new, inserting");
-                }
-                insert = true;
-            }
-
-            if (update) {
-                updateRow(localRowId, serverDiffs, diffsCursor);
-                syncResult.stats.numUpdates++;
-            } else if (conflict) {
-                resolveRow(localRowId, serverSyncId, serverDiffs, diffsCursor);
-                syncResult.stats.numUpdates++;
-            } else if (insert) {
-                insertRow(serverDiffs, diffsCursor);
-                syncResult.stats.numInserts++;
-            }
+            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "checked " + localCount +
+                    " local entries");
+        } finally {
+            if (diffsCursor != null) diffsCursor.close();
+            if (localCursor != null) localCursor.close();
+            if (deletedCursor != null) deletedCursor.close();
         }
 
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "processed " + diffsCount + " server entries");
-        }
-
-        // If tombstones aren't in use delete any remaining local rows that
-        // don't have corresponding server rows. Keep the rows that don't
-        // have a sync id since those were created locally and haven't been
-        // synced to the server yet.
-        if (!diffsArePartial) {
-            while (!localCursor.isAfterLast() && !TextUtils.isEmpty(localCursor.getString(2))) {
-                if (mIsMergeCancelled) {
-                    localCursor.deactivate();
-                    deletedCursor.deactivate();
-                    diffsCursor.deactivate();
-                    return;
-                }
-                localCount++;
-                final String localSyncId = localCursor.getString(2);
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG,
-                            "deleting local record " +
-                                    localCursor.getLong(1) +
-                                    " _sync_id " + localSyncId);
-                }
-                deleteRow(localCursor);
-                if (mDeletedTable != null) {
-                    mDb.delete(mDeletedTable, _SYNC_ID + "=?", new String[] {localSyncId});
-                }
-                syncResult.stats.numDeletes++;
-                mDb.yieldIfContended();
-            }
-        }
-
-        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "checked " + localCount +
-                " local entries");
-        diffsCursor.deactivate();
-        localCursor.deactivate();
-        deletedCursor.deactivate();
 
         if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "applying deletions from the server");
 
         // Apply deletions from the server
         if (mDeletedTableURL != null) {
             diffsCursor = serverDiffs.query(mDeletedTableURL, null, null, null, null);
-
-            while (diffsCursor.moveToNext()) {
-                if (mIsMergeCancelled) {
-                    diffsCursor.deactivate();
-                    return;
+            try {
+                while (diffsCursor.moveToNext()) {
+                    if (mIsMergeCancelled) {
+                        return;
+                    }
+                    // delete all rows that match each element in the diffsCursor
+                    fullyDeleteMatchingRows(diffsCursor, account, syncResult);
+                    mDb.yieldIfContended();
                 }
-                // delete all rows that match each element in the diffsCursor
-                fullyDeleteMatchingRows(diffsCursor, account, syncResult);
-                mDb.yieldIfContended();
+            } finally {
+                diffsCursor.close();
             }
-            diffsCursor.deactivate();
         }
     }
 
-    private void fullyDeleteMatchingRows(Cursor diffsCursor, String account,
+    private void fullyDeleteMatchingRows(Cursor diffsCursor, Account account,
             SyncResult syncResult) {
         int serverSyncIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_ID);
         final boolean deleteBySyncId = !diffsCursor.isNull(serverSyncIdColumn);
 
         // delete the rows explicitly so that the delete operation can be overridden
-        final Cursor c;
         final String[] selectionArgs;
-        if (deleteBySyncId) {
-            selectionArgs = new String[]{diffsCursor.getString(serverSyncIdColumn), account};
-            c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_SYNC_ID_AND_ACCOUNT,
-                    selectionArgs, null, null, null);
-        } else {
-            int serverSyncLocalIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_LOCAL_ID);
-            selectionArgs = new String[]{diffsCursor.getString(serverSyncLocalIdColumn)};
-            c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_ID, selectionArgs,
-                    null, null, null);
-        }
+        Cursor c = null;
         try {
+            if (deleteBySyncId) {
+                selectionArgs = new String[]{diffsCursor.getString(serverSyncIdColumn),
+                        account.mName, account.mType};
+                c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_SYNC_ID_AND_ACCOUNT,
+                        selectionArgs, null, null, null);
+            } else {
+                int serverSyncLocalIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_LOCAL_ID);
+                selectionArgs = new String[]{diffsCursor.getString(serverSyncLocalIdColumn)};
+                c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_ID, selectionArgs,
+                        null, null, null);
+            }
             c.moveToFirst();
             while (!c.isAfterLast()) {
                 deleteRow(c); // advances the cursor
                 syncResult.stats.numDeletes++;
             }
         } finally {
-            c.deactivate();
+          if (c != null) c.close();
         }
         if (deleteBySyncId && mDeletedTable != null) {
             mDb.delete(mDeletedTable, SELECT_BY_SYNC_ID_AND_ACCOUNT, selectionArgs);
@@ -516,43 +517,46 @@
      * Finds local changes, placing the results in the given result object.
      * @param temporaryInstanceFactory As an optimization for the case
      * where there are no client-side diffs, mergeResult may initially
-     * have no {@link android.content.TempProviderSyncResult#tempContentProvider}.  If this is
+     * have no {@link TempProviderSyncResult#tempContentProvider}.  If this is
      * the first in the sequence of AbstractTableMergers to find
      * client-side diffs, it will use the given ContentProvider to
      * create a temporary instance and store its {@link
-     * ContentProvider} in the mergeResult.
+     * android.content.ContentProvider} in the mergeResult.
      * @param account
      * @param syncResult
      */
     private void findLocalChanges(TempProviderSyncResult mergeResult,
-            SyncableContentProvider temporaryInstanceFactory, String account,
+            SyncableContentProvider temporaryInstanceFactory, Account account,
             SyncResult syncResult) {
         SyncableContentProvider clientDiffs = mergeResult.tempContentProvider;
         if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "generating client updates");
 
-        final String[] accountSelectionArgs = new String[]{account};
+        final String[] accountSelectionArgs = new String[]{account.mName, account.mType};
 
         // Generate the client updates and insertions
         // Create a cursor for dirty records
+        long numInsertsOrUpdates = 0;
         Cursor localChangesCursor = mDb.query(mTable, null, SELECT_UNSYNCED, accountSelectionArgs,
                 null, null, null);
-        long numInsertsOrUpdates = localChangesCursor.getCount();
-        while (localChangesCursor.moveToNext()) {
-            if (mIsMergeCancelled) {
-                localChangesCursor.close();
-                return;
+        try {
+            numInsertsOrUpdates = localChangesCursor.getCount();
+            while (localChangesCursor.moveToNext()) {
+                if (mIsMergeCancelled) {
+                    return;
+                }
+                if (clientDiffs == null) {
+                    clientDiffs = temporaryInstanceFactory.getTemporaryInstance();
+                }
+                mValues.clear();
+                cursorRowToContentValues(localChangesCursor, mValues);
+                mValues.remove("_id");
+                DatabaseUtils.cursorLongToContentValues(localChangesCursor, "_id", mValues,
+                        _SYNC_LOCAL_ID);
+                clientDiffs.insert(mTableURL, mValues);
             }
-            if (clientDiffs == null) {
-                clientDiffs = temporaryInstanceFactory.getTemporaryInstance();
-            }
-            mValues.clear();
-            cursorRowToContentValues(localChangesCursor, mValues);
-            mValues.remove("_id");
-            DatabaseUtils.cursorLongToContentValues(localChangesCursor, "_id", mValues,
-                    _SYNC_LOCAL_ID);
-            clientDiffs.insert(mTableURL, mValues);
+        } finally {
+          localChangesCursor.close();
         }
-        localChangesCursor.close();
 
         // Generate the client deletions
         if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "generating client deletions");
@@ -561,23 +565,25 @@
         if (mDeletedTable != null) {
             Cursor deletedCursor = mDb.query(mDeletedTable,
                     syncIdAndVersionProjection,
-                    _SYNC_ACCOUNT + "=? AND " + _SYNC_ID + " IS NOT NULL", accountSelectionArgs,
+                    _SYNC_ACCOUNT + "=? AND " + _SYNC_ACCOUNT_TYPE + "=? AND "
+                            + _SYNC_ID + " IS NOT NULL", accountSelectionArgs,
                     null, null, mDeletedTable + "." + _SYNC_ID);
-
-            numDeletedEntries = deletedCursor.getCount();
-            while (deletedCursor.moveToNext()) {
-                if (mIsMergeCancelled) {
-                    deletedCursor.close();
-                    return;
+            try {
+                numDeletedEntries = deletedCursor.getCount();
+                while (deletedCursor.moveToNext()) {
+                    if (mIsMergeCancelled) {
+                        return;
+                    }
+                    if (clientDiffs == null) {
+                        clientDiffs = temporaryInstanceFactory.getTemporaryInstance();
+                    }
+                    mValues.clear();
+                    DatabaseUtils.cursorRowToContentValues(deletedCursor, mValues);
+                    clientDiffs.insert(mDeletedTableURL, mValues);
                 }
-                if (clientDiffs == null) {
-                    clientDiffs = temporaryInstanceFactory.getTemporaryInstance();
-                }
-                mValues.clear();
-                DatabaseUtils.cursorRowToContentValues(deletedCursor, mValues);
-                clientDiffs.insert(mDeletedTableURL, mValues);
+            } finally {
+                deletedCursor.close();
             }
-            deletedCursor.close();
         }
 
         if (clientDiffs != null) {
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
new file mode 100644
index 0000000..f15a902
--- /dev/null
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.accounts.Account;
+import android.os.Bundle;
+import android.os.Process;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation.
+ * If a sync operation is already in progress when a startSync() request is received then an error
+ * will be returned to the new request and the existing request will be allowed to continue.
+ * When a startSync() is received and there is no sync operation in progress then a thread
+ * will be started to run the operation and {@link #performSync} will be invoked on that thread.
+ * If a cancelSync() is received that matches an existing sync operation then the thread
+ * that is running that sync operation will be interrupted, which will indicate to the thread
+ * that the sync has been canceled.
+ *
+ * @hide
+ */
+public abstract class AbstractThreadedSyncAdapter {
+    private final Context mContext;
+    private final AtomicInteger mNumSyncStarts;
+    private final ISyncAdapterImpl mISyncAdapterImpl;
+
+    // all accesses to this member variable must be synchronized on "this"
+    private SyncThread mSyncThread;
+
+    /** Kernel event log tag.  Also listed in data/etc/event-log-tags. */
+    public static final int LOG_SYNC_DETAILS = 2743;
+
+    /**
+     * Creates an {@link AbstractThreadedSyncAdapter}.
+     * @param context the {@link Context} that this is running within.
+     */
+    public AbstractThreadedSyncAdapter(Context context) {
+        mContext = context;
+        mISyncAdapterImpl = new ISyncAdapterImpl();
+        mNumSyncStarts = new AtomicInteger(0);
+        mSyncThread = null;
+    }
+
+    class ISyncAdapterImpl extends ISyncAdapter.Stub {
+        public void startSync(ISyncContext syncContext, String authority, Account account,
+                Bundle extras) {
+            final SyncContext syncContextClient = new SyncContext(syncContext);
+
+            boolean alreadyInProgress;
+            // synchronize to make sure that mSyncThread doesn't change between when we
+            // check it and when we use it
+            synchronized (this) {
+                if (mSyncThread == null) {
+                    mSyncThread = new SyncThread(
+                            "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
+                            syncContextClient, authority, account, extras);
+                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                    mSyncThread.start();
+                    alreadyInProgress = false;
+                } else {
+                    alreadyInProgress = true;
+                }
+            }
+
+            // do this outside since we don't want to call back into the syncContext while
+            // holding the synchronization lock
+            if (alreadyInProgress) {
+                syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
+            }
+        }
+
+        public void cancelSync(ISyncContext syncContext) {
+            // synchronize to make sure that mSyncThread doesn't change between when we
+            // check it and when we use it
+            synchronized (this) {
+                if (mSyncThread != null
+                        && mSyncThread.mSyncContext.getISyncContext() == syncContext) {
+                    mSyncThread.interrupt();
+                }
+            }
+        }
+    }
+
+    /**
+     * The thread that invokes performSync(). It also acquires the provider for this sync
+     * before calling performSync and releases it afterwards. Cancel this thread in order to
+     * cancel the sync.
+     */
+    private class SyncThread extends Thread {
+        private final SyncContext mSyncContext;
+        private final String mAuthority;
+        private final Account mAccount;
+        private final Bundle mExtras;
+
+        private SyncThread(String name, SyncContext syncContext, String authority,
+                Account account, Bundle extras) {
+            super(name);
+            mSyncContext = syncContext;
+            mAuthority = authority;
+            mAccount = account;
+            mExtras = extras;
+        }
+
+        public void run() {
+            if (isCanceled()) {
+                return;
+            }
+
+            SyncResult syncResult = new SyncResult();
+            ContentProviderClient provider = null;
+            try {
+                provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
+                if (provider != null) {
+                    AbstractThreadedSyncAdapter.this.performSync(mAccount, mExtras,
+                            mAuthority, provider, syncResult);
+                } else {
+                    // TODO(fredq) update the syncResults to indicate that we were unable to
+                    // find the provider. maybe with a ProviderError?
+                }
+            } finally {
+                if (provider != null) {
+                    provider.release();
+                }
+                if (!isCanceled()) {
+                    mSyncContext.onFinished(syncResult);
+                }
+                // synchronize so that the assignment will be seen by other threads
+                // that also synchronize accesses to mSyncThread
+                synchronized (this) {
+                    mSyncThread = null;
+                }
+            }
+        }
+
+        private boolean isCanceled() {
+            return Thread.currentThread().isInterrupted();
+        }
+    }
+
+    /**
+     * @return a reference to the ISyncAdapter interface into this SyncAdapter implementation.
+     */
+    public final ISyncAdapter getISyncAdapter() {
+        return mISyncAdapterImpl;
+    }
+
+    /**
+     * Perform a sync for this account. SyncAdapter-specific parameters may
+     * be specified in extras, which is guaranteed to not be null. Invocations
+     * of this method are guaranteed to be serialized.
+     *
+     * @param account the account that should be synced
+     * @param extras SyncAdapter-specific parameters
+     * @param authority the authority of this sync request
+     * @param provider a ContentProviderClient that points to the ContentProvider for this
+     *   authority
+     * @param syncResult SyncAdapter-specific parameters
+     */
+    public abstract void performSync(Account account, Bundle extras,
+            String authority, ContentProviderClient provider, SyncResult syncResult);
+}
\ No newline at end of file
diff --git a/core/java/android/content/ActiveSyncInfo.java b/core/java/android/content/ActiveSyncInfo.java
index 63be8d1..209dffa 100644
--- a/core/java/android/content/ActiveSyncInfo.java
+++ b/core/java/android/content/ActiveSyncInfo.java
@@ -16,17 +16,18 @@
 
 package android.content;
 
+import android.accounts.Account;
 import android.os.Parcel;
 import android.os.Parcelable.Creator;
 
 /** @hide */
 public class ActiveSyncInfo {
     public final int authorityId;
-    public final String account;
+    public final Account account;
     public final String authority;
     public final long startTime;
     
-    ActiveSyncInfo(int authorityId, String account, String authority,
+    ActiveSyncInfo(int authorityId, Account account, String authority,
             long startTime) {
         this.authorityId = authorityId;
         this.account = account;
@@ -40,14 +41,14 @@
 
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(authorityId);
-        parcel.writeString(account);
+        account.writeToParcel(parcel, 0);
         parcel.writeString(authority);
         parcel.writeLong(startTime);
     }
 
     ActiveSyncInfo(Parcel parcel) {
         authorityId = parcel.readInt();
-        account = parcel.readString();
+        account = new Account(parcel);
         authority = parcel.readString();
         startTime = parcel.readLong();
     }
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 5cc5730..4e631c4 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -32,6 +32,7 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.util.ArrayList;
 
 /**
  * Content providers are one of the primary building blocks of Android applications, providing
@@ -130,6 +131,12 @@
                     selectionArgs, sortOrder);
         }
 
+        public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
+                String sortOrder) {
+            checkReadPermission(uri);
+            return ContentProvider.this.queryEntities(uri, selection, selectionArgs, sortOrder);
+        }
+
         public String getType(Uri uri) {
             return ContentProvider.this.getType(uri);
         }
@@ -145,6 +152,25 @@
             return ContentProvider.this.bulkInsert(uri, initialValues);
         }
 
+        public Uri insertEntity(Uri uri, Entity entities) {
+            checkWritePermission(uri);
+            return ContentProvider.this.insertEntity(uri, entities);
+        }
+
+        public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+                throws OperationApplicationException {
+            for (ContentProviderOperation operation : operations) {
+                if (operation.isReadOperation()) {
+                    checkReadPermission(operation.getUri());
+                }
+
+                if (operation.isWriteOperation()) {
+                    checkWritePermission(operation.getUri());
+                }
+            }
+            return ContentProvider.this.applyBatch(operations);
+        }
+
         public int delete(Uri uri, String selection, String[] selectionArgs) {
             checkWritePermission(uri);
             return ContentProvider.this.delete(uri, selection, selectionArgs);
@@ -156,6 +182,11 @@
             return ContentProvider.this.update(uri, values, selection, selectionArgs);
         }
 
+        public int updateEntity(Uri uri, Entity entity) {
+            checkWritePermission(uri);
+            return ContentProvider.this.updateEntity(uri, entity);
+        }
+
         public ParcelFileDescriptor openFile(Uri uri, String mode)
                 throws FileNotFoundException {
             if (mode != null && mode.startsWith("rw")) checkWritePermission(uri);
@@ -170,12 +201,6 @@
             return ContentProvider.this.openAssetFile(uri, mode);
         }
 
-        public ISyncAdapter getSyncAdapter() {
-            checkWritePermission(null);
-            SyncAdapter sa = ContentProvider.this.getSyncAdapter();
-            return sa != null ? sa.getISyncAdapter() : null;
-        }
-
         private void checkReadPermission(Uri uri) {
             final String rperm = getReadPermission();
             final int pid = Binder.getCallingPid();
@@ -334,6 +359,11 @@
     public abstract Cursor query(Uri uri, String[] projection,
             String selection, String[] selectionArgs, String sortOrder);
 
+    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
+            String sortOrder) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * Return the MIME type of the data at the given URI. This should start with
      * <code>vnd.android.cursor.item</code> for a single record,
@@ -384,6 +414,10 @@
         return numValues;
     }
 
+    public Uri insertEntity(Uri uri, Entity entity) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * A request to delete one or more rows. The selection clause is applied when performing
      * the deletion, allowing the operation to affect multiple rows in a
@@ -428,6 +462,10 @@
     public abstract int update(Uri uri, ContentValues values, String selection,
             String[] selectionArgs);
 
+    public int updateEntity(Uri uri, Entity entity) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * Open a file blob associated with a content URI.
      * This method can be called from multiple
@@ -551,23 +589,6 @@
     }
 
     /**
-     * Get the sync adapter that is to be used by this content provider.
-     * This is intended for use by the sync system. If null then this
-     * content provider is considered not syncable.
-     * This method can be called from multiple
-     * threads, as described in
-     * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
-     * Processes and Threads</a>.
-     * 
-     * @return the SyncAdapter that is to be used by this ContentProvider, or null
-     *   if this ContentProvider is not syncable
-     * @hide
-     */
-    public SyncAdapter getSyncAdapter() {
-        return null;
-    }
-
-    /**
      * Returns true if this instance is a temporary content provider.
      * @return true if this instance is a temporary content provider
      */
@@ -607,4 +628,27 @@
             ContentProvider.this.onCreate();
         }
     }
-}
+
+    /**
+     * Applies each of the {@link ContentProviderOperation} objects and returns an array
+     * of their results. Passes through OperationApplicationException, which may be thrown
+     * by the call to {@link ContentProviderOperation#apply}.
+     * If all the applications succeed then a {@link ContentProviderResult} array with the
+     * same number of elements as the operations will be returned. It is implementation-specific
+     * how many, if any, operations will have been successfully applied if a call to
+     * apply results in a {@link OperationApplicationException}.
+     * @param operations the operations to apply
+     * @return the results of the applications
+     * @throws OperationApplicationException thrown if an application fails.
+     * See {@link ContentProviderOperation#apply} for more information.
+     */
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+            throws OperationApplicationException {
+        final int numOperations = operations.size();
+        final ContentProviderResult[] results = new ContentProviderResult[numOperations];
+        for (int i = 0; i < numOperations; i++) {
+            results[i] = operations.get(i).apply(this, results, i);
+        }
+        return results;
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
new file mode 100644
index 0000000..452653e
--- /dev/null
+++ b/core/java/android/content/ContentProviderClient.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.ParcelFileDescriptor;
+import android.content.res.AssetFileDescriptor;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+/**
+ * The public interface object used to interact with a {@link ContentProvider}. This is obtained by
+ * calling {@link ContentResolver#acquireContentProviderClient}. This object must be released
+ * using {@link #release} in order to indicate to the system that the {@link ContentProvider} is
+ * no longer needed and can be killed to free up resources.
+ */
+public class ContentProviderClient {
+    private final IContentProvider mContentProvider;
+    private final ContentResolver mContentResolver;
+
+    /**
+     * @hide
+     */
+    ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider) {
+        mContentProvider = contentProvider;
+        mContentResolver = contentResolver;
+    }
+
+    /** see {@link ContentProvider#query} */
+    public Cursor query(Uri url, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) throws RemoteException {
+        return mContentProvider.query(url, projection, selection,  selectionArgs, sortOrder);
+    }
+
+    /** see {@link ContentProvider#getType} */
+    public String getType(Uri url) throws RemoteException {
+        return mContentProvider.getType(url);
+    }
+
+    /** see {@link ContentProvider#insert} */
+    public Uri insert(Uri url, ContentValues initialValues)
+            throws RemoteException {
+        return mContentProvider.insert(url, initialValues);
+    }
+
+    /** see {@link ContentProvider#bulkInsert} */
+    public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
+        return mContentProvider.bulkInsert(url, initialValues);
+    }
+
+    /** see {@link ContentProvider#delete} */
+    public int delete(Uri url, String selection, String[] selectionArgs)
+            throws RemoteException {
+        return mContentProvider.delete(url, selection, selectionArgs);
+    }
+
+    /** see {@link ContentProvider#update} */
+    public int update(Uri url, ContentValues values, String selection,
+            String[] selectionArgs) throws RemoteException {
+        return mContentProvider.update(url, values, selection, selectionArgs);
+    }
+
+    /** see {@link ContentProvider#openFile} */
+    public ParcelFileDescriptor openFile(Uri url, String mode)
+            throws RemoteException, FileNotFoundException {
+        return mContentProvider.openFile(url, mode);
+    }
+
+    /** see {@link ContentProvider#openAssetFile} */
+    public AssetFileDescriptor openAssetFile(Uri url, String mode)
+            throws RemoteException, FileNotFoundException {
+        return mContentProvider.openAssetFile(url, mode);
+    }
+
+    /** see {@link ContentProvider#queryEntities} */
+    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
+            String sortOrder) throws RemoteException {
+        return mContentProvider.queryEntities(uri, selection, selectionArgs, sortOrder);
+    }
+
+    /** see {@link ContentProvider#insertEntity} */
+    public Uri insertEntity(Uri uri, Entity entity) throws RemoteException {
+        return mContentProvider.insertEntity(uri, entity);
+    }
+
+    /** see {@link ContentProvider#updateEntity} */
+    public int updateEntity(Uri uri, Entity entity) throws RemoteException {
+        return mContentProvider.updateEntity(uri, entity);
+    }
+
+    /** see {@link ContentProvider#applyBatch} */
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException {
+        return mContentProvider.applyBatch(operations);
+    }
+
+    /**
+     * Call this to indicate to the system that the associated {@link ContentProvider} is no
+     * longer needed by this {@link ContentProviderClient}.
+     * @return true if this was release, false if it was already released
+     */
+    public boolean release() {
+        return mContentResolver.releaseProvider(mContentProvider);
+    }
+
+    /**
+     * Get a reference to the {@link ContentProvider} that is associated with this
+     * client. If the {@link ContentProvider} is running in a different process then
+     * null will be returned. This can be used if you know you are running in the same
+     * process as a provider, and want to get direct access to its implementation details.
+     *
+     * @return If the associated {@link ContentProvider} is local, returns it.
+     * Otherwise returns null.
+     */
+    public ContentProvider getLocalContentProvider() {
+        return ContentProvider.coerceToLocalContentProvider(mContentProvider);
+    }
+}
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index e5e3f74..a4c217b 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -33,6 +33,7 @@
 import android.os.Parcelable;
 
 import java.io.FileNotFoundException;
+import java.util.ArrayList;
 
 /**
  * {@hide}
@@ -105,6 +106,20 @@
                     return true;
                 }
 
+                case QUERY_ENTITIES_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    String selection = data.readString();
+                    String[] selectionArgs = data.readStringArray();
+                    String sortOrder = data.readString();
+                    EntityIterator entityIterator = queryEntities(url, selection, selectionArgs,
+                            sortOrder);
+                    reply.writeNoException();
+                    reply.writeStrongBinder(new IEntityIteratorImpl(entityIterator).asBinder());
+                    return true;
+                }
+
                 case GET_TYPE_TRANSACTION:
                 {
                     data.enforceInterface(IContentProvider.descriptor);
@@ -140,6 +155,43 @@
                     return true;
                 }
 
+                case INSERT_ENTITIES_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    Uri uri = Uri.CREATOR.createFromParcel(data);
+                    Entity entity = (Entity) data.readParcelable(null);
+                    Uri newUri = insertEntity(uri, entity);
+                    reply.writeNoException();
+                    Uri.writeToParcel(reply, newUri);
+                    return true;
+                }
+
+                case UPDATE_ENTITIES_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    Uri uri = Uri.CREATOR.createFromParcel(data);
+                    Entity entity = (Entity) data.readParcelable(null);
+                    int count = updateEntity(uri, entity);
+                    reply.writeNoException();
+                    reply.writeInt(count);
+                    return true;
+                }
+
+                case APPLY_BATCH_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    final int numOperations = data.readInt();
+                    final ArrayList<ContentProviderOperation> operations =
+                            new ArrayList<ContentProviderOperation>(numOperations);
+                    for (int i = 0; i < numOperations; i++) {
+                        operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
+                    }
+                    final ContentProviderResult[] results = applyBatch(operations);
+                    reply.writeNoException();
+                    reply.writeTypedArray(results, 0);
+                    return true;
+                }
+
                 case DELETE_TRANSACTION:
                 {
                     data.enforceInterface(IContentProvider.descriptor);
@@ -206,15 +258,6 @@
                     }
                     return true;
                 }
-
-                case GET_SYNC_ADAPTER_TRANSACTION:
-                {
-                    data.enforceInterface(IContentProvider.descriptor);
-                    ISyncAdapter sa = getSyncAdapter();
-                    reply.writeNoException();
-                    reply.writeStrongBinder(sa != null ? sa.asBinder() : null);
-                    return true;
-                }
             }
         } catch (Exception e) {
             DatabaseUtils.writeExceptionToParcel(reply, e);
@@ -224,6 +267,25 @@
         return super.onTransact(code, data, reply, flags);
     }
 
+    private class IEntityIteratorImpl extends IEntityIterator.Stub {
+        private final EntityIterator mEntityIterator;
+
+        IEntityIteratorImpl(EntityIterator iterator) {
+            mEntityIterator = iterator;
+        }
+        public boolean hasNext() throws RemoteException {
+            return mEntityIterator.hasNext();
+        }
+
+        public Entity next() throws RemoteException {
+            return mEntityIterator.next();
+        }
+
+        public void close() throws RemoteException {
+            mEntityIterator.close();
+        }
+    }
+
     public IBinder asBinder()
     {
         return this;
@@ -297,7 +359,7 @@
         BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
         IBulkCursor bulkCursor = bulkQuery(url, projection, selection, selectionArgs, sortOrder,
                 adaptor.getObserver(), window);
-         
+
         if (bulkCursor == null) {
             return null;
         }
@@ -305,6 +367,54 @@
         return adaptor;
     }
 
+    public EntityIterator queryEntities(Uri url, String selection, String[] selectionArgs,
+            String sortOrder)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+
+        data.writeInterfaceToken(IContentProvider.descriptor);
+
+        url.writeToParcel(data, 0);
+        data.writeString(selection);
+        data.writeStringArray(selectionArgs);
+        data.writeString(sortOrder);
+
+        mRemote.transact(IContentProvider.QUERY_ENTITIES_TRANSACTION, data, reply, 0);
+
+        DatabaseUtils.readExceptionFromParcel(reply);
+
+        IBinder entityIteratorBinder = reply.readStrongBinder();
+
+        data.recycle();
+        reply.recycle();
+
+        return new RemoteEntityIterator(IEntityIterator.Stub.asInterface(entityIteratorBinder));
+    }
+
+    static class RemoteEntityIterator implements EntityIterator {
+        private final IEntityIterator mEntityIterator;
+        RemoteEntityIterator(IEntityIterator entityIterator) {
+            mEntityIterator = entityIterator;
+        }
+
+        public boolean hasNext() throws RemoteException {
+            return mEntityIterator.hasNext();
+        }
+
+        public Entity next() throws RemoteException {
+            return mEntityIterator.next();
+        }
+
+        public void close() {
+            try {
+                mEntityIterator.close();
+            } catch (RemoteException e) {
+                // doesn't matter
+            }
+        }
+    }
+
     public String getType(Uri url) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -366,6 +476,66 @@
         return count;
     }
 
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+
+        data.writeInterfaceToken(IContentProvider.descriptor);
+        data.writeInt(operations.size());
+        for (ContentProviderOperation operation : operations) {
+            operation.writeToParcel(data, 0);
+        }
+        mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0);
+
+        DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply);
+        final ContentProviderResult[] results =
+                reply.createTypedArray(ContentProviderResult.CREATOR);
+
+        data.recycle();
+        reply.recycle();
+
+        return results;
+    }
+
+    public Uri insertEntity(Uri uri, Entity entity) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+            uri.writeToParcel(data, 0);
+            data.writeParcelable(entity, 0);
+
+            mRemote.transact(IContentProvider.INSERT_ENTITIES_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            return Uri.CREATOR.createFromParcel(reply);
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    public int updateEntity(Uri uri, Entity entity) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+            uri.writeToParcel(data, 0);
+            data.writeParcelable(entity, 0);
+
+            mRemote.transact(IContentProvider.UPDATE_ENTITIES_TRANSACTION, data, reply, 0);
+
+            DatabaseUtils.readExceptionFromParcel(reply);
+            return reply.readInt();
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
     public int delete(Uri url, String selection, String[] selectionArgs)
             throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -456,23 +626,6 @@
         return fd;
     }
 
-    public ISyncAdapter getSyncAdapter() throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-
-        data.writeInterfaceToken(IContentProvider.descriptor);
-
-        mRemote.transact(IContentProvider.GET_SYNC_ADAPTER_TRANSACTION, data, reply, 0);
-
-        DatabaseUtils.readExceptionFromParcel(reply);
-        ISyncAdapter syncAdapter = ISyncAdapter.Stub.asInterface(reply.readStrongBinder());
-
-        data.recycle();
-        reply.recycle();
-
-        return syncAdapter;
-    }
-
     private IBinder mRemote;
 }
 
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
new file mode 100644
index 0000000..8b0b6ab
--- /dev/null
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.net.Uri;
+import android.database.Cursor;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.os.Debug;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public class ContentProviderOperation implements Parcelable {
+    private final static int TYPE_INSERT = 1;
+    private final static int TYPE_UPDATE = 2;
+    private final static int TYPE_DELETE = 3;
+    private final static int TYPE_COUNT = 4;
+
+    private final int mType;
+    private final Uri mUri;
+    private final String mSelection;
+    private final String[] mSelectionArgs;
+    private final ContentValues mValues;
+    private final Integer mExpectedCount;
+    private final ContentValues mValuesBackReferences;
+    private final Map<Integer, Integer> mSelectionArgsBackReferences;
+
+    private static final String[] COUNT_COLUMNS = new String[]{"count(*)"};
+
+    /**
+     * Creates a {@link ContentProviderOperation} by copying the contents of a
+     * {@link Builder}.
+     */
+    private ContentProviderOperation(Builder builder) {
+        mType = builder.mType;
+        mUri = builder.mUri;
+        mValues = builder.mValues;
+        mSelection = builder.mSelection;
+        mSelectionArgs = builder.mSelectionArgs;
+        mExpectedCount = builder.mExpectedCount;
+        mSelectionArgsBackReferences = builder.mSelectionArgsBackReferences;
+        mValuesBackReferences = builder.mValuesBackReferences;
+    }
+
+    private ContentProviderOperation(Parcel source) {
+        mType = source.readInt();
+        mUri = Uri.CREATOR.createFromParcel(source);
+        mValues = source.readInt() != 0 ? ContentValues.CREATOR.createFromParcel(source) : null;
+        mSelection = source.readInt() != 0 ? source.readString() : null;
+        mSelectionArgs = source.readInt() != 0 ? source.readStringArray() : null;
+        mExpectedCount = source.readInt() != 0 ? source.readInt() : null;
+        mValuesBackReferences = source.readInt() != 0
+                
+                ? ContentValues.CREATOR.createFromParcel(source)
+                : null;
+        mSelectionArgsBackReferences = source.readInt() != 0
+                ? new HashMap<Integer, Integer>()
+                : null;
+        if (mSelectionArgsBackReferences != null) {
+            final int count = source.readInt();
+            for (int i = 0; i < count; i++) {
+                mSelectionArgsBackReferences.put(source.readInt(), source.readInt());
+            }
+        }
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        Uri.writeToParcel(dest, mUri);
+        if (mValues != null) {
+            dest.writeInt(1);
+            mValues.writeToParcel(dest, 0);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mSelection != null) {
+            dest.writeInt(1);
+            dest.writeString(mSelection);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mSelectionArgs != null) {
+            dest.writeInt(1);
+            dest.writeStringArray(mSelectionArgs);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mExpectedCount != null) {
+            dest.writeInt(1);
+            dest.writeInt(mExpectedCount);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mValuesBackReferences != null) {
+            dest.writeInt(1);
+            mValuesBackReferences.writeToParcel(dest, 0);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mSelectionArgsBackReferences != null) {
+            dest.writeInt(1);
+            dest.writeInt(mSelectionArgsBackReferences.size());
+            for (Map.Entry<Integer, Integer> entry : mSelectionArgsBackReferences.entrySet()) {
+                dest.writeInt(entry.getKey());
+                dest.writeInt(entry.getValue());
+            }
+        } else {
+            dest.writeInt(0);
+        }
+    }
+
+    /**
+     * Create a {@link Builder} suitable for building an insert {@link ContentProviderOperation}.
+     * @param uri The {@link Uri} that is the target of the insert.
+     * @return a {@link Builder}
+     */
+    public static Builder newInsert(Uri uri) {
+        return new Builder(TYPE_INSERT, uri);
+    }
+
+    /**
+     * Create a {@link Builder} suitable for building an update {@link ContentProviderOperation}.
+     * @param uri The {@link Uri} that is the target of the update.
+     * @return a {@link Builder}
+     */
+    public static Builder newUpdate(Uri uri) {
+        return new Builder(TYPE_UPDATE, uri);
+    }
+
+    /**
+     * Create a {@link Builder} suitable for building a delete {@link ContentProviderOperation}.
+     * @param uri The {@link Uri} that is the target of the delete.
+     * @return a {@link Builder}
+     */
+    public static Builder newDelete(Uri uri) {
+        return new Builder(TYPE_DELETE, uri);
+    }
+
+    /**
+     * Create a {@link Builder} suitable for building a count query. When used in conjunction
+     * with {@link Builder#withExpectedCount(int)} this is useful for checking that the
+     * uri/selection has the expected number of rows.
+     * {@link ContentProviderOperation}.
+     * @param uri The {@link Uri} to query.
+     * @return a {@link Builder}
+     */
+    public static Builder newCountQuery(Uri uri) {
+        return new Builder(TYPE_COUNT, uri);
+    }
+
+    public Uri getUri() {
+        return mUri;
+    }
+
+    public boolean isWriteOperation() {
+        return mType == TYPE_DELETE || mType == TYPE_INSERT || mType == TYPE_UPDATE;
+    }
+
+    public boolean isReadOperation() {
+        return mType == TYPE_COUNT;
+    }
+
+    /**
+     * Applies this operation using the given provider. The backRefs array is used to resolve any
+     * back references that were requested using
+     * {@link Builder#withValueBackReferences(ContentValues)} and
+     * {@link Builder#withSelectionBackReference}.
+     * @param provider the {@link ContentProvider} on which this batch is applied
+     * @param backRefs a {@link ContentProviderResult} array that will be consulted
+     * to resolve any requested back references.
+     * @param numBackRefs the number of valid results on the backRefs array.
+     * @return a {@link ContentProviderResult} that contains either the {@link Uri} of the inserted
+     * row if this was an insert otherwise the number of rows affected.
+     * @throws OperationApplicationException thrown if either the insert fails or
+     * if the number of rows affected didn't match the expected count
+     */
+    public ContentProviderResult apply(ContentProvider provider, ContentProviderResult[] backRefs,
+            int numBackRefs) throws OperationApplicationException {
+        ContentValues values = resolveValueBackReferences(backRefs, numBackRefs);
+        String[] selectionArgs =
+                resolveSelectionArgsBackReferences(backRefs, numBackRefs);
+
+        if (mType == TYPE_INSERT) {
+            Uri newUri = provider.insert(mUri, values);
+            if (newUri == null) {
+                throw new OperationApplicationException("insert failed");
+            }
+            return new ContentProviderResult(newUri);
+        }
+
+        int numRows;
+        if (mType == TYPE_DELETE) {
+            numRows = provider.delete(mUri, mSelection, selectionArgs);
+        } else if (mType == TYPE_UPDATE) {
+            numRows = provider.update(mUri, values, mSelection, selectionArgs);
+        } else if (mType == TYPE_COUNT) {
+            Cursor cursor = provider.query(mUri, COUNT_COLUMNS, mSelection, selectionArgs, null);
+            try {
+                if (!cursor.moveToNext()) {
+                    throw new RuntimeException("since we are doing a count query we should always "
+                            + "be able to move to the first row");
+                }
+                if (cursor.getCount() != 1) {
+                    throw new RuntimeException("since we are doing a count query there should "
+                            + "always be exacly row, found " + cursor.getCount());
+                }
+                numRows = cursor.getInt(0);
+            } finally {
+                cursor.close();
+            }
+        } else {
+            throw new IllegalStateException("bad type, " + mType);
+        }
+
+        if (mExpectedCount != null && mExpectedCount != numRows) {
+            throw new OperationApplicationException("wrong number of rows: " + numRows);
+        }
+
+        return new ContentProviderResult(numRows);
+    }
+
+    /**
+     * The ContentValues back references are represented as a ContentValues object where the
+     * key refers to a column and the value is an index of the back reference whose
+     * valued should be associated with the column.
+     * @param backRefs an array of previous results
+     * @param numBackRefs the number of valid previous results in backRefs
+     * @return the ContentValues that should be used in this operation application after
+     * expansion of back references. This can be called if either mValues or mValuesBackReferences
+     * is null
+     * @VisibleForTesting this is intended to be a private method but it is exposed for
+     * unit testing purposes
+     */
+    public ContentValues resolveValueBackReferences(
+            ContentProviderResult[] backRefs, int numBackRefs) {
+        if (mValuesBackReferences == null) {
+            return mValues;
+        }
+        final ContentValues values;
+        if (mValues == null) {
+            values = new ContentValues();
+        } else {
+            values = new ContentValues(mValues);
+        }
+        for (Map.Entry<String, Object> entry : mValuesBackReferences.valueSet()) {
+            String key = entry.getKey();
+            Integer backRefIndex = mValuesBackReferences.getAsInteger(key);
+            if (backRefIndex == null) {
+                throw new IllegalArgumentException("values backref " + key + " is not an integer");
+            }
+            values.put(key, backRefToValue(backRefs, numBackRefs, backRefIndex));
+        }
+        return values;
+    }
+
+    /**
+     * The Selection Arguments back references are represented as a Map of Integer->Integer where
+     * the key is an index into the selection argument array (see {@link Builder#withSelection})
+     * and the value is the index of the previous result that should be used for that selection
+     * argument array slot.
+     * @param backRefs an array of previous results
+     * @param numBackRefs the number of valid previous results in backRefs
+     * @return the ContentValues that should be used in this operation application after
+     * expansion of back references. This can be called if either mValues or mValuesBackReferences
+     * is null
+     * @VisibleForTesting this is intended to be a private method but it is exposed for
+     * unit testing purposes
+     */
+    public String[] resolveSelectionArgsBackReferences(
+            ContentProviderResult[] backRefs, int numBackRefs) {
+        if (mSelectionArgsBackReferences == null) {
+            return mSelectionArgs;
+        }
+        String[] newArgs = new String[mSelectionArgs.length];
+        System.arraycopy(mSelectionArgs, 0, newArgs, 0, mSelectionArgs.length);
+        for (Map.Entry<Integer, Integer> selectionArgBackRef
+                : mSelectionArgsBackReferences.entrySet()) {
+            final Integer selectionArgIndex = selectionArgBackRef.getKey();
+            final int backRefIndex = selectionArgBackRef.getValue();
+            newArgs[selectionArgIndex] = backRefToValue(backRefs, numBackRefs, backRefIndex);
+        }
+        return newArgs;
+    }
+
+    /**
+     * Return the string representation of the requested back reference.
+     * @param backRefs an array of results
+     * @param numBackRefs the number of items in the backRefs array that are valid
+     * @param backRefIndex which backRef to be used
+     * @throws ArrayIndexOutOfBoundsException thrown if the backRefIndex is larger than
+     * the numBackRefs
+     * @return the string representation of the requested back reference.
+     */
+    private static String backRefToValue(ContentProviderResult[] backRefs, int numBackRefs,
+            Integer backRefIndex) {
+        if (backRefIndex >= numBackRefs) {
+            throw new ArrayIndexOutOfBoundsException("asked for back ref " + backRefIndex
+                    + " but there are only " + numBackRefs + " back refs");
+        }
+        ContentProviderResult backRef = backRefs[backRefIndex];
+        String backRefValue;
+        if (backRef.uri != null) {
+            backRefValue = backRef.uri.getLastPathSegment();
+        } else {
+            backRefValue = String.valueOf(backRef.count);
+        }
+        return backRefValue;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<ContentProviderOperation> CREATOR =
+            new Creator<ContentProviderOperation>() {
+        public ContentProviderOperation createFromParcel(Parcel source) {
+            return new ContentProviderOperation(source);
+        }
+
+        public ContentProviderOperation[] newArray(int size) {
+            return new ContentProviderOperation[size];
+        }
+    };
+
+
+    /**
+     * Used to add parameters to a {@link ContentProviderOperation}. The {@link Builder} is
+     * first created by calling {@link ContentProviderOperation#newInsert(android.net.Uri)},
+     * {@link ContentProviderOperation#newUpdate(android.net.Uri)},
+     * {@link ContentProviderOperation#newDelete(android.net.Uri)} or
+     * {@link ContentProviderOperation#newCountQuery(android.net.Uri)}. The withXXX methods
+     * can then be used to add parameters to the builder. See the specific methods to find for
+     * which {@link Builder} type each is allowed. Call {@link #build} to create the
+     * {@link ContentProviderOperation} once all the parameters have been supplied.
+     */
+    public static class Builder {
+        private final int mType;
+        private final Uri mUri;
+        private String mSelection;
+        private String[] mSelectionArgs;
+        private ContentValues mValues;
+        private Integer mExpectedCount;
+        private ContentValues mValuesBackReferences;
+        private Map<Integer, Integer> mSelectionArgsBackReferences;
+
+        /** Create a {@link Builder} of a given type. The uri must not be null. */
+        private Builder(int type, Uri uri) {
+            if (uri == null) {
+                throw new IllegalArgumentException("uri must not be null");
+            }
+            mType = type;
+            mUri = uri;
+        }
+
+        /** Create a ContentProviderOperation from this {@link Builder}. */
+        public ContentProviderOperation build() {
+            if (mType == TYPE_UPDATE) {
+                if ((mValues == null || mValues.size() == 0)
+                        && (mValuesBackReferences == null || mValuesBackReferences.size() == 0)) {
+                    throw new IllegalArgumentException("Empty values");
+                }
+            }
+            return new ContentProviderOperation(this);
+        }
+
+        /**
+         * Add a {@link ContentValues} of back references. The key is the name of the column
+         * and the value is an integer that is the index of the previous result whose
+         * value should be used for the column. The value is added as a {@link String}.
+         * A column value from the back references takes precedence over a value specified in
+         * {@link #withValues}.
+         * This can only be used with builders of type insert or update.
+         * @return this builder, to allow for chaining.
+         */
+        public Builder withValueBackReferences(ContentValues backReferences) {
+            if (mType != TYPE_INSERT && mType != TYPE_UPDATE) {
+                throw new IllegalArgumentException(
+                        "only inserts and updates can have value back-references");
+            }
+            mValuesBackReferences = backReferences;
+            return this;
+        }
+
+        /**
+         * Add a ContentValues back reference.
+         * A column value from the back references takes precedence over a value specified in
+         * {@link #withValues}.
+         * This can only be used with builders of type insert or update.
+         * @return this builder, to allow for chaining.
+         */
+        public Builder withValueBackReference(String key, int previousResult) {
+            if (mType != TYPE_INSERT && mType != TYPE_UPDATE) {
+                throw new IllegalArgumentException(
+                        "only inserts and updates can have value back-references");
+            }
+            if (mValuesBackReferences == null) {
+                mValuesBackReferences = new ContentValues();
+            }
+            mValuesBackReferences.put(key, previousResult);
+            return this;
+        }
+
+        /**
+         * Add a back references as a selection arg. Any value at that index of the selection arg
+         * that was specified by {@link #withSelection} will be overwritten.
+         * This can only be used with builders of type update, delete, or count query.
+         * @return this builder, to allow for chaining.
+         */
+        public Builder withSelectionBackReference(int selectionArgIndex, int previousResult) {
+            if (mType != TYPE_COUNT && mType != TYPE_UPDATE && mType != TYPE_DELETE) {
+                throw new IllegalArgumentException(
+                        "only deletes, updates and counts can have selection back-references");
+            }
+            if (mSelectionArgsBackReferences == null) {
+                mSelectionArgsBackReferences = new HashMap<Integer, Integer>();
+            }
+            mSelectionArgsBackReferences.put(selectionArgIndex, previousResult);
+            return this;
+        }
+
+        /**
+         * The ContentValues to use. This may be null. These values may be overwritten by
+         * the corresponding value specified by {@link #withValueBackReference} or by
+         * future calls to {@link #withValues} or {@link #withValue}.
+         * This can only be used with builders of type insert or update.
+         * @return this builder, to allow for chaining.
+         */
+        public Builder withValues(ContentValues values) {
+            if (mType != TYPE_INSERT && mType != TYPE_UPDATE) {
+                throw new IllegalArgumentException("only inserts and updates can have values");
+            }
+            if (mValues == null) {
+                mValues = new ContentValues();
+            }
+            mValues.putAll(values);
+            return this;
+        }
+
+        /**
+         * A value to insert or update. This value may be overwritten by
+         * the corresponding value specified by {@link #withValueBackReference}.
+         * This can only be used with builders of type insert or update.
+         * @param key the name of this value
+         * @param value the value itself. the type must be acceptable for insertion by
+         * {@link ContentValues#put}
+         * @return this builder, to allow for chaining.
+         */
+        public Builder withValue(String key, Object value) {
+            if (mType != TYPE_INSERT && mType != TYPE_UPDATE) {
+                throw new IllegalArgumentException("only inserts and updates can have values");
+            }
+            if (mValues == null) {
+                mValues = new ContentValues();
+            }
+            if (value == null) {
+                mValues.putNull(key);
+            } else if (value instanceof String) {
+                mValues.put(key, (String) value);
+            } else if (value instanceof Byte) {
+                mValues.put(key, (Byte) value);
+            } else if (value instanceof Short) {
+                mValues.put(key, (Short) value);
+            } else if (value instanceof Integer) {
+                mValues.put(key, (Integer) value);
+            } else if (value instanceof Long) {
+                mValues.put(key, (Long) value);
+            } else if (value instanceof Float) {
+                mValues.put(key, (Float) value);
+            } else if (value instanceof Double) {
+                mValues.put(key, (Double) value);
+            } else if (value instanceof Boolean) {
+                mValues.put(key, (Boolean) value);
+            } else if (value instanceof byte[]) {
+                mValues.put(key, (byte[]) value);
+            } else {
+                throw new IllegalArgumentException("bad value type: " + value.getClass().getName());
+            }
+            return this;
+        }
+        
+        /**
+         * The selection and arguments to use. An occurrence of '?' in the selection will be
+         * replaced with the corresponding occurence of the selection argument. Any of the
+         * selection arguments may be overwritten by a selection argument back reference as
+         * specified by {@link #withSelectionBackReference}.
+         * This can only be used with builders of type update, delete, or count query.
+         * @return this builder, to allow for chaining.
+         */
+        public Builder withSelection(String selection, String[] selectionArgs) {
+            if (mType != TYPE_DELETE && mType != TYPE_UPDATE && mType != TYPE_COUNT) {
+                throw new IllegalArgumentException(
+                        "only deletes, updates and counts can have selections");
+            }
+            mSelection = selection;
+            mSelectionArgs = selectionArgs;
+            return this;
+        }
+
+        /**
+         * If set then if the number of rows affected by this operation do not match
+         * this count {@link OperationApplicationException} will be throw.
+         * This can only be used with builders of type update, delete, or count query.
+         * @return this builder, to allow for chaining.
+         */
+        public Builder withExpectedCount(int count) {
+            if (mType != TYPE_DELETE && mType != TYPE_UPDATE && mType != TYPE_COUNT) {
+                throw new IllegalArgumentException(
+                        "only deletes, updates and counts can have expected counts");
+            }
+            mExpectedCount = count;
+            return this;
+        }
+    }
+}
diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java
new file mode 100644
index 0000000..5d188ef
--- /dev/null
+++ b/core/java/android/content/ContentProviderResult.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.net.Uri;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Contains the result of the application of a {@link ContentProviderOperation}. It is guaranteed
+ * to have exactly one of {@link #uri} or {@link #count} set.
+ */
+public class ContentProviderResult implements Parcelable {
+    public final Uri uri;
+    public final Integer count;
+
+    public ContentProviderResult(Uri uri) {
+        if (uri == null) throw new IllegalArgumentException("uri must not be null");
+        this.uri = uri;
+        this.count = null;
+    }
+
+    public ContentProviderResult(int count) {
+        this.count = count;
+        this.uri = null;
+    }
+
+    public ContentProviderResult(Parcel source) {
+        int type = source.readInt();
+        if (type == 1) {
+            count = source.readInt();
+            uri = null;
+        } else {
+            count = null;
+            uri = Uri.CREATOR.createFromParcel(source);
+        }
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        if (uri == null) {
+            dest.writeInt(1);
+            dest.writeInt(count);
+        } else {
+            dest.writeInt(2);
+            uri.writeToParcel(dest, 0);
+        }
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<ContentProviderResult> CREATOR =
+            new Creator<ContentProviderResult>() {
+        public ContentProviderResult createFromParcel(Parcel source) {
+            return new ContentProviderResult(source);
+        }
+
+        public ContentProviderResult[] newArray(int size) {
+            return new ContentProviderResult[size];
+        }
+    };
+
+    public String toString() {
+        if (uri != null) {
+            return "ContentProviderResult(uri=" + uri.toString() + ")";
+        }
+        return "ContentProviderResult(count=" + count + ")";
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 74144fc..98ed098 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -30,6 +30,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.text.TextUtils;
+import android.accounts.Account;
 import android.util.Config;
 import android.util.Log;
 
@@ -40,15 +41,25 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.List;
+import java.util.ArrayList;
 
 
 /**
  * This class provides applications access to the content model.
  */
 public abstract class ContentResolver {
-    public final static String SYNC_EXTRAS_ACCOUNT = "account";
+    /**
+     * @deprecated instead use
+     * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
+     */
+    public static final String SYNC_EXTRAS_ACCOUNT = "account";
     public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
+    /**
+     * @deprecated instead use
+     * {@link #SYNC_EXTRAS_MANUAL}
+     */
     public static final String SYNC_EXTRAS_FORCE = "force";
+    public static final String SYNC_EXTRAS_MANUAL = "force";
     public static final String SYNC_EXTRAS_UPLOAD = "upload";
     public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
     public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
@@ -88,7 +99,35 @@
      * in the cursor is the same.
      */
     public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
-    
+
+    /** @hide */
+    public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
+    /** @hide */
+    public static final int SYNC_ERROR_AUTHENTICATION = 2;
+    /** @hide */
+    public static final int SYNC_ERROR_IO = 3;
+    /** @hide */
+    public static final int SYNC_ERROR_PARSE = 4;
+    /** @hide */
+    public static final int SYNC_ERROR_CONFLICT = 5;
+    /** @hide */
+    public static final int SYNC_ERROR_TOO_MANY_DELETIONS = 6;
+    /** @hide */
+    public static final int SYNC_ERROR_TOO_MANY_RETRIES = 7;
+    /** @hide */
+    public static final int SYNC_ERROR_INTERNAL = 8;
+
+    /** @hide */
+    public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0;
+    /** @hide */
+    public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1;
+    /** @hide */
+    public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2;
+    /** @hide */
+    public static final int SYNC_OBSERVER_TYPE_STATUS = 1<<3;
+    /** @hide */
+    public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
+
     public ContentResolver(Context context) {
         mContext = context;
     }
@@ -166,6 +205,87 @@
     }
 
     /**
+     * EntityIterator wrapper that releases the associated ContentProviderClient when the
+     * iterator is closed.
+     */
+    private class EntityIteratorWrapper implements EntityIterator {
+        private final EntityIterator mInner;
+        private final ContentProviderClient mClient;
+        private volatile boolean mClientReleased;
+
+        EntityIteratorWrapper(EntityIterator inner, ContentProviderClient client) {
+            mInner = inner;
+            mClient = client;
+            mClientReleased = false;
+        }
+
+        public boolean hasNext() throws RemoteException {
+            if (mClientReleased) {
+                throw new IllegalStateException("this iterator is already closed");
+            }
+            return mInner.hasNext();
+        }
+
+        public Entity next() throws RemoteException {
+            if (mClientReleased) {
+                throw new IllegalStateException("this iterator is already closed");
+            }
+            return mInner.next();
+        }
+
+        public void close() {
+            mClient.release();
+            mInner.close();
+            mClientReleased = true;
+        }
+
+        protected void finalize() throws Throwable {
+            if (!mClientReleased) {
+                mClient.release();
+            }
+            super.finalize();
+        }
+    }
+
+    /**
+     * Query the given URI, returning an {@link EntityIterator} over the result set.
+     *
+     * @param uri The URI, using the content:// scheme, for the content to
+     *         retrieve.
+     * @param selection A filter declaring which rows to return, formatted as an
+     *         SQL WHERE clause (excluding the WHERE itself). Passing null will
+     *         return all rows for the given URI.
+     * @param selectionArgs You may include ?s in selection, which will be
+     *         replaced by the values from selectionArgs, in the order that they
+     *         appear in the selection. The values will be bound as Strings.
+     * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
+     *         clause (excluding the ORDER BY itself). Passing null will use the
+     *         default sort order, which may be unordered.
+     * @return An EntityIterator object
+     * @throws RemoteException thrown if a RemoteException is encountered while attempting
+     *   to communicate with a remote provider.
+     * @throws IllegalArgumentException thrown if there is no provider that matches the uri
+     */
+    public final EntityIterator queryEntities(Uri uri,
+            String selection, String[] selectionArgs, String sortOrder) throws RemoteException {
+        ContentProviderClient provider = acquireContentProviderClient(uri);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown URL " + uri);
+        }
+        try {
+            EntityIterator entityIterator =
+                    provider.queryEntities(uri, selection, selectionArgs, sortOrder);
+            return new EntityIteratorWrapper(entityIterator, provider);
+        } catch(RuntimeException e) {
+            provider.release();
+            throw e;
+        } catch(RemoteException e) {
+            provider.release();
+            throw e;
+        }
+    }
+
+    /**
      * Open a stream on to the content associated with a content URI.  If there
      * is no data associated with the URI, FileNotFoundException is thrown.
      *
@@ -485,6 +605,36 @@
     }
 
     /**
+     * Applies each of the {@link ContentProviderOperation} objects and returns an array
+     * of their results. Passes through OperationApplicationException, which may be thrown
+     * by the call to {@link ContentProviderOperation#apply}.
+     * If all the applications succeed then a {@link ContentProviderResult} array with the
+     * same number of elements as the operations will be returned. It is implementation-specific
+     * how many, if any, operations will have been successfully applied if a call to
+     * apply results in a {@link OperationApplicationException}.
+     * @param authority the authority of the ContentProvider to which this batch should be applied
+     * @param operations the operations to apply
+     * @return the results of the applications
+     * @throws OperationApplicationException thrown if an application fails.
+     * See {@link ContentProviderOperation#apply} for more information.
+     * @throws RemoteException thrown if a RemoteException is encountered while attempting
+     *   to communicate with a remote provider.
+     */
+    public ContentProviderResult[] applyBatch(String authority,
+            ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException {
+        ContentProviderClient provider = acquireContentProviderClient(authority);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown authority " + authority);
+        }
+        try {
+            return provider.applyBatch(operations);
+        } finally {
+            provider.release();
+        }
+    }
+
+    /**
      * Inserts multiple rows into a table at the given URL.
      *
      * This function make no guarantees about the atomicity of the insertions.
@@ -592,6 +742,46 @@
     }
 
     /**
+     * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
+     * that services the content at uri, starting the provider if necessary. Returns
+     * null if there is no provider associated wih the uri. The caller must indicate that they are
+     * done with the provider by calling {@link ContentProviderClient#release} which will allow
+     * the system to release the provider it it determines that there is no other reason for
+     * keeping it active.
+     * @param uri specifies which provider should be acquired
+     * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
+     * that services the content at uri or null if there isn't one.
+     */
+    public final ContentProviderClient acquireContentProviderClient(Uri uri) {
+        IContentProvider provider = acquireProvider(uri);
+        if (provider != null) {
+            return new ContentProviderClient(this, provider);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
+     * with the authority of name, starting the provider if necessary. Returns
+     * null if there is no provider associated wih the uri. The caller must indicate that they are
+     * done with the provider by calling {@link ContentProviderClient#release} which will allow
+     * the system to release the provider it it determines that there is no other reason for
+     * keeping it active.
+     * @param name specifies which provider should be acquired
+     * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
+     * with the authority of name or null if there isn't one.
+     */
+    public final ContentProviderClient acquireContentProviderClient(String name) {
+        IContentProvider provider = acquireProvider(name);
+        if (provider != null) {
+            return new ContentProviderClient(this, provider);
+        }
+
+        return null;
+    }
+
+    /**
      * Register an observer class that gets callbacks when data identified by a
      * given content URI changes.
      *
@@ -676,11 +866,42 @@
      *
      * @param uri the uri of the provider to sync or null to sync all providers.
      * @param extras any extras to pass to the SyncAdapter.
+     * @deprecated instead use
+     * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
      */
     public void startSync(Uri uri, Bundle extras) {
+        Account account = null;
+        if (extras != null) {
+            String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT);
+            if (!TextUtils.isEmpty(accountName)) {
+                account = new Account(accountName, "com.google.GAIA");
+            }
+            extras.remove(SYNC_EXTRAS_ACCOUNT);
+        }
+        requestSync(account, uri != null ? uri.getAuthority() : null, extras);
+    }
+
+    /**
+     * Start an asynchronous sync operation. If you want to monitor the progress
+     * of the sync you may register a SyncObserver. Only values of the following
+     * types may be used in the extras bundle:
+     * <ul>
+     * <li>Integer</li>
+     * <li>Long</li>
+     * <li>Boolean</li>
+     * <li>Float</li>
+     * <li>Double</li>
+     * <li>String</li>
+     * </ul>
+     *
+     * @param account which account should be synced
+     * @param authority which authority should be synced
+     * @param extras any extras to pass to the SyncAdapter.
+     */
+    public static void requestSync(Account account, String authority, Bundle extras) {
         validateSyncExtrasBundle(extras);
         try {
-            getContentService().startSync(uri, extras);
+            getContentService().requestSync(account, authority, extras);
         } catch (RemoteException e) {
         }
     }
@@ -694,6 +915,7 @@
      * <li>Float</li>
      * <li>Double</li>
      * <li>String</li>
+     * <li>Account</li>
      * <li>null</li>
      * </ul>
      * @param extras the Bundle to check
@@ -709,6 +931,7 @@
                 if (value instanceof Float) continue;
                 if (value instanceof Double) continue;
                 if (value instanceof String) continue;
+                if (value instanceof Account) continue;
                 throw new IllegalArgumentException("unexpected value type: "
                         + value.getClass().getName());
             }
@@ -719,13 +942,186 @@
         }
     }
 
+    /**
+     * Cancel any active or pending syncs that match the Uri. If the uri is null then
+     * all syncs will be canceled.
+     *
+     * @param uri the uri of the provider to sync or null to sync all providers.
+     * @deprecated instead use {@link #cancelSync(android.accounts.Account, String)}
+     */
     public void cancelSync(Uri uri) {
+        cancelSync(null /* all accounts */, uri != null ? uri.getAuthority() : null);
+    }
+
+    /**
+     * Cancel any active or pending syncs that match account and authority. The account and
+     * authority can each independently be set to null, which means that syncs with any account
+     * or authority, respectively, will match.
+     *
+     * @param account filters the syncs that match by this account
+     * @param authority filters the syncs that match by this authority
+     */
+    public static void cancelSync(Account account, String authority) {
         try {
-            getContentService().cancelSync(uri);
+            getContentService().cancelSync(account, authority);
         } catch (RemoteException e) {
         }
     }
 
+    /**
+     * Get information about the SyncAdapters that are known to the system.
+     * @return an array of SyncAdapters that have registered with the system
+     */
+    public static SyncAdapterType[] getSyncAdapterTypes() {
+        try {
+            return getContentService().getSyncAdapterTypes();
+        } catch (RemoteException e) {
+            throw new RuntimeException("the ContentService should always be reachable", e);
+        }
+    }
+
+    /**
+     * Check if the provider should be synced when a network tickle is received
+     *
+     * @param account the account whose setting we are querying
+     * @param authority the provider whose setting we are querying
+     * @return true if the provider should be synced when a network tickle is received
+     */
+    public static boolean getSyncAutomatically(Account account, String authority) {
+        try {
+            return getContentService().getSyncAutomatically(account, authority);
+        } catch (RemoteException e) {
+            throw new RuntimeException("the ContentService should always be reachable", e);
+        }
+    }
+
+    /**
+     * Set whether or not the provider is synced when it receives a network tickle.
+     *
+     * @param account the account whose setting we are querying
+     * @param authority the provider whose behavior is being controlled
+     * @param sync true if the provider should be synced when tickles are received for it
+     */
+    public static void setSyncAutomatically(Account account, String authority, boolean sync) {
+        try {
+            getContentService().setSyncAutomatically(account, authority, sync);
+        } catch (RemoteException e) {
+            // exception ignored; if this is thrown then it means the runtime is in the midst of
+            // being restarted
+        }
+    }
+
+    /**
+     * Gets the master auto-sync setting that applies to all the providers and accounts.
+     * If this is false then the per-provider auto-sync setting is ignored.
+     *
+     * @return the master auto-sync setting that applies to all the providers and accounts
+     */
+    public static boolean getMasterSyncAutomatically() {
+        try {
+            return getContentService().getMasterSyncAutomatically();
+        } catch (RemoteException e) {
+            throw new RuntimeException("the ContentService should always be reachable", e);
+        }
+    }
+
+    /**
+     * Sets the master auto-sync setting that applies to all the providers and accounts.
+     * If this is false then the per-provider auto-sync setting is ignored.
+     *
+     * @param sync the master auto-sync setting that applies to all the providers and accounts
+     */
+    public static void setMasterSyncAutomatically(boolean sync) {
+        try {
+            getContentService().setMasterSyncAutomatically(sync);
+        } catch (RemoteException e) {
+            // exception ignored; if this is thrown then it means the runtime is in the midst of
+            // being restarted
+        }
+    }
+
+    /**
+     * Returns true if there is currently a sync operation for the given
+     * account or authority in the pending list, or actively being processed.
+     * @param account the account whose setting we are querying
+     * @param authority the provider whose behavior is being queried
+     * @return true if a sync is active for the given account or authority.
+     */
+    public static boolean isSyncActive(Account account, String authority) {
+        try {
+            return getContentService().isSyncActive(account, authority);
+        } catch (RemoteException e) {
+            throw new RuntimeException("the ContentService should always be reachable", e);
+        }
+    }
+
+    /**
+     * If a sync is active returns the information about it, otherwise returns false.
+     * @return the ActiveSyncInfo for the currently active sync or null if one is not active.
+     * @hide
+     */
+    public static ActiveSyncInfo getActiveSync() {
+        try {
+            return getContentService().getActiveSync();
+        } catch (RemoteException e) {
+            throw new RuntimeException("the ContentService should always be reachable", e);
+        }
+    }
+
+    /**
+     * Returns the status that matches the authority. If there are multiples accounts for
+     * the authority, the one with the latest "lastSuccessTime" status is returned.
+     * @param account the account whose setting we are querying
+     * @param authority the provider whose behavior is being queried
+     * @return the SyncStatusInfo for the authority, or null if none exists
+     * @hide
+     */
+    public static SyncStatusInfo getSyncStatus(Account account, String authority) {
+        try {
+            return getContentService().getSyncStatus(account, authority);
+        } catch (RemoteException e) {
+            throw new RuntimeException("the ContentService should always be reachable", e);
+        }
+    }
+
+    /**
+     * Return true if the pending status is true of any matching authorities.
+     * @param account the account whose setting we are querying
+     * @param authority the provider whose behavior is being queried
+     * @return true if there is a pending sync with the matching account and authority
+     */
+    public static boolean isSyncPending(Account account, String authority) {
+        try {
+            return getContentService().isSyncPending(account, authority);
+        } catch (RemoteException e) {
+            throw new RuntimeException("the ContentService should always be reachable", e);
+        }
+    }
+
+    public static Object addStatusChangeListener(int mask, final SyncStatusObserver callback) {
+        try {
+            ISyncStatusObserver.Stub observer = new ISyncStatusObserver.Stub() {
+                public void onStatusChanged(int which) throws RemoteException {
+                    callback.onStatusChanged(which);
+                }
+            };
+            getContentService().addStatusChangeListener(mask, observer);
+            return observer;
+        } catch (RemoteException e) {
+            throw new RuntimeException("the ContentService should always be reachable", e);
+        }
+    }
+
+    public static void removeStatusChangeListener(Object handle) {
+        try {
+            getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle);
+        } catch (RemoteException e) {
+            // exception ignored; if this is thrown then it means the runtime is in the midst of
+            // being restarted
+        }
+    }
+
+
     private final class CursorWrapperInner extends CursorWrapper {
         private IContentProvider mContentProvider;
         public static final String TAG="CursorWrapperInner";
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index 6cd2c54..7a1ad2b 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.accounts.Account;
 import android.database.IContentObserver;
 import android.database.sqlite.SQLiteException;
 import android.net.Uri;
@@ -160,7 +161,9 @@
             }
             if (syncToNetwork) {
                 SyncManager syncManager = getSyncManager();
-                if (syncManager != null) syncManager.scheduleLocalSync(uri);
+                if (syncManager != null) {
+                    syncManager.scheduleLocalSync(null /* all accounts */, uri.getAuthority());
+                }
             }
         } finally {
             restoreCallingIdentity(identityToken);
@@ -186,14 +189,16 @@
         }
     }
 
-    public void startSync(Uri url, Bundle extras) {
+    public void requestSync(Account account, String authority, Bundle extras) {
         ContentResolver.validateSyncExtrasBundle(extras);
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
         long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
-            if (syncManager != null) syncManager.startSync(url, extras);
+            if (syncManager != null) {
+                syncManager.scheduleSync(account, authority, extras, 0 /* no delay */);
+            }
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -201,34 +206,50 @@
 
     /**
      * Clear all scheduled sync operations that match the uri and cancel the active sync
-     * if it matches the uri. If the uri is null, clear all scheduled syncs and cancel
-     * the active one, if there is one.
-     * @param uri Filter on the sync operations to cancel, or all if null.
+     * if they match the authority and account, if they are present.
+     * @param account filter the pending and active syncs to cancel using this account
+     * @param authority filter the pending and active syncs to cancel using this authority
      */
-    public void cancelSync(Uri uri) {
+    public void cancelSync(Account account, String authority) {
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
         long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
-                syncManager.clearScheduledSyncOperations(uri);
-                syncManager.cancelActiveSync(uri);
+                syncManager.clearScheduledSyncOperations(account, authority);
+                syncManager.cancelActiveSync(account, authority);
             }
         } finally {
             restoreCallingIdentity(identityToken);
         }
     }
 
-    public boolean getSyncProviderAutomatically(String providerName) {
+    /**
+     * Get information about the SyncAdapters that are known to the system.
+     * @return an array of SyncAdapters that have registered with the system
+     */
+    public SyncAdapterType[] getSyncAdapterTypes() {
+        // This makes it so that future permission checks will be in the context of this
+        // process rather than the caller's process. We will restore this before returning.
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            return syncManager.getSyncAdapterTypes();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+    
+    public boolean getSyncAutomatically(Account account, String providerName) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
         long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
-                return syncManager.getSyncStorageEngine().getSyncProviderAutomatically(
-                        null, providerName);
+                return syncManager.getSyncStorageEngine().getSyncAutomatically(
+                        account, providerName);
             }
         } finally {
             restoreCallingIdentity(identityToken);
@@ -236,29 +257,29 @@
         return false;
     }
 
-    public void setSyncProviderAutomatically(String providerName, boolean sync) {
+    public void setSyncAutomatically(Account account, String providerName, boolean sync) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                 "no permission to write the sync settings");
         long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
-                syncManager.getSyncStorageEngine().setSyncProviderAutomatically(
-                        null, providerName, sync);
+                syncManager.getSyncStorageEngine().setSyncAutomatically(
+                        account, providerName, sync);
             }
         } finally {
             restoreCallingIdentity(identityToken);
         }
     }
 
-    public boolean getListenForNetworkTickles() {
+    public boolean getMasterSyncAutomatically() {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
         long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
-                return syncManager.getSyncStorageEngine().getListenForNetworkTickles();
+                return syncManager.getSyncStorageEngine().getMasterSyncAutomatically();
             }
         } finally {
             restoreCallingIdentity(identityToken);
@@ -266,21 +287,21 @@
         return false;
     }
     
-    public void setListenForNetworkTickles(boolean flag) {
+    public void setMasterSyncAutomatically(boolean flag) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                 "no permission to write the sync settings");
         long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
-                syncManager.getSyncStorageEngine().setListenForNetworkTickles(flag);
+                syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag);
             }
         } finally {
             restoreCallingIdentity(identityToken);
         }
     }
 
-    public boolean isSyncActive(String account, String authority) {
+    public boolean isSyncActive(Account account, String authority) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                 "no permission to read the sync stats");
         long identityToken = clearCallingIdentity();
@@ -311,7 +332,7 @@
         return null;
     }
     
-    public SyncStatusInfo getStatusByAuthority(String authority) {
+    public SyncStatusInfo getSyncStatus(Account account, String authority) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                 "no permission to read the sync stats");
         long identityToken = clearCallingIdentity();
@@ -327,15 +348,14 @@
         return null;
     }
     
-    public boolean isAuthorityPending(String account, String authority) {
+    public boolean isSyncPending(Account account, String authority) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                 "no permission to read the sync stats");
         long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
-                return syncManager.getSyncStorageEngine().isAuthorityPending(
-                        account, authority);
+                return syncManager.getSyncStorageEngine().isSyncPending(account, authority);
             }
         } finally {
             restoreCallingIdentity(identityToken);
@@ -348,8 +368,7 @@
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
-                syncManager.getSyncStorageEngine().addStatusChangeListener(
-                        mask, callback);
+                syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
             }
         } finally {
             restoreCallingIdentity(identityToken);
@@ -361,8 +380,7 @@
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
-                syncManager.getSyncStorageEngine().removeStatusChangeListener(
-                        callback);
+                syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
             }
         } finally {
             restoreCallingIdentity(identityToken);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b0396f6..6c8bafc 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1110,6 +1110,16 @@
     public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
     /**
      * Use with {@link #getSystemService} to retrieve a
+     * {@link android.accounts.AccountManager} for receiving intents at a
+     * time of your choosing.
+     * TODO STOPSHIP perform a final review of the the account apis before shipping
+     *
+     * @see #getSystemService
+     * @see android.accounts.AccountManager
+     */
+    public static final String ACCOUNT_SERVICE = "account";
+    /**
+     * Use with {@link #getSystemService} to retrieve a
      * {@link android.app.ActivityManager} for interacting with the global
      * system state.
      *
diff --git a/core/java/android/content/Entity.aidl b/core/java/android/content/Entity.aidl
new file mode 100644
index 0000000..fb201f3
--- /dev/null
+++ b/core/java/android/content/Entity.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/content/Entity.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+package android.content;
+
+parcelable Entity;
diff --git a/core/java/android/content/Entity.java b/core/java/android/content/Entity.java
new file mode 100644
index 0000000..325dce5
--- /dev/null
+++ b/core/java/android/content/Entity.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.net.Uri;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Objects that pass through the ContentProvider and ContentResolver's methods that deal with
+ * Entities must implement this abstract base class and thus themselves be Parcelable.
+ */
+public final class Entity implements Parcelable {
+    final private ContentValues mValues;
+    final private ArrayList<NamedContentValues> mSubValues;
+
+    public Entity(ContentValues values) {
+        mValues = values;
+        mSubValues = new ArrayList<NamedContentValues>();
+    }
+
+    public ContentValues getEntityValues() {
+        return mValues;
+    }
+
+    public ArrayList<NamedContentValues> getSubValues() {
+        return mSubValues;
+    }
+
+    public void addSubValue(Uri uri, ContentValues values) {
+        mSubValues.add(new Entity.NamedContentValues(uri, values));
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        mValues.writeToParcel(dest, 0);
+        dest.writeInt(mSubValues.size());
+        for (NamedContentValues value : mSubValues) {
+            value.uri.writeToParcel(dest, 0);
+            value.values.writeToParcel(dest, 0);
+        }
+    }
+
+    private Entity(Parcel source) {
+        mValues = ContentValues.CREATOR.createFromParcel(source);
+        final int numValues = source.readInt();
+        mSubValues = new ArrayList<NamedContentValues>(numValues);
+        for (int i = 0; i < numValues; i++) {
+            final Uri uri = Uri.CREATOR.createFromParcel(source);
+            final ContentValues values = ContentValues.CREATOR.createFromParcel(source);
+            mSubValues.add(new NamedContentValues(uri, values));
+        }
+    }
+
+    public static final Creator<Entity> CREATOR = new Creator<Entity>() {
+        public Entity createFromParcel(Parcel source) {
+            return new Entity(source);
+        }
+
+        public Entity[] newArray(int size) {
+            return new Entity[size];
+        }
+    };
+
+    public static class NamedContentValues {
+        public final Uri uri;
+        public final ContentValues values;
+
+        public NamedContentValues(Uri uri, ContentValues values) {
+            this.uri = uri;
+            this.values = values;
+        }
+    }
+
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("Entity: ").append(getEntityValues());
+        for (Entity.NamedContentValues namedValue : getSubValues()) {
+            sb.append("\n  ").append(namedValue.uri);
+            sb.append("\n  -> ").append(namedValue.values);
+        }
+        return sb.toString();
+    }
+}
diff --git a/core/java/android/content/EntityIterator.java b/core/java/android/content/EntityIterator.java
new file mode 100644
index 0000000..5e5f14c
--- /dev/null
+++ b/core/java/android/content/EntityIterator.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.RemoteException;
+
+public interface EntityIterator {
+    /**
+     * Returns whether there are more elements to iterate, i.e. whether the
+     * iterator is positioned in front of an element.
+     *
+     * @return {@code true} if there are more elements, {@code false} otherwise.
+     * @see #next
+     * @since Android 1.0
+     */
+    public boolean hasNext() throws RemoteException;
+
+    /**
+     * Returns the next object in the iteration, i.e. returns the element in
+     * front of the iterator and advances the iterator by one position.
+     *
+     * @return the next object.
+     * @throws java.util.NoSuchElementException
+     *             if there are no more elements.
+     * @see #hasNext
+     * @since Android 1.0
+     */
+    public Entity next() throws RemoteException;
+
+    /**
+     * Indicates that this iterator is no longer needed and that any associated resources
+     * may be released (such as a SQLite cursor).
+     */
+    public void close();
+}
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 0606956..7e5aba5 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -28,6 +28,7 @@
 import android.os.ParcelFileDescriptor;
 
 import java.io.FileNotFoundException;
+import java.util.ArrayList;
 
 /**
  * The ipc interface to talk to a content provider.
@@ -43,19 +44,25 @@
             CursorWindow window) throws RemoteException;
     public Cursor query(Uri url, String[] projection, String selection,
             String[] selectionArgs, String sortOrder) throws RemoteException;
+    public EntityIterator queryEntities(Uri url, String selection,
+            String[] selectionArgs, String sortOrder)
+            throws RemoteException;
     public String getType(Uri url) throws RemoteException;
     public Uri insert(Uri url, ContentValues initialValues)
             throws RemoteException;
     public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException;
+    public Uri insertEntity(Uri uri, Entity entities) throws RemoteException;
     public int delete(Uri url, String selection, String[] selectionArgs)
             throws RemoteException;
     public int update(Uri url, ContentValues values, String selection,
             String[] selectionArgs) throws RemoteException;
+    public int updateEntity(Uri uri, Entity entity) throws RemoteException;
     public ParcelFileDescriptor openFile(Uri url, String mode)
             throws RemoteException, FileNotFoundException;
     public AssetFileDescriptor openAssetFile(Uri url, String mode)
             throws RemoteException, FileNotFoundException;
-    public ISyncAdapter getSyncAdapter() throws RemoteException;
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException;
 
     /* IPC constants */
     static final String descriptor = "android.content.IContentProvider";
@@ -65,8 +72,11 @@
     static final int INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
     static final int DELETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3;
     static final int UPDATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 9;
-    static final int GET_SYNC_ADAPTER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 10;
     static final int BULK_INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 12;
     static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13;
     static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14;
+    static final int INSERT_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 16;
+    static final int UPDATE_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 17;
+    static final int QUERY_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 18;
+    static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19;
 }
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index 8617d949..658a5bc 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -16,8 +16,10 @@
 
 package android.content;
 
+import android.accounts.Account;
 import android.content.ActiveSyncInfo;
 import android.content.ISyncStatusObserver;
+import android.content.SyncAdapterType;
 import android.content.SyncStatusInfo;
 import android.net.Uri;
 import android.os.Bundle;
@@ -34,15 +36,15 @@
     void notifyChange(in Uri uri, IContentObserver observer,
             boolean observerWantsSelfNotifications, boolean syncToNetwork);
 
-    void startSync(in Uri url, in Bundle extras);
-    void cancelSync(in Uri uri);
+    void requestSync(in Account account, String authority, in Bundle extras);
+    void cancelSync(in Account account, String authority);
     
     /**
      * Check if the provider should be synced when a network tickle is received
      * @param providerName the provider whose setting we are querying
      * @return true of the provider should be synced when a network tickle is received
      */
-    boolean getSyncProviderAutomatically(String providerName);
+    boolean getSyncAutomatically(in Account account, String providerName);
 
     /**
      * Set whether or not the provider is synced when it receives a network tickle.
@@ -50,32 +52,38 @@
      * @param providerName the provider whose behavior is being controlled
      * @param sync true if the provider should be synced when tickles are received for it
      */
-    void setSyncProviderAutomatically(String providerName, boolean sync);
+    void setSyncAutomatically(in Account account, String providerName, boolean sync);
 
-    void setListenForNetworkTickles(boolean flag);
+    void setMasterSyncAutomatically(boolean flag);
 
-    boolean getListenForNetworkTickles();
+    boolean getMasterSyncAutomatically();
     
     /**
      * Returns true if there is currently a sync operation for the given
      * account or authority in the pending list, or actively being processed.
      */
-    boolean isSyncActive(String account, String authority);
+    boolean isSyncActive(in Account account, String authority);
     
     ActiveSyncInfo getActiveSync();
     
     /**
+     * Returns the types of the SyncAdapters that are registered with the system.
+     * @return Returns the types of the SyncAdapters that are registered with the system.
+     */
+    SyncAdapterType[] getSyncAdapterTypes();
+
+    /**
      * Returns the status that matches the authority. If there are multiples accounts for
      * the authority, the one with the latest "lastSuccessTime" status is returned.
      * @param authority the authority whose row should be selected
      * @return the SyncStatusInfo for the authority, or null if none exists
      */
-    SyncStatusInfo getStatusByAuthority(String authority);
+    SyncStatusInfo getSyncStatus(in Account account, String authority);
 
     /**
      * Return true if the pending status is true of any matching authorities.
      */
-    boolean isAuthorityPending(String account, String authority);
+    boolean isSyncPending(in Account account, String authority);
     
     void addStatusChangeListener(int mask, ISyncStatusObserver callback);
     
diff --git a/core/java/android/content/IEntityIterator.java b/core/java/android/content/IEntityIterator.java
new file mode 100644
index 0000000..1c478b3
--- /dev/null
+++ b/core/java/android/content/IEntityIterator.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * ICPC interface methods for an iterator over Entity objects.
+ * @hide
+ */
+public interface IEntityIterator extends IInterface {
+    /** Local-side IPC implementation stub class. */
+    public static abstract class Stub extends Binder implements IEntityIterator {
+        private static final String TAG = "IEntityIterator";
+        private static final java.lang.String DESCRIPTOR = "android.content.IEntityIterator";
+
+        /** Construct the stub at attach it to the interface. */
+        public Stub() {
+            this.attachInterface(this, DESCRIPTOR);
+        }
+        /**
+         * Cast an IBinder object into an IEntityIterator interface,
+         * generating a proxy if needed.
+         */
+        public static IEntityIterator asInterface(IBinder obj) {
+            if ((obj==null)) {
+                return null;
+            }
+            IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
+            if (((iin!=null)&&(iin instanceof IEntityIterator))) {
+                return ((IEntityIterator)iin);
+            }
+            return new IEntityIterator.Stub.Proxy(obj);
+        }
+
+        public IBinder asBinder() {
+            return this;
+        }
+
+        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                throws RemoteException {
+            switch (code) {
+                case INTERFACE_TRANSACTION:
+                {
+                    reply.writeString(DESCRIPTOR);
+                    return true;
+                }
+
+                case TRANSACTION_hasNext:
+                {
+                    data.enforceInterface(DESCRIPTOR);
+                    boolean _result;
+                    try {
+                        _result = this.hasNext();
+                    } catch (Exception e) {
+                        Log.e(TAG, "caught exception in hasNext()", e);
+                        reply.writeException(e);
+                        return true;
+                    }
+                    reply.writeNoException();
+                    reply.writeInt(((_result)?(1):(0)));
+                    return true;
+                }
+
+                case TRANSACTION_next:
+                {
+                    data.enforceInterface(DESCRIPTOR);
+                    Entity entity;
+                    try {
+                        entity = this.next();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "caught exception in next()", e);
+                        reply.writeException(e);
+                        return true;
+                    }
+                    reply.writeNoException();
+                    entity.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+                    return true;
+                }
+
+                case TRANSACTION_close:
+                {
+                    data.enforceInterface(DESCRIPTOR);
+                    try {
+                        this.close();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "caught exception in close()", e);
+                        reply.writeException(e);
+                        return true;
+                    }
+                    reply.writeNoException();
+                    return true;
+                }
+            }
+            return super.onTransact(code, data, reply, flags);
+        }
+
+        private static class Proxy implements IEntityIterator {
+            private IBinder mRemote;
+            Proxy(IBinder remote) {
+                mRemote = remote;
+            }
+            public IBinder asBinder() {
+                return mRemote;
+            }
+            public java.lang.String getInterfaceDescriptor() {
+                return DESCRIPTOR;
+            }
+            public boolean hasNext() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                boolean _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_hasNext, _data, _reply, 0);
+                    _reply.readException();
+                    _result = (0!=_reply.readInt());
+                }
+                finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            public Entity next() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_next, _data, _reply, 0);
+                    _reply.readException();
+                    return Entity.CREATOR.createFromParcel(_reply);
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            public void close() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_close, _data, _reply, 0);
+                    _reply.readException();
+                }
+                finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+        }
+        static final int TRANSACTION_hasNext = (IBinder.FIRST_CALL_TRANSACTION + 0);
+        static final int TRANSACTION_next = (IBinder.FIRST_CALL_TRANSACTION + 1);
+        static final int TRANSACTION_close = (IBinder.FIRST_CALL_TRANSACTION + 2);
+    }
+    public boolean hasNext() throws RemoteException;
+    public Entity next() throws RemoteException;
+    public void close() throws RemoteException;
+}
diff --git a/core/java/android/content/ISyncAdapter.aidl b/core/java/android/content/ISyncAdapter.aidl
index 671188c..4660527 100644
--- a/core/java/android/content/ISyncAdapter.aidl
+++ b/core/java/android/content/ISyncAdapter.aidl
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.accounts.Account;
 import android.os.Bundle;
 import android.content.ISyncContext;
 
@@ -30,14 +31,17 @@
      *
      * @param syncContext the ISyncContext used to indicate the progress of the sync. When
      *   the sync is finished (successfully or not) ISyncContext.onFinished() must be called.
+     * @param authority the authority that should be synced
      * @param account the account that should be synced
      * @param extras SyncAdapter-specific parameters
      */
-    void startSync(ISyncContext syncContext, String account, in Bundle extras);
+    void startSync(ISyncContext syncContext, String authority,
+      in Account account, in Bundle extras);
 
     /**
      * Cancel the most recently initiated sync. Due to race conditions, this may arrive
      * after the ISyncContext.onFinished() for that sync was called.
+     * @param syncContext the ISyncContext that was passed to {@link #startSync}
      */
-    void cancelSync();
+    void cancelSync(ISyncContext syncContext);
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6b723bc..a57084c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1304,7 +1304,7 @@
      * that wait until power is available to trigger.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
+    public static final String ACTION_POWER_CONNECTED = "android.intent.action.POWER_CONNECTED";
     /**
      * Broadcast Action:  External power has been removed from the device.
      * This is intended for applications that wish to register specifically to this notification.
@@ -1313,7 +1313,8 @@
      * that wait until power is available to trigger.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED";
+    public static final String ACTION_POWER_DISCONNECTED =
+            "android.intent.action.POWER_DISCONNECTED";
     /**
      * Broadcast Action:  Device is shutting down.
      * This is broadcast when the device is being shut down (completely turned
@@ -1586,6 +1587,20 @@
             "android.intent.action.REBOOT";
 
     /**
+     * Broadcast Action: a remote intent is to be broadcasted.
+     *
+     * A remote intent is used for remote RPC between devices. The remote intent
+     * is serialized and sent from one device to another device. The receiving
+     * device parses the remote intent and broadcasts it. Note that anyone can
+     * broadcast a remote intent. However, if the intent receiver of the remote intent
+     * does not trust intent broadcasts from arbitrary intent senders, it should require
+     * the sender to hold certain permissions so only trusted sender's broadcast will be
+     * let through.
+     */
+    public static final String ACTION_REMOTE_INTENT =
+            "android.intent.action.REMOTE_INTENT";
+
+    /**
      * @hide
      * TODO: This will be unhidden in a later CL.
      * Broadcast Action: The TextToSpeech synthesizer has completed processing 
@@ -1853,6 +1868,13 @@
     public static final String EXTRA_INSTALLER_PACKAGE_NAME
             = "android.intent.extra.INSTALLER_PACKAGE_NAME";
 
+    /**
+     * Used in the extra field in the remote intent. It's astring token passed with the
+     * remote intent.
+     */
+    public static final String EXTRA_REMOTE_INTENT_TOKEN =
+            "android.intent.extra.remote_intent_token";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Intent flags (see mFlags variable).
diff --git a/core/java/android/content/OperationApplicationException.java b/core/java/android/content/OperationApplicationException.java
new file mode 100644
index 0000000..d4101bf
--- /dev/null
+++ b/core/java/android/content/OperationApplicationException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+/**
+ * Thrown when an application of a {@link ContentProviderOperation} fails due the specified
+ * constraints.
+ */
+public class OperationApplicationException extends Exception {
+    public OperationApplicationException() {
+        super();
+    }
+    public OperationApplicationException(String message) {
+        super(message);
+    }
+    public OperationApplicationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+    public OperationApplicationException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/core/java/android/content/SyncAdapter.java b/core/java/android/content/SyncAdapter.java
index 7826e50..1d5ade1 100644
--- a/core/java/android/content/SyncAdapter.java
+++ b/core/java/android/content/SyncAdapter.java
@@ -18,6 +18,7 @@
 
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.accounts.Account;
 
 /**
  * @hide
@@ -29,12 +30,12 @@
     public static final int LOG_SYNC_DETAILS = 2743;
 
     class Transport extends ISyncAdapter.Stub {
-        public void startSync(ISyncContext syncContext, String account,
+        public void startSync(ISyncContext syncContext, String authority, Account account,
                 Bundle extras) throws RemoteException {
             SyncAdapter.this.startSync(new SyncContext(syncContext), account, extras);
         }
 
-        public void cancelSync() throws RemoteException {
+        public void cancelSync(ISyncContext syncContext) throws RemoteException {
             SyncAdapter.this.cancelSync();
         }
     }
@@ -42,9 +43,9 @@
     Transport mTransport = new Transport();
 
     /**
-     * Get the Transport object.  (note this is package private).
+     * Get the Transport object.
      */
-    final ISyncAdapter getISyncAdapter()
+    public final ISyncAdapter getISyncAdapter()
     {
         return mTransport;
     }
@@ -59,7 +60,7 @@
      * @param account the account that should be synced
      * @param extras SyncAdapter-specific parameters
      */
-    public abstract void startSync(SyncContext syncContext, String account, Bundle extras);
+    public abstract void startSync(SyncContext syncContext, Account account, Bundle extras);
 
     /**
      * Cancel the most recently initiated sync. Due to race conditions, this may arrive
diff --git a/include/utils/executablepath.h b/core/java/android/content/SyncAdapterType.aidl
similarity index 65%
copy from include/utils/executablepath.h
copy to core/java/android/content/SyncAdapterType.aidl
index c979432..e67841f 100644
--- a/include/utils/executablepath.h
+++ b/core/java/android/content/SyncAdapterType.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UTILS_EXECUTABLEPATH_H
-#define _UTILS_EXECUTABLEPATH_H
+package android.content;
 
-#include <limits.h>
+parcelable SyncAdapterType;
 
-// returns the path to this executable
-#if __cplusplus
-extern "C"
-#endif
-void executablepath(char s[PATH_MAX]);
-
-#endif // _UTILS_EXECUTABLEPATH_H
diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java
new file mode 100644
index 0000000..5a96003
--- /dev/null
+++ b/core/java/android/content/SyncAdapterType.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.text.TextUtils;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Value type that represents a SyncAdapterType. This object overrides {@link #equals} and
+ * {@link #hashCode}, making it suitable for use as the key of a {@link java.util.Map}
+ */
+public class SyncAdapterType implements Parcelable {
+    public final String authority;
+    public final String accountType;
+
+    public SyncAdapterType(String authority, String accountType) {
+        if (TextUtils.isEmpty(authority)) {
+            throw new IllegalArgumentException("the authority must not be empty: " + authority);
+        }
+        if (TextUtils.isEmpty(accountType)) {
+            throw new IllegalArgumentException("the accountType must not be empty: " + accountType);
+        }
+        this.authority = authority;
+        this.accountType = accountType;
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (!(o instanceof SyncAdapterType)) return false;
+        final SyncAdapterType other = (SyncAdapterType)o;
+        return authority.equals(other.authority) && accountType.equals(other.accountType);
+    }
+
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + authority.hashCode();
+        result = 31 * result + accountType.hashCode();
+        return result;
+    }
+
+    public String toString() {
+        return "SyncAdapterType {name=" + authority + ", type=" + accountType + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(authority);
+        dest.writeString(accountType);
+    }
+
+    public SyncAdapterType(Parcel source) {
+        this(source.readString(), source.readString());
+    }
+
+    public static final Creator<SyncAdapterType> CREATOR = new Creator<SyncAdapterType>() {
+        public SyncAdapterType createFromParcel(Parcel source) {
+            return new SyncAdapterType(source);
+        }
+
+        public SyncAdapterType[] newArray(int size) {
+            return new SyncAdapterType[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java
new file mode 100644
index 0000000..ce47d76
--- /dev/null
+++ b/core/java/android/content/SyncAdaptersCache.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.content.pm.RegisteredServicesCache;
+import android.content.res.TypedArray;
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * A cache of services that export the {@link android.content.ISyncAdapter} interface.
+ * @hide
+ */
+/* package private */ class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> {
+    private static final String TAG = "Account";
+
+    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";
+
+    SyncAdaptersCache(Context context) {
+        super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME);
+    }
+
+    public SyncAdapterType parseServiceAttributes(String packageName, AttributeSet attrs) {
+        TypedArray sa = mContext.getResources().obtainAttributes(attrs,
+                com.android.internal.R.styleable.SyncAdapter);
+        try {
+            final String authority =
+                    sa.getString(com.android.internal.R.styleable.SyncAdapter_contentAuthority);
+            final String accountType =
+                    sa.getString(com.android.internal.R.styleable.SyncAdapter_accountType);
+            if (authority == null || accountType == null) {
+                return null;
+            }
+            return new SyncAdapterType(authority, accountType);
+        } finally {
+            sa.recycle();
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 4d2cce8..f73b394 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -21,8 +21,9 @@
 import com.android.internal.R;
 import com.android.internal.util.ArrayUtils;
 
-import android.accounts.AccountMonitor;
-import android.accounts.AccountMonitorListener;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.OnAccountsUpdatedListener;
 import android.app.AlarmManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -30,11 +31,10 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
+import android.content.pm.RegisteredServicesCache;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
-import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -48,7 +48,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.format.Time;
 import android.util.Config;
@@ -72,11 +71,12 @@
 import java.util.Map;
 import java.util.PriorityQueue;
 import java.util.Random;
+import java.util.Collection;
 
 /**
  * @hide
  */
-class SyncManager {
+class SyncManager implements OnAccountsUpdatedListener {
     private static final String TAG = "SyncManager";
 
     // used during dumping of the Sync history
@@ -117,14 +117,11 @@
     private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock";
 
     private Context mContext;
-    private ContentResolver mContentResolver;
 
     private String mStatusText = "";
     private long mHeartbeatTime = 0;
 
-    private AccountMonitor mAccountMonitor;
-
-    private volatile String[] mAccounts = null;
+    private volatile Account[] mAccounts = null;
 
     volatile private PowerManager.WakeLock mSyncWakeLock;
     volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
@@ -151,17 +148,18 @@
     private final PendingIntent mSyncAlarmIntent;
     private final PendingIntent mSyncPollAlarmIntent;
 
+    private final SyncAdaptersCache mSyncAdapters;
+
     private BroadcastReceiver mStorageIntentReceiver =
             new BroadcastReceiver() {
                 public void onReceive(Context context, Intent intent) {
-                    ensureContentResolver();
                     String action = intent.getAction();
                     if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
                             Log.v(TAG, "Internal storage is low.");
                         }
                         mStorageIsLow = true;
-                        cancelActiveSync(null /* no url */);
+                        cancelActiveSync(null /* any account */, null /* any authority */);
                     } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
                             Log.v(TAG, "Internal storage is ok.");
@@ -172,6 +170,43 @@
                 }
             };
 
+    private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            if (!mFactoryTest) {
+                AccountManager.get(mContext).addOnAccountsUpdatedListener(SyncManager.this,
+                        mSyncHandler, true /* updateImmediately */);
+            }
+        }
+    };
+
+    public void onAccountsUpdated(Account[] accounts) {
+        final boolean hadAccountsAlready = mAccounts != null;
+        mAccounts = accounts;
+
+        // if a sync is in progress yet it is no longer in the accounts list,
+        // cancel it
+        ActiveSyncContext activeSyncContext = mActiveSyncContext;
+        if (activeSyncContext != null) {
+            if (!ArrayUtils.contains(accounts, activeSyncContext.mSyncOperation.account)) {
+                Log.d(TAG, "canceling sync since the account has been removed");
+                sendSyncFinishedOrCanceledMessage(activeSyncContext,
+                        null /* no result since this is a cancel */);
+            }
+        }
+
+        // we must do this since we don't bother scheduling alarms when
+        // the accounts are not set yet
+        sendCheckAlarmsMessage();
+
+        mSyncStorageEngine.doDatabaseCleanup(accounts);
+
+        if (hadAccountsAlready && accounts.length > 0) {
+            // request a sync so that if the password was changed we will
+            // retry any sync that failed when it was wrong
+            scheduleSync(null, null, null, 0 /* no delay */);
+        }
+    }
+
     private BroadcastReceiver mConnectivityIntentReceiver =
             new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
@@ -229,7 +264,11 @@
 
     private static final String SYNCMANAGER_PREFS_FILENAME = "/data/system/syncmanager.prefs";
 
+    private final boolean mFactoryTest;
+
     public SyncManager(Context context, boolean factoryTest) {
+        mFactoryTest = factoryTest;
+
         // Initialize the SyncStorageEngine first, before registering observers
         // and creating threads and so on; it may fail if the disk is full.
         SyncStorageEngine.init(context);
@@ -244,6 +283,8 @@
 
         mPackageManager = null;
 
+        mSyncAdapters = new SyncAdaptersCache(mContext);
+
         mSyncAlarmIntent = PendingIntent.getBroadcast(
                 mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0);
 
@@ -253,6 +294,9 @@
         IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
         context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
 
+        intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+        context.registerReceiver(mBootCompletedReceiver, intentFilter);
+
         intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
         intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
         context.registerReceiver(mStorageIntentReceiver, intentFilter);
@@ -282,48 +326,12 @@
         mHandleAlarmWakeLock.setReferenceCounted(false);
 
         mSyncStorageEngine.addStatusChangeListener(
-                SyncStorageEngine.CHANGE_SETTINGS, new ISyncStatusObserver.Stub() {
+                ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() {
             public void onStatusChanged(int which) {
                 // force the sync loop to run if the settings change
                 sendCheckAlarmsMessage();
             }
         });
-        
-        if (!factoryTest) {
-            AccountMonitorListener listener = new AccountMonitorListener() {
-                public void onAccountsUpdated(String[] accounts) {
-                    final boolean hadAccountsAlready = mAccounts != null;
-                    // copy the accounts into a new array and change mAccounts to point to it
-                    String[] newAccounts = new String[accounts.length];
-                    System.arraycopy(accounts, 0, newAccounts, 0, accounts.length);
-                    mAccounts = newAccounts;
-
-                    // if a sync is in progress yet it is no longer in the accounts list, cancel it
-                    ActiveSyncContext activeSyncContext = mActiveSyncContext;
-                    if (activeSyncContext != null) {
-                        if (!ArrayUtils.contains(newAccounts,
-                                activeSyncContext.mSyncOperation.account)) {
-                            Log.d(TAG, "canceling sync since the account has been removed");
-                            sendSyncFinishedOrCanceledMessage(activeSyncContext,
-                                    null /* no result since this is a cancel */);
-                        }
-                    }
-
-                    // we must do this since we don't bother scheduling alarms when
-                    // the accounts are not set yet
-                    sendCheckAlarmsMessage();
-
-                    mSyncStorageEngine.doDatabaseCleanup(accounts);
-
-                    if (hadAccountsAlready && mAccounts.length > 0) {
-                        // request a sync so that if the password was changed we will retry any sync
-                        // that failed when it was wrong
-                        startSync(null /* all providers */, null /* no extras */);
-                    }
-                }
-            };
-            mAccountMonitor = new AccountMonitor(context, listener);
-        }
     }
 
     private synchronized void initializeSyncPoll() {
@@ -397,7 +405,8 @@
         scheduleSyncPollAlarm(nextRelativePollTimeMs);
 
         // perform a poll
-        scheduleSync(null /* sync all syncable providers */, new Bundle(), 0 /* no delay */);
+        scheduleSync(null /* sync all syncable accounts */, null /* sync all syncable providers */,
+                new Bundle(), 0 /* no delay */);
     }
 
     private void writeSyncPollTime(long when) {
@@ -452,19 +461,13 @@
         return mSyncStorageEngine;
     }
     
-    private void ensureContentResolver() {
-        if (mContentResolver == null) {
-            mContentResolver = mContext.getContentResolver();
-        }
-    }
-
     private void ensureAlarmService() {
         if (mAlarmService == null) {
             mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
         }
     }
 
-    public String getSyncingAccount() {
+    public Account getSyncingAccount() {
         ActiveSyncContext activeSyncContext = mActiveSyncContext;
         return (activeSyncContext != null) ? activeSyncContext.mSyncOperation.account : null;
     }
@@ -499,20 +502,21 @@
      *
      * <p>You'll start getting callbacks after this.
      *
-     * @param url The Uri of a specific provider to be synced, or
-     *          null to sync all providers.
+     * @param requestedAccount the account to sync, may be null to signify all accounts
+     * @param requestedAuthority the authority to sync, may be null to indicate all authorities
      * @param extras a Map of SyncAdapter-specific information to control
 *          syncs of a specific provider. Can be null. Is ignored
 *          if the url is null.
      * @param delay how many milliseconds in the future to wait before performing this
-     *   sync. -1 means to make this the next sync to perform.
      */
-    public void scheduleSync(Uri url, Bundle extras, long delay) {
+    public void scheduleSync(Account requestedAccount, String requestedAuthority,
+            Bundle extras, long delay) {
         boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
         if (isLoggable) {
             Log.v(TAG, "scheduleSync:"
                     + " delay " + delay
-                    + ", url " + ((url == null) ? "(null)" : url)
+                    + ", account " + requestedAccount
+                    + ", authority " + requestedAuthority
                     + ", extras " + ((extras == null) ? "(null)" : extras));
         }
 
@@ -535,10 +539,9 @@
             delay = -1; // this means schedule at the front of the queue
         }
 
-        String[] accounts;
-        String accountFromExtras = extras.getString(ContentResolver.SYNC_EXTRAS_ACCOUNT);
-        if (!TextUtils.isEmpty(accountFromExtras)) {
-            accounts = new String[]{accountFromExtras};
+        Account[] accounts;
+        if (requestedAccount != null) {
+            accounts = new Account[]{requestedAccount};
         } else {
             // if the accounts aren't configured yet then we can't support an account-less
             // sync request
@@ -560,14 +563,14 @@
         }
 
         final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
-        final boolean force = extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false);
+        final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
 
         int source;
         if (uploadOnly) {
             source = SyncStorageEngine.SOURCE_LOCAL;
-        } else if (force) {
+        } else if (manualSync) {
             source = SyncStorageEngine.SOURCE_USER;
-        } else if (url == null) {
+        } else if (requestedAuthority == null) {
             source = SyncStorageEngine.SOURCE_POLL;
         } else {
             // this isn't strictly server, since arbitrary callers can (and do) request
@@ -575,20 +578,33 @@
             source = SyncStorageEngine.SOURCE_SERVER;
         }
 
-        List<String> names = new ArrayList<String>();
-        List<ProviderInfo> providers = new ArrayList<ProviderInfo>();
-        populateProvidersList(url, names, providers);
+        // Compile a list of authorities that have sync adapters.
+        // For each authority sync each account that matches a sync adapter.
+        final HashSet<String> syncableAuthorities = new HashSet<String>();
+        for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter :
+                mSyncAdapters.getAllServices()) {
+            syncableAuthorities.add(syncAdapter.type.authority);
+        }
 
-        final int numProviders = providers.size();
-        for (int i = 0; i < numProviders; i++) {
-            if (!providers.get(i).isSyncable) continue;
-            final String name = names.get(i);
-            for (String account : accounts) {
-                scheduleSyncOperation(new SyncOperation(account, source, name, extras, delay));
-                // TODO: remove this when Calendar supports multiple accounts. Until then
-                // pretend that only the first account exists when syncing calendar.
-                if ("calendar".equals(name)) {
-                    break;
+        // if the url was specified then replace the list of authorities with just this authority
+        // or clear it if this authority isn't syncable
+        if (requestedAuthority != null) {
+            final boolean isSyncable = syncableAuthorities.contains(requestedAuthority);
+            syncableAuthorities.clear();
+            if (isSyncable) syncableAuthorities.add(requestedAuthority);
+        }
+
+        for (String authority : syncableAuthorities) {
+            for (Account account : accounts) {
+                if (mSyncAdapters.getServiceInfo(new SyncAdapterType(authority, account.mType))
+                        != null) {
+                    scheduleSyncOperation(
+                            new SyncOperation(account, source, authority, extras, delay));
+                    // TODO: remove this when Calendar supports multiple accounts. Until then
+                    // pretend that only the first account exists when syncing calendar.
+                    if ("calendar".equals(authority)) {
+                        break;
+                    }
                 }
             }
         }
@@ -598,36 +614,10 @@
         mStatusText = message;
     }
 
-    private void populateProvidersList(Uri url, List<String> names, List<ProviderInfo> providers) {
-        try {
-            final IPackageManager packageManager = getPackageManager();
-            if (url == null) {
-                packageManager.querySyncProviders(names, providers);
-            } else {
-                final String authority = url.getAuthority();
-                ProviderInfo info = packageManager.resolveContentProvider(url.getAuthority(), 0);
-                if (info != null) {
-                    // only set this provider if the requested authority is the primary authority
-                    String[] providerNames = info.authority.split(";");
-                    if (url.getAuthority().equals(providerNames[0])) {
-                        names.add(authority);
-                        providers.add(info);
-                    }
-                }
-            }
-        } catch (RemoteException ex) {
-            // we should really never get this, but if we do then clear the lists, which
-            // will result in the dropping of the sync request
-            Log.e(TAG, "error trying to get the ProviderInfo for " + url, ex);
-            names.clear();
-            providers.clear();
-        }
-    }
-
-    public void scheduleLocalSync(Uri url) {
+    public void scheduleLocalSync(Account account, String authority) {
         final Bundle extras = new Bundle();
         extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
-        scheduleSync(url, extras, LOCAL_SYNC_DELAY);
+        scheduleSync(account, authority, extras, LOCAL_SYNC_DELAY);
     }
 
     private IPackageManager getPackageManager() {
@@ -641,18 +631,16 @@
         return mPackageManager;
     }
 
-    /**
-     * Initiate a sync for this given URL, or pass null for a full sync.
-     *
-     * <p>You'll start getting callbacks after this.
-     *
-     * @param url The Uri of a specific provider to be synced, or
-     *          null to sync all providers.
-     * @param extras a Map of SyncAdapter specific information to control
-     *          syncs of a specific provider. Can be null. Is ignored
-     */
-    public void startSync(Uri url, Bundle extras) {
-        scheduleSync(url, extras, 0 /* no delay */);
+    public SyncAdapterType[] getSyncAdapterTypes() {
+        final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos =
+                mSyncAdapters.getAllServices();
+        SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()];
+        int i = 0;
+        for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
+            types[i] = serviceInfo.type;
+            ++i;
+        }
+        return types;
     }
 
     public void updateHeartbeatTime() {
@@ -721,8 +709,7 @@
         }
 
         // Cap the delay
-        ensureContentResolver();
-        long maxSyncRetryTimeInSeconds = Settings.Gservices.getLong(mContentResolver,
+        long maxSyncRetryTimeInSeconds = Settings.Gservices.getLong(mContext.getContentResolver(),
                 Settings.Gservices.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
                 DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS);
         if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) {
@@ -736,17 +723,22 @@
     }
 
     /**
-     * Cancel the active sync if it matches the uri. The uri corresponds to the one passed
-     * in to startSync().
-     * @param uri If non-null, the active sync is only canceled if it matches the uri.
-     *   If null, any active sync is canceled.
+     * Cancel the active sync if it matches the authority and account.
+     * @param account limit the cancelations to syncs with this account, if non-null
+     * @param authority limit the cancelations to syncs with this authority, if non-null
      */
-    public void cancelActiveSync(Uri uri) {
+    public void cancelActiveSync(Account account, String authority) {
         ActiveSyncContext activeSyncContext = mActiveSyncContext;
         if (activeSyncContext != null) {
-            // if a Uri was specified then only cancel the sync if it matches the the uri
-            if (uri != null) {
-                if (!uri.getAuthority().equals(activeSyncContext.mSyncOperation.authority)) {
+            // if an authority was specified then only cancel the sync if it matches
+            if (account != null) {
+                if (!account.equals(activeSyncContext.mSyncOperation.account)) {
+                    return;
+                }
+            }
+            // if an account was specified then only cancel the sync if it matches
+            if (authority != null) {
+                if (!authority.equals(activeSyncContext.mSyncOperation.authority)) {
                     return;
                 }
             }
@@ -798,14 +790,13 @@
     }
 
     /**
-     * Remove any scheduled sync operations that match uri. The uri corresponds to the one passed
-     * in to startSync().
-     * @param uri If non-null, only operations that match the uri are cleared.
-     *   If null, all operations are cleared.
+     * Remove scheduled sync operations.
+     * @param account limit the removals to operations with this account, if non-null
+     * @param authority limit the removals to operations with this authority, if non-null
      */
-    public void clearScheduledSyncOperations(Uri uri) {
+    public void clearScheduledSyncOperations(Account account, String authority) {
         synchronized (mSyncQueue) {
-            mSyncQueue.clear(null, uri != null ? uri.getAuthority() : null);
+            mSyncQueue.clear(account, authority);
         }
     }
 
@@ -857,7 +848,7 @@
      * Value type that represents a sync operation.
      */
     static class SyncOperation implements Comparable {
-        final String account;
+        final Account account;
         int syncSource;
         String authority;
         Bundle extras;
@@ -866,7 +857,7 @@
         long delay;
         SyncStorageEngine.PendingOperation pendingOperation;
 
-        SyncOperation(String account, int source, String authority, Bundle extras, long delay) {
+        SyncOperation(Account account, int source, String authority, Bundle extras, long delay) {
             this.account = account;
             this.syncSource = source;
             this.authority = authority;
@@ -937,21 +928,19 @@
     /**
      * @hide
      */
-    class ActiveSyncContext extends ISyncContext.Stub {
+    class ActiveSyncContext extends ISyncContext.Stub implements ServiceConnection {
         final SyncOperation mSyncOperation;
         final long mHistoryRowId;
-        final IContentProvider mContentProvider;
-        final ISyncAdapter mSyncAdapter;
+        ISyncAdapter mSyncAdapter;
         final long mStartTime;
         long mTimeoutStartTime;
 
-        public ActiveSyncContext(SyncOperation syncOperation, IContentProvider contentProvider,
-                ISyncAdapter syncAdapter, long historyRowId) {
+        public ActiveSyncContext(SyncOperation syncOperation,
+                long historyRowId) {
             super();
             mSyncOperation = syncOperation;
             mHistoryRowId = historyRowId;
-            mContentProvider = contentProvider;
-            mSyncAdapter = syncAdapter;
+            mSyncAdapter = null;
             mStartTime = SystemClock.elapsedRealtime();
             mTimeoutStartTime = mStartTime;
         }
@@ -977,6 +966,37 @@
                     .append(", syncOperation ").append(mSyncOperation);
         }
 
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Message msg = mSyncHandler.obtainMessage();
+            msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED;
+            msg.obj = new ServiceConnectionData(this, ISyncAdapter.Stub.asInterface(service));
+            mSyncHandler.sendMessage(msg);
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            Message msg = mSyncHandler.obtainMessage();
+            msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED;
+            msg.obj = new ServiceConnectionData(this, null);
+            mSyncHandler.sendMessage(msg);
+        }
+
+        boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this);
+            }
+            Intent intent = new Intent();
+            intent.setAction("android.content.SyncAdapter");
+            intent.setComponent(info.componentName);
+            return mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
+        }
+
+        void unBindFromSyncAdapter() {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.d(TAG, "unBindFromSyncAdapter: connection " + this);
+            }
+            mContext.unbindService(this);
+        }
+
         @Override
         public String toString() {
             StringBuilder sb = new StringBuilder();
@@ -991,6 +1011,12 @@
         if (isSyncEnabled()) {
             dumpSyncHistory(pw, sb);
         }
+
+        pw.println();
+        pw.println("SyncAdapters:");
+        for (RegisteredServicesCache.ServiceInfo info : mSyncAdapters.getAllServices()) {
+            pw.println("  " + info);
+        }
     }
 
     static String formatTime(long time) {
@@ -1004,7 +1030,7 @@
         pw.print("data connected: "); pw.println(mDataConnectionIsConnected);
         pw.print("memory low: "); pw.println(mStorageIsLow);
 
-        final String[] accounts = mAccounts;
+        final Account[] accounts = mAccounts;
         pw.print("accounts: ");
         if (accounts != null) {
             pw.println(accounts.length);
@@ -1068,7 +1094,8 @@
             for (int i=0; i<N; i++) {
                 SyncStorageEngine.PendingOperation op = ops.get(i);
                 pw.print("  #"); pw.print(i); pw.print(": account=");
-                pw.print(op.account); pw.print(" authority=");
+                pw.print(op.account.mName); pw.print(":");
+                pw.print(op.account.mType); pw.print(" authority=");
                 pw.println(op.authority);
                 if (op.extras != null && op.extras.size() > 0) {
                     sb.setLength(0);
@@ -1078,7 +1105,7 @@
             }
         }
 
-        HashSet<String> processedAccounts = new HashSet<String>();
+        HashSet<Account> processedAccounts = new HashSet<Account>();
         ArrayList<SyncStatusInfo> statuses
                 = mSyncStorageEngine.getSyncStatus();
         if (statuses != null && statuses.size() > 0) {
@@ -1090,7 +1117,7 @@
                 SyncStorageEngine.AuthorityInfo authority
                         = mSyncStorageEngine.getAuthority(status.authorityId);
                 if (authority != null) {
-                    String curAccount = authority.account;
+                    Account curAccount = authority.account;
                     
                     if (processedAccounts.contains(curAccount)) {
                         continue;
@@ -1098,8 +1125,9 @@
                     
                     processedAccounts.add(curAccount);
                     
-                    pw.print("  Account "); pw.print(authority.account);
-                    pw.println(":");
+                    pw.print("  Account "); pw.print(authority.account.mName);
+                            pw.print(" "); pw.print(authority.account.mType);
+                            pw.println(":");
                     for (int j=i; j<N; j++) {
                         status = statuses.get(j);
                         authority = mSyncStorageEngine.getAuthority(status.authorityId);
@@ -1219,9 +1247,15 @@
                 SyncStorageEngine.AuthorityInfo authority
                         = mSyncStorageEngine.getAuthority(item.authorityId);
                 pw.print("  #"); pw.print(i+1); pw.print(": ");
-                        pw.print(authority != null ? authority.account : "<no account>");
-                        pw.print(" ");
-                        pw.print(authority != null ? authority.authority : "<no account>");
+                        if (authority != null) {
+                            pw.print(authority.account.mName);
+                            pw.print(":");
+                            pw.print(authority.account.mType);
+                            pw.print(" ");
+                            pw.print(authority.authority);
+                        } else {
+                            pw.print("<no account>");
+                        }
                 Time time = new Time();
                 time.set(item.eventTime);
                 pw.print(" "); pw.print(SyncStorageEngine.SOURCES[item.source]);
@@ -1278,6 +1312,15 @@
         }
     }
 
+    class ServiceConnectionData {
+        public final ActiveSyncContext activeSyncContext;
+        public final ISyncAdapter syncAdapter;
+        ServiceConnectionData(ActiveSyncContext activeSyncContext, ISyncAdapter syncAdapter) {
+            this.activeSyncContext = activeSyncContext;
+            this.syncAdapter = syncAdapter;
+        }
+    }
+
     /**
      * Handles SyncOperation Messages that are posted to the associated
      * HandlerThread.
@@ -1287,6 +1330,8 @@
         private static final int MESSAGE_SYNC_FINISHED = 1;
         private static final int MESSAGE_SYNC_ALARM = 2;
         private static final int MESSAGE_CHECK_ALARMS = 3;
+        private static final int MESSAGE_SERVICE_CONNECTED = 4;
+        private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
 
         public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
         private Long mAlarmScheduleTime = null;
@@ -1301,7 +1346,7 @@
          */
         class SyncNotificationInfo {
             // only valid if isActive is true
-            public String account;
+            public Account account;
 
             // only valid if isActive is true
             public String authority;
@@ -1358,6 +1403,53 @@
                         runStateIdle();
                         break;
 
+                    case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
+                        ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
+                                    + msgData.activeSyncContext
+                                    + " active is " + mActiveSyncContext);
+                        }
+                        // check that this isn't an old message
+                        if (mActiveSyncContext == msgData.activeSyncContext) {
+                            runBoundToSyncAdapter(msgData.syncAdapter);
+                        }
+                        break;
+                    }
+
+                    case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
+                        ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
+                                    + msgData.activeSyncContext
+                                    + " active is " + mActiveSyncContext);
+                        }
+                        // check that this isn't an old message
+                        if (mActiveSyncContext == msgData.activeSyncContext) {
+                            // cancel the sync if we have a syncadapter, which means one is
+                            // outstanding
+                            if (mActiveSyncContext.mSyncAdapter != null) {
+                                try {
+                                    mActiveSyncContext.mSyncAdapter.cancelSync(mActiveSyncContext);
+                                } catch (RemoteException e) {
+                                    // we don't need to retry this in this case
+                                }
+                            }
+
+                            // pretend that the sync failed with an IOException,
+                            // which is a soft error
+                            SyncResult syncResult = new SyncResult();
+                            syncResult.stats.numIoExceptions++;
+                            runSyncFinishedOrCanceled(syncResult);
+
+                            // since we are no longer syncing, check if it is time to start a new
+                            // sync
+                            runStateIdle();
+                        }
+
+                        break;
+                    }
+
                     case SyncHandler.MESSAGE_SYNC_ALARM: {
                         boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
                         if (isLoggable) {
@@ -1456,7 +1548,7 @@
 
             // If the accounts aren't known yet then we aren't ready to run. We will be kicked
             // when the account lookup request does complete.
-            String[] accounts = mAccounts;
+            Account[] accounts = mAccounts;
             if (accounts == null) {
                 if (isLoggable) {
                     Log.v(TAG, "runStateIdle: accounts not known, skipping");
@@ -1468,14 +1560,14 @@
             // Otherwise consume SyncOperations from the head of the SyncQueue until one is
             // found that is runnable (not disabled, etc). If that one is ready to run then
             // start it, otherwise just get out.
-            SyncOperation syncOperation;
+            SyncOperation op;
             final ConnectivityManager connManager = (ConnectivityManager)
                     mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-            final boolean backgroundDataSetting = connManager.getBackgroundDataSetting();
+            final boolean backgroundDataUsageAllowed = connManager.getBackgroundDataSetting();
             synchronized (mSyncQueue) {
                 while (true) {
-                    syncOperation = mSyncQueue.head();
-                    if (syncOperation == null) {
+                    op = mSyncQueue.head();
+                    if (op == null) {
                         if (isLoggable) {
                             Log.v(TAG, "runStateIdle: no more sync operations, returning");
                         }
@@ -1485,39 +1577,40 @@
                     // Sync is disabled, drop this operation.
                     if (!isSyncEnabled()) {
                         if (isLoggable) {
-                            Log.v(TAG, "runStateIdle: sync disabled, dropping " + syncOperation);
+                            Log.v(TAG, "runStateIdle: sync disabled, dropping " + op);
                         }
                         mSyncQueue.popHead();
                         continue;
                     }
 
-                    // skip the sync if it isn't a force and the settings are off for this provider
-                    final boolean force = syncOperation.extras.getBoolean(
-                            ContentResolver.SYNC_EXTRAS_FORCE, false);
-                    if (!force && (!backgroundDataSetting
-                            || !mSyncStorageEngine.getListenForNetworkTickles()
-                            || !mSyncStorageEngine.getSyncProviderAutomatically(
-                                    null, syncOperation.authority))) {
+                    // skip the sync if it isn't manual and auto sync is disabled
+                    final boolean manualSync = op.extras.getBoolean(
+                            ContentResolver.SYNC_EXTRAS_MANUAL, false);
+                    final boolean syncAutomatically =
+                            mSyncStorageEngine.getSyncAutomatically(op.account, op.authority)
+                                    || mSyncStorageEngine.getMasterSyncAutomatically();
+                    boolean syncAllowed =
+                            manualSync || (backgroundDataUsageAllowed && syncAutomatically);
+                    if (!syncAllowed) {
                         if (isLoggable) {
-                            Log.v(TAG, "runStateIdle: sync off, dropping " + syncOperation);
+                            Log.v(TAG, "runStateIdle: sync off, dropping " + op);
                         }
                         mSyncQueue.popHead();
                         continue;
                     }
 
                     // skip the sync if the account of this operation no longer exists
-                    if (!ArrayUtils.contains(accounts, syncOperation.account)) {
+                    if (!ArrayUtils.contains(accounts, op.account)) {
                         mSyncQueue.popHead();
                         if (isLoggable) {
-                            Log.v(TAG, "runStateIdle: account not present, dropping "
-                                    + syncOperation);
+                            Log.v(TAG, "runStateIdle: account not present, dropping " + op);
                         }
                         continue;
                     }
 
                     // go ahead and try to sync this syncOperation
                     if (isLoggable) {
-                        Log.v(TAG, "runStateIdle: found sync candidate: " + syncOperation);
+                        Log.v(TAG, "runStateIdle: found sync candidate: " + op);
                     }
                     break;
                 }
@@ -1525,11 +1618,10 @@
                 // If the first SyncOperation isn't ready to run schedule a wakeup and
                 // get out.
                 final long now = SystemClock.elapsedRealtime();
-                if (syncOperation.earliestRunTime > now) {
+                if (op.earliestRunTime > now) {
                     if (Log.isLoggable(TAG, Log.DEBUG)) {
                         Log.d(TAG, "runStateIdle: the time is " + now + " yet the next "
-                                + "sync operation is for " + syncOperation.earliestRunTime
-                                + ": " + syncOperation);
+                                + "sync operation is for " + op.earliestRunTime + ": " + op);
                     }
                     return;
                 }
@@ -1537,72 +1629,73 @@
                 // We will do this sync. Remove it from the queue and run it outside of the
                 // synchronized block.
                 if (isLoggable) {
-                    Log.v(TAG, "runStateIdle: we are going to sync " + syncOperation);
+                    Log.v(TAG, "runStateIdle: we are going to sync " + op);
                 }
                 mSyncQueue.popHead();
             }
 
-            String providerName = syncOperation.authority;
-            ensureContentResolver();
-            IContentProvider contentProvider;
-
-            // acquire the provider and update the sync history
-            try {
-                contentProvider = mContentResolver.acquireProvider(providerName);
-                if (contentProvider == null) {
-                    Log.e(TAG, "Provider " + providerName + " doesn't exist");
-                    return;
+            // connect to the sync adapter
+            SyncAdapterType syncAdapterType = new SyncAdapterType(op.authority,
+                    op.account.mType);
+            RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
+                    mSyncAdapters.getServiceInfo(syncAdapterType);
+            if (syncAdapterInfo == null) {
+                if (Config.LOGD) {
+                    Log.d(TAG, "can't find a sync adapter for " + syncAdapterType);
                 }
-                if (contentProvider.getSyncAdapter() == null) {
-                    Log.e(TAG, "Provider " + providerName + " isn't syncable, " + contentProvider);
-                    return;
-                }
-            } catch (RemoteException remoteExc) {
-                Log.e(TAG, "Caught a RemoteException while preparing for sync, rescheduling "
-                        + syncOperation, remoteExc);
-                rescheduleWithDelay(syncOperation);
-                return;
-            } catch (RuntimeException exc) {
-                Log.e(TAG, "Caught a RuntimeException while validating sync of " + providerName,
-                        exc);
+                runStateIdle();
                 return;
             }
 
-            final long historyRowId = insertStartSyncEvent(syncOperation);
-
-            try {
-                ISyncAdapter syncAdapter = contentProvider.getSyncAdapter();
-                ActiveSyncContext activeSyncContext = new ActiveSyncContext(syncOperation,
-                        contentProvider, syncAdapter, historyRowId);
-                mSyncWakeLock.acquire();
-                if (Log.isLoggable(TAG, Log.DEBUG)) {
-                    Log.d(TAG, "starting sync of " + syncOperation);
-                }
-                syncAdapter.startSync(activeSyncContext, syncOperation.account,
-                        syncOperation.extras);
-                mActiveSyncContext = activeSyncContext;
+            ActiveSyncContext activeSyncContext =
+                    new ActiveSyncContext(op, insertStartSyncEvent(op));
+            mActiveSyncContext = activeSyncContext;
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "runStateIdle: setting mActiveSyncContext to " + mActiveSyncContext);
+            }
+            mSyncStorageEngine.setActiveSync(mActiveSyncContext);
+            if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo)) {
+                Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo);
+                mActiveSyncContext = null;
                 mSyncStorageEngine.setActiveSync(mActiveSyncContext);
+                runStateIdle();
+                return;
+            }
+
+            mSyncWakeLock.acquire();
+            // no need to schedule an alarm, as that will be done by our caller.
+
+            // the next step will occur when we get either a timeout or a
+            // MESSAGE_SERVICE_CONNECTED or MESSAGE_SERVICE_DISCONNECTED message
+        }
+
+        private void runBoundToSyncAdapter(ISyncAdapter syncAdapter) {
+            mActiveSyncContext.mSyncAdapter = syncAdapter;
+            final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
+            try {
+                syncAdapter.startSync(mActiveSyncContext, syncOperation.authority,
+                        syncOperation.account, syncOperation.extras);
             } catch (RemoteException remoteExc) {
                 if (Config.LOGD) {
                     Log.d(TAG, "runStateIdle: caught a RemoteException, rescheduling", remoteExc);
                 }
+                mActiveSyncContext.unBindFromSyncAdapter();
                 mActiveSyncContext = null;
                 mSyncStorageEngine.setActiveSync(mActiveSyncContext);
                 rescheduleWithDelay(syncOperation);
             } catch (RuntimeException exc) {
+                mActiveSyncContext.unBindFromSyncAdapter();
                 mActiveSyncContext = null;
                 mSyncStorageEngine.setActiveSync(mActiveSyncContext);
                 Log.e(TAG, "Caught a RuntimeException while starting the sync " + syncOperation,
                         exc);
             }
-
-            // no need to schedule an alarm, as that will be done by our caller.
         }
 
         private void runSyncFinishedOrCanceled(SyncResult syncResult) {
             boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
             if (isLoggable) Log.v(TAG, "runSyncFinishedOrCanceled");
-            ActiveSyncContext activeSyncContext = mActiveSyncContext;
+            final ActiveSyncContext activeSyncContext = mActiveSyncContext;
             mActiveSyncContext = null;
             mSyncStorageEngine.setActiveSync(mActiveSyncContext);
 
@@ -1642,10 +1735,12 @@
                     Log.v(TAG, "runSyncFinishedOrCanceled: is a cancel: operation "
                             + syncOperation);
                 }
-                try {
-                    activeSyncContext.mSyncAdapter.cancelSync();
-                } catch (RemoteException e) {
-                    // we don't need to retry this in this case
+                if (activeSyncContext.mSyncAdapter != null) {
+                    try {
+                        activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext);
+                    } catch (RemoteException e) {
+                        // we don't need to retry this in this case
+                    }
                 }
                 historyMessage = SyncStorageEngine.MESG_CANCELED;
                 downstreamActivity = 0;
@@ -1655,7 +1750,7 @@
             stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
                     upstreamActivity, downstreamActivity, elapsedTime);
 
-            mContentResolver.releaseProvider(activeSyncContext.mContentProvider);
+            activeSyncContext.unBindFromSyncAdapter();
 
             if (syncResult != null && syncResult.tooManyDeletions) {
                 installHandleTooManyDeletesNotification(syncOperation.account,
@@ -1683,21 +1778,21 @@
          */
         private int syncResultToErrorNumber(SyncResult syncResult) {
             if (syncResult.syncAlreadyInProgress)
-                return SyncStorageEngine.ERROR_SYNC_ALREADY_IN_PROGRESS;
+                return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
             if (syncResult.stats.numAuthExceptions > 0)
-                return SyncStorageEngine.ERROR_AUTHENTICATION;
+                return ContentResolver.SYNC_ERROR_AUTHENTICATION;
             if (syncResult.stats.numIoExceptions > 0)
-                return SyncStorageEngine.ERROR_IO;
+                return ContentResolver.SYNC_ERROR_IO;
             if (syncResult.stats.numParseExceptions > 0)
-                return SyncStorageEngine.ERROR_PARSE;
+                return ContentResolver.SYNC_ERROR_PARSE;
             if (syncResult.stats.numConflictDetectedExceptions > 0)
-                return SyncStorageEngine.ERROR_CONFLICT;
+                return ContentResolver.SYNC_ERROR_CONFLICT;
             if (syncResult.tooManyDeletions)
-                return SyncStorageEngine.ERROR_TOO_MANY_DELETIONS;
+                return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS;
             if (syncResult.tooManyRetries)
-                return SyncStorageEngine.ERROR_TOO_MANY_RETRIES;
+                return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES;
             if (syncResult.databaseError)
-                return SyncStorageEngine.ERROR_INTERNAL;
+                return ContentResolver.SYNC_ERROR_INTERNAL;
             throw new IllegalStateException("we are not in an error state, " + syncResult);
         }
 
@@ -1738,9 +1833,10 @@
                 } else {
                     final boolean timeToShowNotification =
                             now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY;
-                    final boolean syncIsForced = syncOperation.extras
-                            .getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false);
-                    shouldInstall = timeToShowNotification || syncIsForced;
+                    // show the notification immediately if this is a manual sync
+                    final boolean manualSync = syncOperation.extras
+                            .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
+                    shouldInstall = timeToShowNotification || manualSync;
                 }
             }
 
@@ -1860,7 +1956,7 @@
             mContext.sendBroadcast(syncStateIntent);
         }
 
-        private void installHandleTooManyDeletesNotification(String account, String authority,
+        private void installHandleTooManyDeletesNotification(Account account, String authority,
                 long numDeletes) {
             if (mNotificationMgr == null) return;
             Intent clickIntent = new Intent();
@@ -1995,9 +2091,9 @@
             SyncOperation existingOperation = mOpsByKey.get(operationKey);
 
             // if this operation matches an existing operation that is being retried (delay > 0)
-            // and this operation isn't forced, ignore this operation
+            // and this isn't a manual sync operation, ignore this operation
             if (existingOperation != null && existingOperation.delay > 0) {
-                if (!operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false)) {
+                if (!operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)) {
                     return false;
                 }
             }
@@ -2071,7 +2167,7 @@
             if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(true /* check the DB */);
         }
 
-        public void clear(String account, String authority) {
+        public void clear(Account account, String authority) {
             Iterator<Map.Entry<String, SyncOperation>> entries = mOpsByKey.entrySet().iterator();
             while (entries.hasNext()) {
                 Map.Entry<String, SyncOperation> entry = entries.next();
diff --git a/core/java/android/content/SyncStateContentProviderHelper.java b/core/java/android/content/SyncStateContentProviderHelper.java
index f503e6f..dc728ec 100644
--- a/core/java/android/content/SyncStateContentProviderHelper.java
+++ b/core/java/android/content/SyncStateContentProviderHelper.java
@@ -23,6 +23,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.net.Uri;
+import android.accounts.Account;
 
 /**
  * Extends the schema of a ContentProvider to include the _sync_state table
@@ -43,14 +44,15 @@
     private static final Uri CONTENT_URI =
             Uri.parse("content://" + SYNC_STATE_AUTHORITY + "/state");
 
-    private static final String ACCOUNT_WHERE = "_sync_account = ?";
+    private static final String ACCOUNT_WHERE = "_sync_account = ? AND _sync_account_type = ?";
 
     private final Provider mInternalProviderInterface;
 
     private static final String SYNC_STATE_TABLE = "_sync_state";
-    private static long DB_VERSION = 2;
+    private static long DB_VERSION = 3;
 
-    private static final String[] ACCOUNT_PROJECTION = new String[]{"_sync_account"};
+    private static final String[] ACCOUNT_PROJECTION =
+            new String[]{"_sync_account", "_sync_account_type"};
 
     static {
         sURIMatcher.addURI(SYNC_STATE_AUTHORITY, "state", STATE);
@@ -70,8 +72,9 @@
         db.execSQL("CREATE TABLE _sync_state (" +
                    "_id INTEGER PRIMARY KEY," +
                    "_sync_account TEXT," +
+                   "_sync_account_type TEXT," +
                    "data TEXT," +
-                   "UNIQUE(_sync_account)" +
+                   "UNIQUE(_sync_account, _sync_account_type)" +
                    ");");
 
         db.execSQL("DROP TABLE IF EXISTS _sync_state_metadata");
@@ -168,15 +171,17 @@
      * @param account the account of the row that should be copied over.
      */
     public void copySyncState(SQLiteDatabase dbSrc, SQLiteDatabase dbDest,
-            String account) {
-        final String[] whereArgs = new String[]{account};
-        Cursor c = dbSrc.query(SYNC_STATE_TABLE, new String[]{"_sync_account", "data"},
+            Account account) {
+        final String[] whereArgs = new String[]{account.mName, account.mType};
+        Cursor c = dbSrc.query(SYNC_STATE_TABLE,
+                new String[]{"_sync_account", "_sync_account_type", "data"},
                 ACCOUNT_WHERE, whereArgs, null, null, null);
         try {
             if (c.moveToNext()) {
                 ContentValues values = new ContentValues();
                 values.put("_sync_account", c.getString(0));
-                values.put("data", c.getBlob(1));
+                values.put("_sync_account_type", c.getString(1));
+                values.put("data", c.getBlob(2));
                 dbDest.replace(SYNC_STATE_TABLE, "_sync_account", values);
             }
         } finally {
@@ -184,14 +189,17 @@
         }
     }
 
-    public void onAccountsChanged(String[] accounts) {
+    public void onAccountsChanged(Account[] accounts) {
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null);
         try {
             while (c.moveToNext()) {
-                final String account = c.getString(0);
+                final String accountName = c.getString(0);
+                final String accountType = c.getString(1);
+                Account account = new Account(accountName, accountType);
                 if (!ArrayUtils.contains(accounts, account)) {
-                    db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account});
+                    db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE,
+                            new String[]{accountName, accountType});
                 }
             }
         } finally {
@@ -199,9 +207,9 @@
         }
     }
 
-    public void discardSyncData(SQLiteDatabase db, String account) {
+    public void discardSyncData(SQLiteDatabase db, Account account) {
         if (account != null) {
-            db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account});
+            db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account.mName, account.mType});
         } else {
             db.delete(SYNC_STATE_TABLE, null, null);
         }
@@ -210,9 +218,9 @@
     /**
      * Retrieves the SyncData bytes for the given account. The byte array returned may be null.
      */
-    public byte[] readSyncDataBytes(SQLiteDatabase db, String account) {
+    public byte[] readSyncDataBytes(SQLiteDatabase db, Account account) {
         Cursor c = db.query(SYNC_STATE_TABLE, null, ACCOUNT_WHERE,
-                new String[]{account}, null, null, null);
+                new String[]{account.mName, account.mType}, null, null, null);
         try {
             if (c.moveToFirst()) {
                 return c.getBlob(c.getColumnIndexOrThrow("data"));
@@ -226,9 +234,10 @@
     /**
      * Sets the SyncData bytes for the given account. The bytes array may be null.
      */
-    public void writeSyncDataBytes(SQLiteDatabase db, String account, byte[] data) {
+    public void writeSyncDataBytes(SQLiteDatabase db, Account account, byte[] data) {
         ContentValues values = new ContentValues();
         values.put("data", data);
-        db.update(SYNC_STATE_TABLE, values, ACCOUNT_WHERE, new String[]{account});
+        db.update(SYNC_STATE_TABLE, values, ACCOUNT_WHERE,
+                new String[]{account.mName, account.mType});
     }
 }
diff --git a/include/utils/executablepath.h b/core/java/android/content/SyncStatusObserver.java
similarity index 65%
rename from include/utils/executablepath.h
rename to core/java/android/content/SyncStatusObserver.java
index c979432..663378a 100644
--- a/include/utils/executablepath.h
+++ b/core/java/android/content/SyncStatusObserver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _UTILS_EXECUTABLEPATH_H
-#define _UTILS_EXECUTABLEPATH_H
+package android.content;
 
-#include <limits.h>
-
-// returns the path to this executable
-#if __cplusplus
-extern "C"
-#endif
-void executablepath(char s[PATH_MAX]);
-
-#endif // _UTILS_EXECUTABLEPATH_H
+public interface SyncStatusObserver {
+    void onStatusChanged(int which);
+}
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 9c25e73..aaba7c7 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -24,6 +24,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.accounts.Account;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
@@ -86,6 +87,9 @@
     /** Enum value for a user-initiated sync. */
     public static final int SOURCE_USER = 3;
 
+    private static final Intent SYNC_CONNECTION_SETTING_CHANGED_INTENT =
+            new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
+
     // TODO: i18n -- grab these out of resources.
     /** String names for the sync source types. */
     public static final String[] SOURCES = { "SERVER",
@@ -93,26 +97,10 @@
                                              "POLL",
                                              "USER" };
 
-    // Error types
-    public static final int ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
-    public static final int ERROR_AUTHENTICATION = 2;
-    public static final int ERROR_IO = 3;
-    public static final int ERROR_PARSE = 4;
-    public static final int ERROR_CONFLICT = 5;
-    public static final int ERROR_TOO_MANY_DELETIONS = 6;
-    public static final int ERROR_TOO_MANY_RETRIES = 7;
-    public static final int ERROR_INTERNAL = 8;
-
     // The MESG column will contain one of these or one of the Error types.
     public static final String MESG_SUCCESS = "success";
     public static final String MESG_CANCELED = "canceled";
 
-    public static final int CHANGE_SETTINGS = 1<<0;
-    public static final int CHANGE_PENDING = 1<<1;
-    public static final int CHANGE_ACTIVE = 1<<2;
-    public static final int CHANGE_STATUS = 1<<3;
-    public static final int CHANGE_ALL = 0x7fffffff;
-    
     public static final int MAX_HISTORY = 15;
     
     private static final int MSG_WRITE_STATUS = 1;
@@ -122,7 +110,7 @@
     private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour
     
     public static class PendingOperation {
-        final String account;
+        final Account account;
         final int syncSource;
         final String authority;
         final Bundle extras;        // note: read-only.
@@ -130,7 +118,7 @@
         int authorityId;
         byte[] flatExtras;
         
-        PendingOperation(String account, int source,
+        PendingOperation(Account account, int source,
                 String authority, Bundle extras) {
             this.account = account;
             this.syncSource = source;
@@ -149,22 +137,22 @@
     }
     
     static class AccountInfo {
-        final String account;
+        final Account account;
         final HashMap<String, AuthorityInfo> authorities =
                 new HashMap<String, AuthorityInfo>();
         
-        AccountInfo(String account) {
+        AccountInfo(Account account) {
             this.account = account;
         }
     }
     
     public static class AuthorityInfo {
-        final String account;
+        final Account account;
         final String authority;
         final int ident;
         boolean enabled;
-        
-        AuthorityInfo(String account, String authority, int ident) {
+
+        AuthorityInfo(Account account, String authority, int ident) {
             this.account = account;
             this.authority = authority;
             this.ident = ident;
@@ -200,8 +188,8 @@
     private final SparseArray<AuthorityInfo> mAuthorities =
             new SparseArray<AuthorityInfo>();
     
-    private final HashMap<String, AccountInfo> mAccounts =
-        new HashMap<String, AccountInfo>();
+    private final HashMap<Account, AccountInfo> mAccounts =
+        new HashMap<Account, AccountInfo>();
 
     private final ArrayList<PendingOperation> mPendingOperations =
             new ArrayList<PendingOperation>();
@@ -256,7 +244,7 @@
     private int mNumPendingFinished = 0;
     
     private int mNextHistoryId = 0;
-    private boolean mListenForTickles = true;
+    private boolean mMasterSyncAutomatically = true;
     
     private SyncStorageEngine(Context context) {
         mContext = context;
@@ -353,14 +341,14 @@
         }
     }
     
-    public boolean getSyncProviderAutomatically(String account, String providerName) {
+    public boolean getSyncAutomatically(Account account, String providerName) {
         synchronized (mAuthorities) {
             if (account != null) {
                 AuthorityInfo authority = getAuthorityLocked(account, providerName,
-                        "getSyncProviderAutomatically");
-                return authority != null ? authority.enabled : false;
+                        "getSyncAutomatically");
+                return authority != null && authority.enabled;
             }
-            
+
             int i = mAuthorities.size();
             while (i > 0) {
                 i--;
@@ -374,45 +362,35 @@
         }
     }
 
-    public void setSyncProviderAutomatically(String account, String providerName, boolean sync) {
+    public void setSyncAutomatically(Account account, String providerName, boolean sync) {
         synchronized (mAuthorities) {
-            if (account != null) {
-                AuthorityInfo authority = getAuthorityLocked(account, providerName,
-                        "setSyncProviderAutomatically");
-                if (authority != null) {
-                    authority.enabled = sync;
-                }
-            } else {
-                int i = mAuthorities.size();
-                while (i > 0) {
-                    i--;
-                    AuthorityInfo authority = mAuthorities.get(i);
-                    if (authority.authority.equals(providerName)) {
-                        authority.enabled = sync;
-                    }
-                }
+            AuthorityInfo authority = getAuthorityLocked(account, providerName,
+                    "setSyncAutomatically");
+            if (authority != null) {
+                authority.enabled = sync;
             }
             writeAccountInfoLocked();
         }
         
-        reportChange(CHANGE_SETTINGS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
     }
 
-    public void setListenForNetworkTickles(boolean flag) {
+    public void setMasterSyncAutomatically(boolean flag) {
         synchronized (mAuthorities) {
-            mListenForTickles = flag;
+            mMasterSyncAutomatically = flag;
             writeAccountInfoLocked();
         }
-        reportChange(CHANGE_SETTINGS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+        mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
     }
 
-    public boolean getListenForNetworkTickles() {
+    public boolean getMasterSyncAutomatically() {
         synchronized (mAuthorities) {
-            return mListenForTickles;
+            return mMasterSyncAutomatically;
         }
     }
     
-    public AuthorityInfo getAuthority(String account, String authority) {
+    public AuthorityInfo getAuthority(Account account, String authority) {
         synchronized (mAuthorities) {
             return getAuthorityLocked(account, authority, null);
         }
@@ -428,7 +406,7 @@
      * Returns true if there is currently a sync operation for the given
      * account or authority in the pending list, or actively being processed.
      */
-    public boolean isSyncActive(String account, String authority) {
+    public boolean isSyncActive(Account account, String authority) {
         synchronized (mAuthorities) {
             int i = mPendingOperations.size();
             while (i > 0) {
@@ -477,7 +455,7 @@
             status.pending = true;
         }
         
-        reportChange(CHANGE_PENDING);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
         return op;
     }
 
@@ -523,7 +501,7 @@
             }
         }
         
-        reportChange(CHANGE_PENDING);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
         return res;
     }
 
@@ -539,7 +517,7 @@
             }
             writePendingOperationsLocked();
         }
-        reportChange(CHANGE_PENDING);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
         return num;
     }
 
@@ -567,7 +545,7 @@
      * Called when the set of account has changed, given the new array of
      * active accounts.
      */
-    public void doDatabaseCleanup(String[] accounts) {
+    public void doDatabaseCleanup(Account[] accounts) {
         synchronized (mAuthorities) {
             if (DEBUG) Log.w(TAG, "Updating for new accounts...");
             SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
@@ -646,20 +624,20 @@
             }
         }
         
-        reportChange(CHANGE_ACTIVE);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
     }
 
     /**
      * To allow others to send active change reports, to poke clients.
      */
     public void reportActiveChange() {
-        reportChange(CHANGE_ACTIVE);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
     }
     
     /**
      * Note that sync has started for the given account and authority.
      */
-    public long insertStartSyncEvent(String accountName, String authorityName,
+    public long insertStartSyncEvent(Account accountName, String authorityName,
             long now, int source) {
         long id;
         synchronized (mAuthorities) {
@@ -685,7 +663,7 @@
             if (DEBUG) Log.v(TAG, "returning historyId " + id);
         }
         
-        reportChange(CHANGE_STATUS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
         return id;
     }
 
@@ -789,7 +767,7 @@
             }            
         }
         
-        reportChange(CHANGE_STATUS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
     }
 
     /**
@@ -847,7 +825,7 @@
     /**
      * Return true if the pending status is true of any matching authorities.
      */
-    public boolean isAuthorityPending(String account, String authority) {
+    public boolean isSyncPending(Account account, String authority) {
         synchronized (mAuthorities) {
             final int N = mSyncStatus.size();
             for (int i=0; i<N; i++) {
@@ -903,7 +881,7 @@
      */
     public long getInitialSyncFailureTime() {
         synchronized (mAuthorities) {
-            if (!mListenForTickles) {
+            if (!mMasterSyncAutomatically) {
                 return 0;
             }
             
@@ -944,7 +922,7 @@
      * @param tag If non-null, this will be used in a log message if the
      * requested authority does not exist.
      */
-    private AuthorityInfo getAuthorityLocked(String accountName, String authorityName,
+    private AuthorityInfo getAuthorityLocked(Account accountName, String authorityName,
             String tag) {
         AccountInfo account = mAccounts.get(accountName);
         if (account == null) {
@@ -964,7 +942,7 @@
         return authority;
     }
     
-    private AuthorityInfo getOrCreateAuthorityLocked(String accountName,
+    private AuthorityInfo getOrCreateAuthorityLocked(Account accountName,
             String authorityName, int ident, boolean doWrite) {
         AccountInfo account = mAccounts.get(accountName);
         if (account == null) {
@@ -1037,7 +1015,7 @@
             if ("accounts".equals(tagName)) {
                 String listen = parser.getAttributeValue(
                         null, "listen-for-tickles");
-                mListenForTickles = listen == null
+                mMasterSyncAutomatically = listen == null
                             || Boolean.parseBoolean(listen);
                 eventType = parser.next();
                 do {
@@ -1055,6 +1033,11 @@
                             if (id >= 0) {
                                 String accountName = parser.getAttributeValue(
                                         null, "account");
+                                String accountType = parser.getAttributeValue(
+                                        null, "type");
+                                if (accountType == null) {
+                                    accountType = "com.google.GAIA";
+                                }
                                 String authorityName = parser.getAttributeValue(
                                         null, "authority");
                                 String enabled = parser.getAttributeValue(
@@ -1066,7 +1049,8 @@
                                 if (authority == null) {
                                     if (DEBUG_FILE) Log.v(TAG, "Creating entry");
                                     authority = getOrCreateAuthorityLocked(
-                                        accountName, authorityName, id, false);
+                                            new Account(accountName, accountType),
+                                            authorityName, id, false);
                                 }
                                 if (authority != null) {
                                     authority.enabled = enabled == null
@@ -1112,7 +1096,7 @@
             out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
             
             out.startTag(null, "accounts");
-            if (!mListenForTickles) {
+            if (!mMasterSyncAutomatically) {
                 out.attribute(null, "listen-for-tickles", "false");
             }
             
@@ -1121,7 +1105,8 @@
                 AuthorityInfo authority = mAuthorities.get(i);
                 out.startTag(null, "authority");
                 out.attribute(null, "id", Integer.toString(authority.ident));
-                out.attribute(null, "account", authority.account);
+                out.attribute(null, "account", authority.account.mName);
+                out.attribute(null, "type", authority.account.mType);
                 out.attribute(null, "authority", authority.authority);
                 if (!authority.enabled) {
                     out.attribute(null, "enabled", "false");
@@ -1170,6 +1155,8 @@
         }
         
         if (db != null) {
+            final boolean hasType = db.getVersion() >= 11;
+            
             // Copy in all of the status information, as well as accounts.
             if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db");
             SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
@@ -1177,6 +1164,9 @@
             HashMap<String,String> map = new HashMap<String,String>();
             map.put("_id", "status._id as _id");
             map.put("account", "stats.account as account");
+            if (hasType) {
+                map.put("account_type", "stats.account_type as account_type");
+            }
             map.put("authority", "stats.authority as authority");
             map.put("totalElapsedTime", "totalElapsedTime");
             map.put("numSyncs", "numSyncs");
@@ -1195,9 +1185,15 @@
             Cursor c = qb.query(db, null, null, null, null, null, null);
             while (c.moveToNext()) {
                 String accountName = c.getString(c.getColumnIndex("account"));
+                String accountType = hasType
+                        ? c.getString(c.getColumnIndex("account_type")) : null;
+                if (accountType == null) {
+                    accountType = "com.google.GAIA";
+                }
                 String authorityName = c.getString(c.getColumnIndex("authority"));
                 AuthorityInfo authority = this.getOrCreateAuthorityLocked(
-                        accountName, authorityName, -1, false);
+                        new Account(accountName, accountType),
+                        authorityName, -1, false);
                 if (authority != null) {
                     int i = mSyncStatus.size();
                     boolean found = false;
@@ -1240,13 +1236,18 @@
                 String value = c.getString(c.getColumnIndex("value"));
                 if (name == null) continue;
                 if (name.equals("listen_for_tickles")) {
-                    setListenForNetworkTickles(value == null
-                            || Boolean.parseBoolean(value));
+                    setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value));
                 } else if (name.startsWith("sync_provider_")) {
                     String provider = name.substring("sync_provider_".length(),
                             name.length());
-                    setSyncProviderAutomatically(null, provider,
-                            value == null || Boolean.parseBoolean(value));
+                    int i = mAuthorities.size();
+                    while (i > 0) {
+                        i--;
+                        AuthorityInfo authority = mAuthorities.get(i);
+                        if (authority.authority.equals(provider)) {
+                            authority.enabled = value == null || Boolean.parseBoolean(value);
+                        }
+                    }
                 }
             }
             
diff --git a/core/java/android/content/SyncableContentProvider.java b/core/java/android/content/SyncableContentProvider.java
index e0cd786..ab4e91c 100644
--- a/core/java/android/content/SyncableContentProvider.java
+++ b/core/java/android/content/SyncableContentProvider.java
@@ -19,6 +19,7 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
+import android.accounts.Account;
 
 import java.util.Map;
 
@@ -32,6 +33,16 @@
 public abstract class SyncableContentProvider extends ContentProvider {
     protected abstract boolean isTemporary();
 
+    private volatile TempProviderSyncAdapter mTempProviderSyncAdapter;
+
+    public void setTempProviderSyncAdapter(TempProviderSyncAdapter syncAdapter) {
+        mTempProviderSyncAdapter = syncAdapter;
+    }
+
+    public TempProviderSyncAdapter getTempProviderSyncAdapter() {
+        return mTempProviderSyncAdapter;
+    }
+
     /**
      * Close resources that must be closed. You must call this to properly release
      * the resources used by the SyncableContentProvider.
@@ -110,7 +121,7 @@
      * @param context the sync context for the operation
      * @param account
      */
-    public abstract void onSyncStart(SyncContext context, String account);
+    public abstract void onSyncStart(SyncContext context, Account account);
 
     /**
      * Called right after a sync is completed
@@ -124,7 +135,7 @@
      * The account of the most recent call to onSyncStart()
      * @return the account
      */
-    public abstract String getSyncingAccount();
+    public abstract Account getSyncingAccount();
 
     /**
      * Merge diffs from a sync source with this content provider.
@@ -194,7 +205,7 @@
      * Make sure that there are no entries for accounts that no longer exist
      * @param accountsArray the array of currently-existing accounts
      */
-    protected abstract void onAccountsChanged(String[] accountsArray);
+    protected abstract void onAccountsChanged(Account[] accountsArray);
 
     /**
      * A helper method to delete all rows whose account is not in the accounts
@@ -203,26 +214,24 @@
      *
      * @param accounts a map of existing accounts
      * @param table the table to delete from
-     * @param accountColumnName the name of the column that is expected
-     * to hold the account.
      */
-    protected abstract void deleteRowsForRemovedAccounts(Map<String, Boolean> accounts,
-            String table, String accountColumnName);
+    protected abstract void deleteRowsForRemovedAccounts(Map<Account, Boolean> accounts,
+            String table);
 
     /**
      * Called when the sync system determines that this provider should no longer
      * contain records for the specified account.
      */
-    public abstract void wipeAccount(String account);
+    public abstract void wipeAccount(Account account);
 
     /**
      * Retrieves the SyncData bytes for the given account. The byte array returned may be null.
      */
-    public abstract byte[] readSyncDataBytes(String account);
+    public abstract byte[] readSyncDataBytes(Account account);
 
     /**
      * Sets the SyncData bytes for the given account. The bytes array may be null.
      */
-    public abstract void writeSyncDataBytes(String account, byte[] data);
+    public abstract void writeSyncDataBytes(Account account, byte[] data);
 }
 
diff --git a/core/java/android/content/TempProviderSyncAdapter.java b/core/java/android/content/TempProviderSyncAdapter.java
index eb3a5da..fb05fe7 100644
--- a/core/java/android/content/TempProviderSyncAdapter.java
+++ b/core/java/android/content/TempProviderSyncAdapter.java
@@ -12,6 +12,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.TimingLogger;
+import android.accounts.Account;
 
 /**
  * @hide
@@ -62,12 +63,10 @@
      *
      * @param context allows you to publish status and interact with the
      * @param account the account to sync
-     * @param forced if true then the sync was forced
+     * @param manualSync true if this sync was requested manually by the user
      * @param result information to track what happened during this sync attempt
-     * @return true, if the sync was successfully started. One reason it can
-     *   fail to start is if there is no user configured on the device.
      */
-    public abstract void onSyncStarting(SyncContext context, String account, boolean forced,
+    public abstract void onSyncStarting(SyncContext context, Account account, boolean manualSync,
             SyncResult result);
 
     /**
@@ -168,12 +167,12 @@
      * exist.
      * @param accounts the list of accounts
      */
-    public abstract void onAccountsChanged(String[] accounts);
+    public abstract void onAccountsChanged(Account[] accounts);
 
     private Context mContext;
 
     private class SyncThread extends Thread {
-        private final String mAccount;
+        private final Account mAccount;
         private final Bundle mExtras;
         private final SyncContext mSyncContext;
         private volatile boolean mIsCanceled = false;
@@ -181,7 +180,7 @@
         private long mInitialRxBytes;
         private final SyncResult mResult;
 
-        SyncThread(SyncContext syncContext, String account, Bundle extras) {
+        SyncThread(SyncContext syncContext, Account account, Bundle extras) {
             super("SyncThread");
             mAccount = account;
             mExtras = extras;
@@ -221,19 +220,19 @@
             }
         }
 
-        private void sync(SyncContext syncContext, String account, Bundle extras) {
+        private void sync(SyncContext syncContext, Account account, Bundle extras) {
             mIsCanceled = false;
 
             mProviderSyncStarted = false;
             mAdapterSyncStarted = false;
             String message = null;
 
-            boolean syncForced = extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false);
+            boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
 
             try {
                 mProvider.onSyncStart(syncContext, account);
                 mProviderSyncStarted = true;
-                onSyncStarting(syncContext, account, syncForced, mResult);
+                onSyncStarting(syncContext, account, manualSync, mResult);
                 if (mResult.hasError()) {
                     message = "SyncAdapter failed while trying to start sync";
                     return;
@@ -273,7 +272,7 @@
             }
         }
 
-        private void runSyncLoop(SyncContext syncContext, String account, Bundle extras) {
+        private void runSyncLoop(SyncContext syncContext, Account account, Bundle extras) {
             TimingLogger syncTimer = new TimingLogger(TAG + "Profiling", "sync");
             syncTimer.addSplit("start");
             int loopCount = 0;
@@ -518,7 +517,7 @@
         EventLog.writeEvent(SyncAdapter.LOG_SYNC_DETAILS, TAG, bytesSent, bytesReceived, "");
     }
 
-    public void startSync(SyncContext syncContext, String account, Bundle extras) {
+    public void startSync(SyncContext syncContext, Account account, Bundle extras) {
         if (mSyncThread != null) {
             syncContext.onFinished(SyncResult.ALREADY_IN_PROGRESS);
             return;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index bf2a895..68f8417 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -119,6 +119,7 @@
      *                 providers that can sync.
      * @param outInfo Filled in with a list of the ProviderInfo for each
      *                name in 'outNames'.
+     * @deprecated
      */
     void querySyncProviders(inout List<String> outNames,
             inout List<ProviderInfo> outInfo);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 941ca9e..3250a87 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1435,8 +1435,6 @@
      * which market the package came from.
      * 
      * @param packageName The name of the package to query
-     *
-     * @hide
      */
     public abstract String getInstallerPackageName(String packageName);
     
diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java
index b67ddf6..1d11b31 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -65,7 +65,11 @@
      *  running in the same process.  Higher goes first. */
     public int initOrder = 0;
     
-    /** Whether or not this provider is syncable. */
+    /**
+     * Whether or not this provider is syncable.
+     * @deprecated This flag is now being ignored. The current way to make a provider
+     * syncable is to provide a SyncAdapter service for a given provider/account type. 
+     */
     public boolean isSyncable = false;
 
     public ProviderInfo() {
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
new file mode 100644
index 0000000..bb94372
--- /dev/null
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.Context;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ComponentName;
+import android.content.res.XmlResourceParser;
+import android.util.Log;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import java.util.Map;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.IOException;
+
+import com.google.android.collect.Maps;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * A cache of registered services. This cache
+ * is built by interrogating the {@link PackageManager} and is updated as packages are added,
+ * removed and changed. The services are referred to by type V and
+ * are made available via the {@link #getServiceInfo} method.
+ * @hide
+ */
+public abstract class RegisteredServicesCache<V> {
+    private static final String TAG = "PackageManager";
+
+    public final Context mContext;
+    private final String mInterfaceName;
+    private final String mMetaDataName;
+    private final String mAttributesName;
+
+    // no need to be synchronized since the map is never changed once mService is written
+    private volatile Map<V, ServiceInfo<V>> mServices;
+
+    // synchronized on "this"
+    private BroadcastReceiver mReceiver = null;
+
+    public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
+            String attributeName) {
+        mContext = context;
+        mInterfaceName = interfaceName;
+        mMetaDataName = metaDataName;
+        mAttributesName = attributeName;
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+        getAllServices();
+        Map<V, ServiceInfo<V>> services = mServices;
+        fout.println("RegisteredServicesCache: " + services.size() + " services");
+        for (ServiceInfo info : services.values()) {
+            fout.println("  " + info);
+        }
+    }
+
+    private boolean maybeRegisterForPackageChanges() {
+        synchronized (this) {
+            if (mReceiver == null) {
+                synchronized (this) {
+                    mReceiver = new BroadcastReceiver() {
+                        public void onReceive(Context context, Intent intent) {
+                            mServices = generateServicesMap();
+                        }
+                    };
+                }
+
+                IntentFilter intentFilter = new IntentFilter();
+                intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+                intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+                intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+                mContext.registerReceiver(mReceiver, intentFilter);
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private void maybeUnregisterForPackageChanges() {
+        synchronized (this) {
+            if (mReceiver != null) {
+                mContext.unregisterReceiver(mReceiver);
+                mReceiver = null;
+            }
+        }
+    }
+
+    /**
+     * Value type that describes a Service. The information within can be used
+     * to bind to the service.
+     */
+    public static class ServiceInfo<V> {
+        public final V type;
+        public final ComponentName componentName;
+
+        private ServiceInfo(V type, ComponentName componentName) {
+            this.type = type;
+            this.componentName = componentName;
+        }
+
+        public String toString() {
+            return "ServiceInfo: " + type + ", " + componentName;
+        }
+    }
+
+    /**
+     * Accessor for the registered authenticators.
+     * @param type the account type of the authenticator
+     * @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();
+        }
+        return mServices.get(type);
+    }
+
+    /**
+     * @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
+     * registered authenticators.
+     */
+    public Collection<ServiceInfo<V>> getAllServices() {
+        if (mServices == null) {
+            maybeRegisterForPackageChanges();
+            mServices = generateServicesMap();
+        }
+        return Collections.unmodifiableCollection(mServices.values());
+    }
+
+    /**
+     * Stops the monitoring of package additions, removals and changes.
+     */
+    public void close() {
+        maybeUnregisterForPackageChanges();
+    }
+
+    protected void finalize() throws Throwable {
+        synchronized (this) {
+            if (mReceiver != null) {
+                Log.e(TAG, "RegisteredServicesCache finalized without being closed");
+            }
+        }
+        close();
+        super.finalize();
+    }
+
+    private Map<V, ServiceInfo<V>> generateServicesMap() {
+        Map<V, ServiceInfo<V>> services = Maps.newHashMap();
+        PackageManager pm = mContext.getPackageManager();
+
+        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());
+                }
+            } catch (XmlPullParserException e) {
+                Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
+            } catch (IOException e) {
+                Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
+            }
+        }
+
+        return services;
+    }
+
+    private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
+            throws XmlPullParserException, IOException {
+        android.content.pm.ServiceInfo si = service.serviceInfo;
+        ComponentName componentName = new ComponentName(si.packageName, si.name);
+
+        PackageManager pm = mContext.getPackageManager();
+
+        XmlResourceParser parser = null;
+        try {
+            parser = si.loadXmlMetaData(pm, mMetaDataName);
+            if (parser == null) {
+                throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
+            }
+
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+
+            String nodeName = parser.getName();
+            if (!mAttributesName.equals(nodeName)) {
+                throw new XmlPullParserException(
+                        "Meta-data does not start with " + mAttributesName +  " tag");
+            }
+
+            V v = parseServiceAttributes(si.packageName, attrs);
+            if (v == null) {
+                return null;
+            }
+            return new ServiceInfo<V>(v, componentName);
+        } finally {
+            if (parser != null) parser.close();
+        }
+    }
+
+    public abstract V parseServiceAttributes(String packageName, AttributeSet attrs);
+}
diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java
index 4ac0aef..27a02e2 100644
--- a/core/java/android/database/AbstractWindowedCursor.java
+++ b/core/java/android/database/AbstractWindowedCursor.java
@@ -166,6 +166,48 @@
         return mWindow.isBlob(mPos, columnIndex);
     }
 
+    public boolean isString(int columnIndex)
+    {
+        checkPosition();
+
+        synchronized(mUpdatedRows) {
+            if (isFieldUpdated(columnIndex)) {
+                Object object = getUpdatedField(columnIndex);
+                return object == null || object instanceof String;
+            }
+        }
+
+        return mWindow.isString(mPos, columnIndex);
+    }
+
+    public boolean isLong(int columnIndex)
+    {
+        checkPosition();
+
+        synchronized(mUpdatedRows) {
+            if (isFieldUpdated(columnIndex)) {
+                Object object = getUpdatedField(columnIndex);
+                return object != null && (object instanceof Integer || object instanceof Long);
+            }
+        }
+
+        return mWindow.isLong(mPos, columnIndex);
+    }
+
+    public boolean isFloat(int columnIndex)
+    {
+        checkPosition();
+
+        synchronized(mUpdatedRows) {
+            if (isFieldUpdated(columnIndex)) {
+                Object object = getUpdatedField(columnIndex);
+                return object != null && (object instanceof Float || object instanceof Double);
+            }
+        }
+
+        return mWindow.isFloat(mPos, columnIndex);
+    }
+
     @Override
     protected void checkPosition()
     {
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 8e26730..99db81b 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -263,7 +263,58 @@
         }
     }
 
+    /**
+     * Checks if a field contains a long
+     *
+     * @param row the row to read from, row - getStartPosition() being the actual row in the window
+     * @param col the column to read from
+     * @return {@code true} if given field is a long
+     */
+    public boolean isLong(int row, int col) {
+        acquireReference();
+        try {
+            return isInteger_native(row - mStartPos, col);
+        } finally {
+            releaseReference();
+        }
+    }
+
+    /**
+     * Checks if a field contains a float.
+     *
+     * @param row the row to read from, row - getStartPosition() being the actual row in the window
+     * @param col the column to read from
+     * @return {@code true} if given field is a float
+     */
+    public boolean isFloat(int row, int col) {
+        acquireReference();
+        try {
+            return isFloat_native(row - mStartPos, col);
+        } finally {
+            releaseReference();
+        }
+    }
+
+    /**
+     * Checks if a field contains either a String or is null.
+     *
+     * @param row the row to read from, row - getStartPosition() being the actual row in the window
+     * @param col the column to read from
+     * @return {@code true} if given field is {@code NULL} or a String
+     */
+    public boolean isString(int row, int col) {
+        acquireReference();
+        try {
+            return isString_native(row - mStartPos, col);
+        } finally {
+            releaseReference();
+        }
+    }
+
     private native boolean isBlob_native(int row, int col);
+    private native boolean isString_native(int row, int col);
+    private native boolean isInteger_native(int row, int col);
+    private native boolean isFloat_native(int row, int col);
 
     /**
      * Returns a String for the given field.
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 10f3806..4ca6601 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -20,6 +20,7 @@
 
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.OperationApplicationException;
 import android.database.sqlite.SQLiteAbortException;
 import android.database.sqlite.SQLiteConstraintException;
 import android.database.sqlite.SQLiteDatabase;
@@ -82,6 +83,8 @@
             code = 8;
         } else if (e instanceof SQLiteException) {
             code = 9;
+        } else if (e instanceof OperationApplicationException) {
+            code = 10;
         } else {
             reply.writeException(e);
             Log.e(TAG, "Writing exception to parcel", e);
@@ -123,6 +126,18 @@
         }
     }
 
+    public static void readExceptionWithOperationApplicationExceptionFromParcel(
+            Parcel reply) throws OperationApplicationException {
+        int code = reply.readInt();
+        if (code == 0) return;
+        String msg = reply.readString();
+        if (code == 10) {
+            throw new OperationApplicationException(msg);
+        } else {
+            DatabaseUtils.readExceptionFromParcel(reply, msg, code);
+        }
+    }
+
     private static final void readExceptionFromParcel(Parcel reply, String msg, int code) {
         switch (code) {
             case 2:
@@ -211,7 +226,7 @@
             sb.append(sqlString);
         sb.append('\'');
     }
-    
+
     /**
      * SQL-escape a string.
      */
@@ -240,7 +255,7 @@
             appendEscapedSQLString(sql, value.toString());
         }
     }
-    
+
     /**
      * Concatenates two SQL WHERE clauses, handling empty or null values.
      * @hide
@@ -252,12 +267,12 @@
         if (TextUtils.isEmpty(b)) {
             return a;
         }
-            
+
         return "(" + a + ") AND (" + b + ")";
     }
-    
+
     /**
-     * return the collation key 
+     * return the collation key
      * @param name
      * @return the collation key
      */
@@ -269,7 +284,7 @@
             return "";
         }
     }
-    
+
     /**
      * return the collation key in hex format
      * @param name
@@ -280,7 +295,7 @@
         char[] keys = Hex.encodeHex(arr);
         return new String(keys, 0, getKeyLen(arr) * 2);
     }
-    
+
     private static int getKeyLen(byte[] arr) {
         if (arr[arr.length - 1] != 0) {
             return arr.length;
@@ -289,16 +304,16 @@
             return arr.length-1;
         }
     }
-    
+
     private static byte[] getCollationKeyInBytes(String name) {
         if (mColl == null) {
             mColl = Collator.getInstance();
             mColl.setStrength(Collator.PRIMARY);
         }
-        return mColl.getCollationKey(name).toByteArray();        
+        return mColl.getCollationKey(name).toByteArray();
     }
-    
-    private static Collator mColl = null;    
+
+    private static Collator mColl = null;
     /**
      * Prints the contents of a Cursor to System.out. The position is restored
      * after printing.
@@ -591,10 +606,12 @@
     public static long queryNumEntries(SQLiteDatabase db, String table) {
         Cursor cursor = db.query(table, countProjection,
                 null, null, null, null, null);
-        cursor.moveToFirst();
-        long count = cursor.getLong(0);
-        cursor.deactivate();
-        return count;
+        try {
+            cursor.moveToFirst();
+            return cursor.getLong(0);
+        } finally {
+            cursor.close();
+        }
     }
 
     /**
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 8a63919..af54a71 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -355,23 +355,26 @@
             String groupBy, String having, String sortOrder, String limit) {
         String[] projection = computeProjection(projectionIn);
 
+        StringBuilder where = new StringBuilder();
+
         if (mWhereClause.length() > 0) {
-            mWhereClause.append(')');
+            where.append(mWhereClause.toString());
+            where.append(')');
         }
 
         // Tack on the user's selection, if present.
         if (selection != null && selection.length() > 0) {
             if (mWhereClause.length() > 0) {
-                mWhereClause.append(" AND ");
+                where.append(" AND ");
             }
 
-            mWhereClause.append('(');
-            mWhereClause.append(selection);
-            mWhereClause.append(')');
+            where.append('(');
+            where.append(selection);
+            where.append(')');
         }
 
         return buildQueryString(
-                mDistinct, mTables, projection, mWhereClause.toString(),
+                mDistinct, mTables, projection, where.toString(),
                 groupBy, having, sortOrder, limit);
     }
 
diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java
index f4a2a6a..f6159de 100644
--- a/core/java/android/net/WebAddress.java
+++ b/core/java/android/net/WebAddress.java
@@ -54,7 +54,7 @@
     static Pattern sAddressPattern = Pattern.compile(
             /* scheme    */ "(?:(http|HTTP|https|HTTPS|file|FILE)\\:\\/\\/)?" +
             /* authority */ "(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" +
-            /* host      */ "([-A-Za-z0-9%]+(?:\\.[-A-Za-z0-9%]+)*)?" +
+            /* host      */ "([-A-Za-z0-9%_]+(?:\\.[-A-Za-z0-9%_]+)*)?" +
             /* port      */ "(?:\\:([0-9]+))?" +
             /* path      */ "(\\/?.*)?");
 
diff --git a/core/java/android/net/http/Request.java b/core/java/android/net/http/Request.java
index df4fff0..3fb3d3f 100644
--- a/core/java/android/net/http/Request.java
+++ b/core/java/android/net/http/Request.java
@@ -116,12 +116,17 @@
         mBodyProvider = bodyProvider;
         mBodyLength = bodyLength;
 
-        if (bodyProvider == null) {
+        if (bodyProvider == null && !"POST".equalsIgnoreCase(method)) {
             mHttpRequest = new BasicHttpRequest(method, getUri());
         } else {
             mHttpRequest = new BasicHttpEntityEnclosingRequest(
                     method, getUri());
-            setBodyProvider(bodyProvider, bodyLength);
+            // it is ok to have null entity for BasicHttpEntityEnclosingRequest.
+            // By using BasicHttpEntityEnclosingRequest, it will set up the
+            // correct content-length, content-type and content-encoding.
+            if (bodyProvider != null) {
+                setBodyProvider(bodyProvider, bodyLength);
+            }
         }
         addHeader(HOST_HEADER, getHostPort());
 
@@ -255,6 +260,8 @@
             // process gzip content encoding
             Header contentEncoding = entity.getContentEncoding();
             InputStream nis = null;
+            byte[] buf = null;
+            int count = 0;
             try {
                 if (contentEncoding != null &&
                     contentEncoding.getValue().equals("gzip")) {
@@ -265,9 +272,8 @@
 
                 /* accumulate enough data to make it worth pushing it
                  * up the stack */
-                byte[] buf = mConnection.getBuf();
+                buf = mConnection.getBuf();
                 int len = 0;
-                int count = 0;
                 int lowWater = buf.length / 2;
                 while (len != -1) {
                     len = nis.read(buf, count, buf.length - count);
@@ -284,6 +290,10 @@
                 /* InflaterInputStream throws an EOFException when the
                    server truncates gzipped content.  Handle this case
                    as we do truncated non-gzipped content: no error */
+                if (count > 0) {
+                    // if there is uncommited content, we should commit them
+                    mEventHandler.data(buf, count);
+                }
                 if (HttpLog.LOGV) HttpLog.v( "readResponse() handling " + e);
             } catch(IOException e) {
                 // don't throw if we have a non-OK status code
diff --git a/core/java/android/net/http/RequestQueue.java b/core/java/android/net/http/RequestQueue.java
index 66d5722..54a1cce 100644
--- a/core/java/android/net/http/RequestQueue.java
+++ b/core/java/android/net/http/RequestQueue.java
@@ -52,10 +52,44 @@
 
     private Context mContext;
 
+    private static class RequestSet {
+        private final LinkedList<Request> mHighPriority;
+        private final LinkedList<Request> mLowPriority;
+
+        RequestSet() {
+            mHighPriority = new LinkedList<Request>();
+            mLowPriority = new LinkedList<Request>();
+        }
+
+        void add(Request req, boolean head) {
+            LinkedList l = mLowPriority;
+            if (req.mHighPriority) {
+                l = mHighPriority;
+            }
+            if (head) {
+                l.addFirst(req);
+            } else {
+                l.add(req);
+            }
+        }
+
+        Request removeFirst() {
+            if (!mHighPriority.isEmpty()) {
+                return mHighPriority.removeFirst();
+            } else if (!mLowPriority.isEmpty()) {
+                return mLowPriority.removeFirst();
+            }
+            return null;
+        }
+
+        boolean isEmpty() {
+            return mHighPriority.isEmpty() && mLowPriority.isEmpty();
+        }
+    };
     /**
      * Requests, indexed by HttpHost (scheme, host, port)
      */
-    private LinkedHashMap<HttpHost, LinkedList<Request>> mPending;
+    private LinkedHashMap<HttpHost, RequestSet> mPending;
 
     /* Support for notifying a client when queue is empty */
     private boolean mClientWaiting = false;
@@ -344,7 +378,7 @@
     public RequestQueue(Context context, int connectionCount) {
         mContext = context;
 
-        mPending = new LinkedHashMap<HttpHost, LinkedList<Request>>(32);
+        mPending = new LinkedHashMap<HttpHost, RequestSet>(32);
 
         mActivePool = new ActivePool(connectionCount);
         mActivePool.startup();
@@ -480,7 +514,7 @@
         req = new Request(method, httpHost, mProxyHost, uri.mPath, bodyProvider,
                           bodyLength, eventHandler, headers, highPriority);
 
-        queueRequest(req, highPriority);
+        queueRequest(req, false);
 
         mActivePool.mTotalRequest++;
 
@@ -520,19 +554,24 @@
         HttpLog.v("dump()");
         StringBuilder dump = new StringBuilder();
         int count = 0;
-        Iterator<Map.Entry<HttpHost, LinkedList<Request>>> iter;
+        Iterator<Map.Entry<HttpHost, RequestSet>> iter;
 
         // mActivePool.log(dump);
 
         if (!mPending.isEmpty()) {
             iter = mPending.entrySet().iterator();
             while (iter.hasNext()) {
-                Map.Entry<HttpHost, LinkedList<Request>> entry = iter.next();
+                Map.Entry<HttpHost, RequestSet> entry = iter.next();
                 String hostName = entry.getKey().getHostName();
                 StringBuilder line = new StringBuilder("p" + count++ + " " + hostName + " ");
 
-                LinkedList<Request> reqList = entry.getValue();
-                ListIterator reqIter = reqList.listIterator(0);
+                RequestSet reqList = entry.getValue();
+                ListIterator reqIter = reqList.mHighPriority.listIterator(0);
+                while (iter.hasNext()) {
+                    Request request = (Request)iter.next();
+                    line.append(request + " ");
+                }
+                reqIter = reqList.mLowPriority.listIterator(0);
                 while (iter.hasNext()) {
                     Request request = (Request)iter.next();
                     line.append(request + " ");
@@ -564,7 +603,7 @@
         Request ret = null;
 
         if (mNetworkConnected && mPending.containsKey(host)) {
-            LinkedList<Request> reqList = mPending.get(host);
+            RequestSet reqList = mPending.get(host);
             ret = reqList.removeFirst();
             if (reqList.isEmpty()) {
                 mPending.remove(host);
@@ -597,18 +636,14 @@
 
     protected synchronized void queueRequest(Request request, boolean head) {
         HttpHost host = request.mProxyHost == null ? request.mHost : request.mProxyHost;
-        LinkedList<Request> reqList;
+        RequestSet reqList;
         if (mPending.containsKey(host)) {
             reqList = mPending.get(host);
         } else {
-            reqList = new LinkedList<Request>();
+            reqList = new RequestSet();
             mPending.put(host, reqList);
         }
-        if (head) {
-            reqList.addFirst(request);
-        } else {
-            reqList.add(request);
-        }
+        reqList.add(request, head);
     }
 
 
@@ -621,12 +656,12 @@
     }
 
     /* helper */
-    private Request removeFirst(LinkedHashMap<HttpHost, LinkedList<Request>> requestQueue) {
+    private Request removeFirst(LinkedHashMap<HttpHost, RequestSet> requestQueue) {
         Request ret = null;
-        Iterator<Map.Entry<HttpHost, LinkedList<Request>>> iter = requestQueue.entrySet().iterator();
+        Iterator<Map.Entry<HttpHost, RequestSet>> iter = requestQueue.entrySet().iterator();
         if (iter.hasNext()) {
-            Map.Entry<HttpHost, LinkedList<Request>> entry = iter.next();
-            LinkedList<Request> reqList = entry.getValue();
+            Map.Entry<HttpHost, RequestSet> entry = iter.next();
+            RequestSet reqList = entry.getValue();
             ret = reqList.removeFirst();
             if (reqList.isEmpty()) {
                 requestQueue.remove(entry.getKey());
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index d40ea6b..b0fc78e 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -662,6 +662,25 @@
     public static final native int getBinderDeathObjectCount();
 
     /**
+     * Primes the register map cache.
+     *
+     * Only works for classes in the bootstrap class loader.  Does not
+     * cause classes to be loaded if they're not already present.
+     *
+     * The classAndMethodDesc argument is a concatentation of the VM-internal
+     * class descriptor, method name, and method descriptor.  Examples:
+     *     Landroid/os/Looper;.loop:()V
+     *     Landroid/app/ActivityThread;.main:([Ljava/lang/String;)V
+     *
+     * @param classAndMethodDesc the method to prepare
+     *
+     * @hide
+     */
+    public static final boolean cacheRegisterMap(String classAndMethodDesc) {
+        return VMDebug.cacheRegisterMap(classAndMethodDesc);
+    }
+
+    /**
      * API for gathering and querying instruction counts.
      *
      * Example usage:
diff --git a/core/java/android/os/Exec.java b/core/java/android/os/Exec.java
deleted file mode 100644
index a50d5fe..0000000
--- a/core/java/android/os/Exec.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import java.io.FileDescriptor;
-
-/**
- * @hide
- * Tools for executing commands.  Not for public consumption.
- */
-
-public class Exec
-{
-    /**
-     * @param cmd The command to execute
-     * @param arg0 The first argument to the command, may be null
-     * @param arg1 the second argument to the command, may be null
-     * @return the file descriptor of the started process.
-     * 
-     */
-    public static FileDescriptor createSubprocess(
-        String cmd, String arg0, String arg1) {
-        return createSubprocess(cmd, arg0, arg1, null);
-    }
-    
-    /**
-     * @param cmd The command to execute
-     * @param arg0 The first argument to the command, may be null
-     * @param arg1 the second argument to the command, may be null
-     * @param processId A one-element array to which the process ID of the
-     * started process will be written.
-     * @return the file descriptor of the started process.
-     * 
-     */
-     public static native FileDescriptor createSubprocess(
-        String cmd, String arg0, String arg1, int[] processId);
-    
-     public static native void setPtyWindowSize(FileDescriptor fd,
-       int row, int col, int xpixel, int ypixel);
-    /**
-     * Causes the calling thread to wait for the process associated with the
-     * receiver to finish executing.
-     * 
-     * @return The exit value of the Process being waited on
-     * 
-     */
-    public static native int waitFor(int processId);
-}
-
diff --git a/core/java/android/os/LatencyTimer.java b/core/java/android/os/LatencyTimer.java
new file mode 100644
index 0000000..ed2f0f9
--- /dev/null
+++ b/core/java/android/os/LatencyTimer.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * A class to help with measuring latency in your code.
+ * 
+ * Suggested usage:
+ * 1) Instanciate a LatencyTimer as a class field.
+ *      private [static] LatencyTimer mLt = new LatencyTimer(100, 1000);
+ * 2) At various points in the code call sample with a string and the time delta to some fixed time.
+ *    The string should be unique at each point of the code you are measuring.
+ *      mLt.sample("before processing event", System.nanoTime() - event.getEventTimeNano());
+ *      processEvent(event);
+ *      mLt.sample("after processing event ", System.nanoTime() - event.getEventTimeNano());
+ *
+ * @hide
+ */
+public final class LatencyTimer
+{
+    final String TAG = "LatencyTimer";
+    final int mSampleSize;
+    final int mScaleFactor;
+    volatile HashMap<String, long[]> store = new HashMap<String, long[]>();
+
+    /**
+    * Creates a LatencyTimer object
+    * @param sampleSize number of samples to collect before printing out the average
+    * @param scaleFactor divisor used to make each sample smaller to prevent overflow when
+    *        (sampleSize * average sample value)/scaleFactor > Long.MAX_VALUE
+    */
+    public LatencyTimer(int sampleSize, int scaleFactor) {
+        if (scaleFactor == 0) {
+            scaleFactor = 1;
+        }
+        mScaleFactor = scaleFactor;
+        mSampleSize = sampleSize;
+    }
+
+    /**
+     * Add a sample delay for averaging.
+     * @param tag string used for printing out the result. This should be unique at each point of
+     *  this called.
+     * @param delta time difference from an unique point of reference for a particular iteration
+     */
+    public void sample(String tag, long delta) {
+        long[] array = getArray(tag);
+
+        // array[mSampleSize] holds the number of used entries
+        final int index = (int) array[mSampleSize]++;
+        array[index] = delta;
+        if (array[mSampleSize] == mSampleSize) {
+            long totalDelta = 0;
+            for (long d : array) {
+                totalDelta += d/mScaleFactor;
+            }
+            array[mSampleSize] = 0;
+            Log.i(TAG, tag + " average = " + totalDelta / mSampleSize);
+        }
+    }
+
+    private long[] getArray(String tag) {
+        long[] data = store.get(tag);
+        if (data == null) {
+            synchronized(store) {
+                data = store.get(tag);
+                if (data == null) {
+                    data = new long[mSampleSize + 1];
+                    store.put(tag, data);
+                    data[mSampleSize] = 0;
+                }
+            }
+        }
+        return data;
+    }
+}
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index c14925c..03542dd 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -52,7 +52,7 @@
     private static native void native_write(FileDescriptor fd, int address, byte[] buffer,
             int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
     private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException;
-    private static native boolean native_is_ashmem_region(FileDescriptor fd) throws IOException;
+    private static native int native_get_mapped_size(FileDescriptor fd) throws IOException;
 
     private FileDescriptor mFD;        // ashmem file descriptor
     private int mAddress;   // address of ashmem memory
@@ -300,7 +300,20 @@
      * @hide
      */
     public static boolean isMemoryFile(FileDescriptor fd) throws IOException {
-        return native_is_ashmem_region(fd);
+        return (native_get_mapped_size(fd) >= 0);
+    }
+
+    /**
+     * Returns the size of the memory file, rounded up to a page boundary, that
+     * the file descriptor refers to, or -1 if the file descriptor does not
+     * refer to a memory file.
+     *
+     * @throws IOException If <code>fd</code> is not a valid file descriptor.
+     *
+     * @hide
+     */
+    public static int getMappedSize(FileDescriptor fd) throws IOException {
+        return native_get_mapped_size(fd);
     }
 
     /**
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 23c0a7bf..5ab305e 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -22,7 +22,7 @@
  * Takes care of the grunt work of maintaining a list of remote interfaces,
  * typically for the use of performing callbacks from a
  * {@link android.app.Service} to its clients.  In particular, this:
- * 
+ *
  * <ul>
  * <li> Keeps track of a set of registered {@link IInterface} callbacks,
  * taking care to identify them through their underlying unique {@link IBinder}
@@ -34,13 +34,13 @@
  * multithreaded incoming calls, and a thread-safe way to iterate over a
  * snapshot of the list without holding its lock.
  * </ul>
- * 
+ *
  * <p>To use this class, simply create a single instance along with your
  * service, and call its {@link #register} and {@link #unregister} methods
  * as client register and unregister with your service.  To call back on to
  * the registered clients, use {@link #beginBroadcast},
  * {@link #getBroadcastItem}, and {@link #finishBroadcast}.
- * 
+ *
  * <p>If a registered callback's process goes away, this class will take
  * care of automatically removing it from the list.  If you want to do
  * additional work in this situation, you can create a subclass that
@@ -51,7 +51,7 @@
             = new HashMap<IBinder, Callback>();
     private Object[] mActiveBroadcast;
     private boolean mKilled = false;
-    
+
     private final class Callback implements IBinder.DeathRecipient {
         final E mCallback;
         final Object mCookie;
@@ -60,7 +60,7 @@
             mCallback = callback;
             mCookie = cookie;
         }
-        
+
         public void binderDied() {
             synchronized (mCallbacks) {
                 mCallbacks.remove(mCallback.asBinder());
@@ -68,7 +68,7 @@
             onCallbackDied(mCallback, mCookie);
         }
     }
-    
+
     /**
      * Simple version of {@link RemoteCallbackList#register(E, Object)}
      * that does not take a cookie object.
@@ -85,19 +85,20 @@
      * object is already in the list), then it will be left as-is.
      * Registrations are not counted; a single call to {@link #unregister}
      * will remove a callback after any number calls to register it.
-     * 
+     *
      * @param callback The callback interface to be added to the list.  Must
      * not be null -- passing null here will cause a NullPointerException.
      * Most services will want to check for null before calling this with
      * an object given from a client, so that clients can't crash the
      * service with bad data.
+     *
      * @param cookie Optional additional data to be associated with this
      * callback.
      * 
      * @return Returns true if the callback was successfully added to the list.
      * Returns false if it was not added, either because {@link #kill} had
      * previously been called or the callback's process has gone away.
-     * 
+     *
      * @see #unregister
      * @see #kill
      * @see #onCallbackDied
@@ -118,7 +119,7 @@
             }
         }
     }
-    
+
     /**
      * Remove from the list a callback that was previously added with
      * {@link #register}.  This uses the
@@ -126,14 +127,14 @@
      * find the previous registration.
      * Registrations are not counted; a single unregister call will remove
      * a callback after any number calls to {@link #register} for it.
-     * 
+     *
      * @param callback The callback to be removed from the list.  Passing
      * null here will cause a NullPointerException, so you will generally want
      * to check for null before calling.
-     * 
+     *
      * @return Returns true if the callback was found and unregistered.  Returns
      * false if the given callback was not found on the list.
-     * 
+     *
      * @see #register
      */
     public boolean unregister(E callback) {
@@ -146,13 +147,13 @@
             return false;
         }
     }
-    
+
     /**
      * Disable this callback list.  All registered callbacks are unregistered,
      * and the list is disabled so that future calls to {@link #register} will
      * fail.  This should be used when a Service is stopping, to prevent clients
      * from registering callbacks after it is stopped.
-     * 
+     *
      * @see #register
      */
     public void kill() {
@@ -164,7 +165,7 @@
             mKilled = true;
         }
     }
-    
+
     /**
      * Old version of {@link #onCallbackDied(E, Object)} that
      * does not provide a cookie.
@@ -189,7 +190,7 @@
     public void onCallbackDied(E callback, Object cookie) {
         onCallbackDied(callback);
     }
-    
+
     /**
      * Prepare to start making calls to the currently registered callbacks.
      * This creates a copy of the callback list, which you can retrieve items
@@ -198,12 +199,12 @@
      * same thread (usually by scheduling with {@link Handler} or
      * do your own synchronization.  You must call {@link #finishBroadcast}
      * when done.
-     * 
+     *
      * <p>A typical loop delivering a broadcast looks like this:
-     * 
+     *
      * <pre>
      * final int N = callbacks.beginBroadcast();
-     * for (int i=0; i<N; i++) {
+     * for (int i=0; i&lt;N; i++) {
      *     try {
      *         callbacks.getBroadcastItem(i).somethingHappened();
      *     } catch (RemoteException e) {
@@ -212,11 +213,11 @@
      *     }
      * }
      * callbacks.finishBroadcast();</pre>
-     * 
+     *
      * @return Returns the number of callbacks in the broadcast, to be used
      * with {@link #getBroadcastItem} to determine the range of indices you
      * can supply.
-     * 
+     *
      * @see #getBroadcastItem
      * @see #finishBroadcast
      */
@@ -237,26 +238,26 @@
             return i;
         }
     }
-    
+
     /**
      * Retrieve an item in the active broadcast that was previously started
      * with {@link #beginBroadcast}.  This can <em>only</em> be called after
      * the broadcast is started, and its data is no longer valid after
      * calling {@link #finishBroadcast}.
-     * 
+     *
      * <p>Note that it is possible for the process of one of the returned
      * callbacks to go away before you call it, so you will need to catch
      * {@link RemoteException} when calling on to the returned object.
      * The callback list itself, however, will take care of unregistering
      * these objects once it detects that it is no longer valid, so you can
      * handle such an exception by simply ignoring it.
-     * 
+     *
      * @param index Which of the registered callbacks you would like to
      * retrieve.  Ranges from 0 to 1-{@link #beginBroadcast}.
-     * 
+     *
      * @return Returns the callback interface that you can call.  This will
      * always be non-null.
-     * 
+     *
      * @see #beginBroadcast
      */
     public E getBroadcastItem(int index) {
@@ -272,12 +273,12 @@
     public Object getBroadcastCookie(int index) {
         return ((Callback)mActiveBroadcast[index]).mCookie;
     }
-    
+
     /**
      * Clean up the state of a broadcast previously initiated by calling
      * {@link #beginBroadcast}.  This must always be called when you are done
      * with a broadcast.
-     * 
+     *
      * @see #beginBroadcast
      */
     public void finishBroadcast() {
diff --git a/core/java/android/preference/RingtonePreference.java b/core/java/android/preference/RingtonePreference.java
index 6beb06d..b46f180 100644
--- a/core/java/android/preference/RingtonePreference.java
+++ b/core/java/android/preference/RingtonePreference.java
@@ -31,8 +31,9 @@
  * The chosen ringtone's URI will be persisted as a string.
  * <p>
  * If the user chooses the "Default" item, the saved string will be one of
- * {@link System#DEFAULT_RINGTONE_URI} or
- * {@link System#DEFAULT_NOTIFICATION_URI}. If the user chooses the "Silent"
+ * {@link System#DEFAULT_RINGTONE_URI},
+ * {@link System#DEFAULT_NOTIFICATION_URI}, or
+ * {@link System#DEFAULT_ALARM_ALERT_URI}. If the user chooses the "Silent"
  * item, the saved string will be an empty string.
  * 
  * @attr ref android.R.styleable#RingtonePreference_ringtoneType
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index 20702a1..abdcd93 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -22,6 +22,7 @@
 import android.media.Ringtone;
 import android.media.RingtoneManager;
 import android.media.AudioManager;
+import android.net.Uri;
 import android.os.Handler;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
@@ -147,10 +148,16 @@
                     System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),
                     false, mVolumeObserver);
     
-            mRingtone = RingtoneManager.getRingtone(mContext,
-                    mStreamType == AudioManager.STREAM_NOTIFICATION
-                            ? Settings.System.DEFAULT_NOTIFICATION_URI
-                            : Settings.System.DEFAULT_RINGTONE_URI);
+            Uri defaultUri = null;
+            if (mStreamType == AudioManager.STREAM_RING) {
+                defaultUri = Settings.System.DEFAULT_RINGTONE_URI;
+            } else if (mStreamType == AudioManager.STREAM_NOTIFICATION) {
+                defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+            } else {
+                defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI;
+            }
+
+            mRingtone = RingtoneManager.getRingtone(mContext, defaultUri);
             mRingtone.setStreamType(mStreamType);
         }
         
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index 1ba5e25e..b95e4e1 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -107,7 +107,7 @@
     public static final String[] HISTORY_PROJECTION = new String[] {
         BookmarkColumns._ID, BookmarkColumns.URL, BookmarkColumns.VISITS,
         BookmarkColumns.DATE, BookmarkColumns.BOOKMARK, BookmarkColumns.TITLE,
-        BookmarkColumns.FAVICON };
+        BookmarkColumns.FAVICON, BookmarkColumns.THUMBNAIL };
 
     /* these indices dependent on HISTORY_PROJECTION */
     public static final int HISTORY_PROJECTION_ID_INDEX = 0;
@@ -117,6 +117,10 @@
     public static final int HISTORY_PROJECTION_BOOKMARK_INDEX = 4;
     public static final int HISTORY_PROJECTION_TITLE_INDEX = 5;
     public static final int HISTORY_PROJECTION_FAVICON_INDEX = 6;
+    /**
+     * @hide
+     */
+    public static final int HISTORY_PROJECTION_THUMBNAIL_INDEX = 7;
 
     /* columns needed to determine whether to truncate history */
     public static final String[] TRUNCATE_HISTORY_PROJECTION = new String[] {
@@ -513,6 +517,10 @@
         public static final String TITLE = "title";
         public static final String CREATED = "created";
         public static final String FAVICON = "favicon";
+        /**
+         * @hide
+         */
+        public static final String THUMBNAIL = "thumbnail";
     }
 
     public static class SearchColumns implements BaseColumns {
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 4a709f6..3145166 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -32,6 +32,7 @@
 import android.text.format.Time;
 import android.util.Config;
 import android.util.Log;
+import android.accounts.Account;
 import com.android.internal.database.ArrayListCursor;
 import com.google.android.gdata.client.AndroidGDataClient;
 import com.google.android.gdata.client.AndroidXmlParserFactory;
@@ -157,11 +158,12 @@
          * @param account the account whose rows should be deleted
          * @return the count of rows that were deleted
          */
-        public static int deleteCalendarsForAccount(ContentResolver cr,
-                String account) {
+        public static int deleteCalendarsForAccount(ContentResolver cr, Account account) {
             // delete all calendars that match this account
-            return Calendar.Calendars.delete(cr, Calendar.Calendars._SYNC_ACCOUNT + "=?",
-                    new String[] {account});
+            return Calendar.Calendars.delete(cr,
+                    Calendar.Calendars._SYNC_ACCOUNT + "=? AND "
+                            + Calendar.Calendars._SYNC_ACCOUNT_TYPE + "=?",
+                    new String[] {account.mName, account.mType});
         }
 
         /**
@@ -170,9 +172,6 @@
         public static final Uri CONTENT_URI =
             Uri.parse("content://calendar/calendars");
 
-        public static final Uri LIVE_CONTENT_URI =
-            Uri.parse("content://calendar/calendars?update=1");
-
         /**
          * The default sort order for this table
          */
diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java
index 84fe184..5f84e57 100644
--- a/core/java/android/provider/Contacts.java
+++ b/core/java/android/provider/Contacts.java
@@ -30,6 +30,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.widget.ImageView;
+import android.accounts.Account;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
@@ -39,7 +40,7 @@
  */
 public class Contacts {
     private static final String TAG = "Contacts";
-    
+
     public static final String AUTHORITY = "contacts";
 
     /**
@@ -75,6 +76,12 @@
         public static final String _SYNC_ACCOUNT = "_sync_account";
 
         /**
+         * The _SYNC_ACCOUNT_TYPE to which this setting corresponds. This may be null.
+         * <P>Type: TEXT</P>
+         */
+        public static final String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
+
+        /**
          * The key of this setting.
          * <P>Type: TEXT</P>
          */
@@ -134,6 +141,7 @@
                 selectString = (account == null)
                         ? "_sync_account is null AND key=?"
                         : "_sync_account=? AND key=?";
+//                : "_sync_account=? AND _sync_account_type=? AND key=?";
                 selectArgs = (account == null)
                 ? new String[]{key}
                 : new String[]{account, key};
@@ -158,7 +166,8 @@
             // the account name is, so we're using a global setting for SYNC_EVERYTHING.
             // Some day when we add multiple accounts to the UI this should honor the account
             // that was asked for.
-            //values.put(_SYNC_ACCOUNT, account);
+            //values.put(_SYNC_ACCOUNT, account.mName);
+            //values.put(_SYNC_ACCOUNT_TYPE, account.mType);
             values.put(KEY, key);
             values.put(VALUE, value);
             cr.update(Settings.CONTENT_URI, values, null, null);
@@ -182,7 +191,7 @@
          * <p>Type: TEXT</P>
          */
         public static final String PHONETIC_NAME = "phonetic_name";
-        
+
         /**
          * The display name. If name is not null name, else if number is not null number,
          * else if email is not null email.
@@ -197,7 +206,7 @@
          * @hide Used only in Contacts application for now.
          */
         public static final String SORT_STRING = "sort_string";
-        
+
         /**
          * Notes about the person.
          * <P>Type: TEXT</P>
@@ -239,7 +248,7 @@
          * The server version of the photo
          * <P>Type: TEXT (the version number portion of the photo URI)</P>
          */
-        public static final String PHOTO_VERSION = "photo_version";       
+        public static final String PHOTO_VERSION = "photo_version";
     }
 
     /**
@@ -278,14 +287,14 @@
          * additional path segment after this URI. This matches any people with
          * at least one E-mail or IM {@link ContactMethods} that match the
          * filter.
-         * 
+         *
          * Not exposed because we expect significant changes in the contacts
          * schema and do not want to have to support this.
          * @hide
          */
         public static final Uri WITH_EMAIL_OR_IM_FILTER_URI =
             Uri.parse("content://contacts/people/with_email_or_im_filter");
-        
+
         /**
          * The MIME type of {@link #CONTENT_URI} providing a directory of
          * people.
@@ -370,13 +379,13 @@
             if (groupId == 0) {
                 throw new IllegalStateException("Failed to find the My Contacts group");
             }
-            
+
             return addToGroup(resolver, personId, groupId);
         }
 
         /**
          * Adds a person to a group referred to by name.
-         * 
+         *
          * @param resolver the resolver to use
          * @param personId the person to add to the group
          * @param groupName the name of the group to add the contact to
@@ -400,13 +409,13 @@
             if (groupId == 0) {
                 throw new IllegalStateException("Failed to find the My Contacts group");
             }
-            
+
             return addToGroup(resolver, personId, groupId);
         }
 
         /**
          * Adds a person to a group.
-         * 
+         *
          * @param resolver the resolver to use
          * @param personId the person to add to the group
          * @param groupId the group to add the person to
@@ -418,14 +427,14 @@
             values.put(GroupMembership.GROUP_ID, groupId);
             return resolver.insert(GroupMembership.CONTENT_URI, values);
         }
-        
+
         private static final String[] GROUPS_PROJECTION = new String[] {
             Groups._ID,
         };
 
         /**
          * Creates a new contacts and adds it to the "My Contacts" group.
-         * 
+         *
          * @param resolver the ContentResolver to use
          * @param values the values to use when creating the contact
          * @return the URI of the contact, or null if the operation fails
@@ -463,7 +472,7 @@
             values.put(Photos.DATA, data);
             cr.update(photoUri, values, null, null);
         }
-        
+
         /**
          * Opens an InputStream for the person's photo and returns the photo as a Bitmap.
          * If the person's photo isn't present returns the placeholderImageResource instead.
@@ -730,7 +739,7 @@
             CharSequence display = "";
 
             if (type != People.Phones.TYPE_CUSTOM) {
-                CharSequence[] labels = labelArray != null? labelArray 
+                CharSequence[] labels = labelArray != null? labelArray
                         : context.getResources().getTextArray(
                                 com.android.internal.R.array.phoneTypes);
                 try {
@@ -750,7 +759,7 @@
                 CharSequence label) {
             return getDisplayLabel(context, type, label, null);
         }
-        
+
         /**
          * The content:// style URL for this table
          */
@@ -846,6 +855,12 @@
         public static final String GROUP_SYNC_ACCOUNT = "group_sync_account";
 
         /**
+         * The account type of the group.
+         * <P>Type: TEXT</P>
+         */
+        public static final String GROUP_SYNC_ACCOUNT_TYPE = "group_sync_account_type";
+
+        /**
          * The row id of the person.
          * <P>Type: TEXT</P>
          */
@@ -969,7 +984,7 @@
             throw new IllegalArgumentException(
                     "the value is not a valid encoded protocol, " + encodedString);
         }
-        
+
         /**
          * This looks up the provider name defined in
          * {@link android.provider.Im.ProviderNames} from the predefined IM protocol id.
@@ -1022,13 +1037,7 @@
                         }
                     } else {
                         if (!TextUtils.isEmpty(label)) {
-                            if (label.toString().equals(MOBILE_EMAIL_TYPE_NAME)) {
-                                display =
-                                    context.getString(
-                                            com.android.internal.R.string.mobileEmailTypeName);
-                            } else {
-                                display = label;
-                            }
+                            display = label;
                         }
                     }
                     break;
@@ -1188,7 +1197,7 @@
 
         /**
          * Gets the resource ID for the proper presence icon.
-         * 
+         *
          * @param status the status to get the icon for
          * @return the resource ID for the proper presence icon
          */
@@ -1196,17 +1205,17 @@
             switch (status) {
                 case Contacts.People.AVAILABLE:
                     return com.android.internal.R.drawable.presence_online;
-    
+
                 case Contacts.People.IDLE:
                 case Contacts.People.AWAY:
                     return com.android.internal.R.drawable.presence_away;
-    
+
                 case Contacts.People.DO_NOT_DISTURB:
                     return com.android.internal.R.drawable.presence_busy;
-    
+
                 case Contacts.People.INVISIBLE:
                     return com.android.internal.R.drawable.presence_invisible;
-                    
+
                 case Contacts.People.OFFLINE:
                 default:
                     return com.android.internal.R.drawable.presence_offline;
@@ -1229,7 +1238,7 @@
      */
     public interface OrganizationColumns {
         /**
-         * The type of the the phone number.
+         * The type of the organizations.
          * <P>Type: INTEGER (one of the constants below)</P>
          */
         public static final String TYPE = "type";
@@ -1446,28 +1455,27 @@
          * This is the intent that is fired when a search suggestion is clicked on.
          */
         public static final String SEARCH_SUGGESTION_CLICKED =
-                "android.provider.Contacts.SEARCH_SUGGESTION_CLICKED";
+                ContactsContract.Intents.SEARCH_SUGGESTION_CLICKED;
 
         /**
-         * This is the intent that is fired when a search suggestion for dialing a number 
+         * This is the intent that is fired when a search suggestion for dialing a number
          * is clicked on.
          */
         public static final String SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED =
-                "android.provider.Contacts.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED";
+                ContactsContract.Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED;
 
         /**
          * This is the intent that is fired when a search suggestion for creating a contact
          * is clicked on.
          */
         public static final String SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED =
-                "android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED";
+                ContactsContract.Intents.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED;
 
         /**
          * Starts an Activity that lets the user pick a contact to attach an image to.
          * After picking the contact it launches the image cropper in face detection mode.
          */
-        public static final String ATTACH_IMAGE =
-                "com.android.contacts.action.ATTACH_IMAGE";
+        public static final String ATTACH_IMAGE = ContactsContract.Intents.ATTACH_IMAGE;
 
         /**
          * Takes as input a data URI with a mailto: or tel: scheme. If a single
@@ -1493,7 +1501,7 @@
          * prompting the user when the contact doesn't exist.
          */
         public static final String SHOW_OR_CREATE_CONTACT =
-                "com.android.contacts.action.SHOW_OR_CREATE_CONTACT";
+                ContactsContract.Intents.SHOW_OR_CREATE_CONTACT;
 
         /**
          * Used with {@link #SHOW_OR_CREATE_CONTACT} to force creating a new
@@ -1502,9 +1510,8 @@
          * <p>
          * Type: BOOLEAN
          */
-        public static final String EXTRA_FORCE_CREATE =
-                "com.android.contacts.action.FORCE_CREATE";
-        
+        public static final String EXTRA_FORCE_CREATE = ContactsContract.Intents.EXTRA_FORCE_CREATE;
+
         /**
          * Used with {@link #SHOW_OR_CREATE_CONTACT} to specify an exact
          * description to be shown when prompting user about creating a new
@@ -1513,7 +1520,7 @@
          * Type: STRING
          */
         public static final String EXTRA_CREATE_DESCRIPTION =
-            "com.android.contacts.action.CREATE_DESCRIPTION";
+                ContactsContract.Intents.EXTRA_CREATE_DESCRIPTION;
 
         /**
          * Intents related to the Contacts app UI.
@@ -1522,43 +1529,42 @@
             /**
              * The action for the default contacts list tab.
              */
-            public static final String LIST_DEFAULT =
-                    "com.android.contacts.action.LIST_DEFAULT";
+            public static final String LIST_DEFAULT = ContactsContract.Intents.UI.LIST_DEFAULT;
 
             /**
              * The action for the contacts list tab.
              */
             public static final String LIST_GROUP_ACTION =
-                    "com.android.contacts.action.LIST_GROUP";
+                    ContactsContract.Intents.UI.LIST_GROUP_ACTION;
 
             /**
              * When in LIST_GROUP_ACTION mode, this is the group to display.
              */
-            public static final String GROUP_NAME_EXTRA_KEY = "com.android.contacts.extra.GROUP";
-            
+            public static final String GROUP_NAME_EXTRA_KEY =
+                    ContactsContract.Intents.UI.GROUP_NAME_EXTRA_KEY;
             /**
              * The action for the all contacts list tab.
              */
             public static final String LIST_ALL_CONTACTS_ACTION =
-                    "com.android.contacts.action.LIST_ALL_CONTACTS";
+                    ContactsContract.Intents.UI.LIST_ALL_CONTACTS_ACTION;
 
             /**
              * The action for the contacts with phone numbers list tab.
              */
             public static final String LIST_CONTACTS_WITH_PHONES_ACTION =
-                    "com.android.contacts.action.LIST_CONTACTS_WITH_PHONES";
+                    ContactsContract.Intents.UI.LIST_CONTACTS_WITH_PHONES_ACTION;
 
             /**
              * The action for the starred contacts list tab.
              */
             public static final String LIST_STARRED_ACTION =
-                    "com.android.contacts.action.LIST_STARRED";
+                    ContactsContract.Intents.UI.LIST_STARRED_ACTION;
 
             /**
              * The action for the frequent contacts list tab.
              */
             public static final String LIST_FREQUENT_ACTION =
-                    "com.android.contacts.action.LIST_FREQUENT";
+                    ContactsContract.Intents.UI.LIST_FREQUENT_ACTION;
 
             /**
              * The action for the "strequent" contacts list tab. It first lists the starred
@@ -1566,15 +1572,15 @@
              * order of the number of times they have been contacted.
              */
             public static final String LIST_STREQUENT_ACTION =
-                    "com.android.contacts.action.LIST_STREQUENT";
+                    ContactsContract.Intents.UI.LIST_STREQUENT_ACTION;
 
             /**
              * A key for to be used as an intent extra to set the activity
              * title to a custom String value.
              */
             public static final String TITLE_EXTRA_KEY =
-                "com.android.contacts.extra.TITLE_EXTRA";
-            
+                    ContactsContract.Intents.UI.TITLE_EXTRA_KEY;
+
             /**
              * Activity Action: Display a filtered list of contacts
              * <p>
@@ -1583,15 +1589,15 @@
              * <p>
              * Output: Nothing.
              */
-            public static final String FILTER_CONTACTS_ACTION = 
-                "com.android.contacts.action.FILTER_CONTACTS";
-            
+            public static final String FILTER_CONTACTS_ACTION =
+                    ContactsContract.Intents.UI.FILTER_CONTACTS_ACTION;
+
             /**
              * Used as an int extra field in {@link #FILTER_CONTACTS_ACTION}
              * intents to supply the text on which to filter.
              */
-            public static final String FILTER_TEXT_EXTRA_KEY = 
-                "com.android.contacts.extra.FILTER_TEXT";
+            public static final String FILTER_TEXT_EXTRA_KEY =
+                    ContactsContract.Intents.UI.FILTER_TEXT_EXTRA_KEY;
         }
 
         /**
@@ -1600,170 +1606,179 @@
          */
         public static final class Insert {
             /** The action code to use when adding a contact */
-            public static final String ACTION = Intent.ACTION_INSERT;
-
+            public static final String ACTION = ContactsContract.Intents.Insert.ACTION;
             /**
              * If present, forces a bypass of quick insert mode.
              */
-            public static final String FULL_MODE = "full_mode";
-
+            public static final String FULL_MODE = ContactsContract.Intents.Insert.FULL_MODE;
             /**
              * The extra field for the contact name.
              * <P>Type: String</P>
              */
-            public static final String NAME = "name";
+            public static final String NAME = ContactsContract.Intents.Insert.NAME;
 
             /**
              * The extra field for the contact phonetic name.
              * <P>Type: String</P>
              */
-            public static final String PHONETIC_NAME = "phonetic_name";
+            public static final String PHONETIC_NAME =
+                    ContactsContract.Intents.Insert.PHONETIC_NAME;
 
             /**
              * The extra field for the contact company.
              * <P>Type: String</P>
              */
-            public static final String COMPANY = "company";
+            public static final String COMPANY = ContactsContract.Intents.Insert.COMPANY;
 
             /**
              * The extra field for the contact job title.
              * <P>Type: String</P>
              */
-            public static final String JOB_TITLE = "job_title";
+            public static final String JOB_TITLE = ContactsContract.Intents.Insert.JOB_TITLE;
 
             /**
              * The extra field for the contact notes.
              * <P>Type: String</P>
              */
-            public static final String NOTES = "notes";
+            public static final String NOTES = ContactsContract.Intents.Insert.NOTES;
 
             /**
              * The extra field for the contact phone number.
              * <P>Type: String</P>
              */
-            public static final String PHONE = "phone";
+            public static final String PHONE = ContactsContract.Intents.Insert.PHONE;
 
             /**
              * The extra field for the contact phone number type.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns},
              *  or a string specifying a custom label.</P>
              */
-            public static final String PHONE_TYPE = "phone_type";
+            public static final String PHONE_TYPE = ContactsContract.Intents.Insert.PHONE_TYPE;
 
             /**
              * The extra field for the phone isprimary flag.
              * <P>Type: boolean</P>
              */
-            public static final String PHONE_ISPRIMARY = "phone_isprimary";
+            public static final String PHONE_ISPRIMARY =
+                    ContactsContract.Intents.Insert.PHONE_ISPRIMARY;
 
             /**
              * The extra field for an optional second contact phone number.
              * <P>Type: String</P>
              */
-            public static final String SECONDARY_PHONE = "secondary_phone";
+            public static final String SECONDARY_PHONE =
+                    ContactsContract.Intents.Insert.SECONDARY_PHONE;
 
             /**
              * The extra field for an optional second contact phone number type.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns},
              *  or a string specifying a custom label.</P>
              */
-            public static final String SECONDARY_PHONE_TYPE = "secondary_phone_type";
+            public static final String SECONDARY_PHONE_TYPE =
+                    ContactsContract.Intents.Insert.SECONDARY_PHONE_TYPE;
 
             /**
              * The extra field for an optional third contact phone number.
              * <P>Type: String</P>
              */
-            public static final String TERTIARY_PHONE = "tertiary_phone";
+            public static final String TERTIARY_PHONE =
+                    ContactsContract.Intents.Insert.TERTIARY_PHONE;
 
             /**
              * The extra field for an optional third contact phone number type.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns},
              *  or a string specifying a custom label.</P>
              */
-            public static final String TERTIARY_PHONE_TYPE = "tertiary_phone_type";
+            public static final String TERTIARY_PHONE_TYPE =
+                    ContactsContract.Intents.Insert.TERTIARY_PHONE_TYPE;
 
             /**
              * The extra field for the contact email address.
              * <P>Type: String</P>
              */
-            public static final String EMAIL = "email";
+            public static final String EMAIL = ContactsContract.Intents.Insert.EMAIL;
 
             /**
              * The extra field for the contact email type.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
              *  or a string specifying a custom label.</P>
              */
-            public static final String EMAIL_TYPE = "email_type";
+            public static final String EMAIL_TYPE = ContactsContract.Intents.Insert.EMAIL_TYPE;
 
             /**
              * The extra field for the email isprimary flag.
              * <P>Type: boolean</P>
              */
-            public static final String EMAIL_ISPRIMARY = "email_isprimary";
+            public static final String EMAIL_ISPRIMARY =
+                    ContactsContract.Intents.Insert.EMAIL_ISPRIMARY;
 
             /**
              * The extra field for an optional second contact email address.
              * <P>Type: String</P>
              */
-            public static final String SECONDARY_EMAIL = "secondary_email";
+            public static final String SECONDARY_EMAIL =
+                    ContactsContract.Intents.Insert.SECONDARY_EMAIL;
 
             /**
              * The extra field for an optional second contact email type.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
              *  or a string specifying a custom label.</P>
              */
-            public static final String SECONDARY_EMAIL_TYPE = "secondary_email_type";
+            public static final String SECONDARY_EMAIL_TYPE =
+                    ContactsContract.Intents.Insert.SECONDARY_EMAIL_TYPE;
 
             /**
              * The extra field for an optional third contact email address.
              * <P>Type: String</P>
              */
-            public static final String TERTIARY_EMAIL = "tertiary_email";
+            public static final String TERTIARY_EMAIL =
+                    ContactsContract.Intents.Insert.TERTIARY_EMAIL;
 
             /**
              * The extra field for an optional third contact email type.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
              *  or a string specifying a custom label.</P>
              */
-            public static final String TERTIARY_EMAIL_TYPE = "tertiary_email_type";
+            public static final String TERTIARY_EMAIL_TYPE =
+                    ContactsContract.Intents.Insert.TERTIARY_EMAIL_TYPE;
 
             /**
              * The extra field for the contact postal address.
              * <P>Type: String</P>
              */
-            public static final String POSTAL = "postal";
+            public static final String POSTAL = ContactsContract.Intents.Insert.POSTAL;
 
             /**
              * The extra field for the contact postal address type.
              * <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
              *  or a string specifying a custom label.</P>
              */
-            public static final String POSTAL_TYPE = "postal_type";
+            public static final String POSTAL_TYPE = ContactsContract.Intents.Insert.POSTAL_TYPE;
 
             /**
              * The extra field for the postal isprimary flag.
              * <P>Type: boolean</P>
              */
-            public static final String POSTAL_ISPRIMARY = "postal_isprimary";
+            public static final String POSTAL_ISPRIMARY = ContactsContract.Intents.Insert.POSTAL_ISPRIMARY;
 
             /**
              * The extra field for an IM handle.
              * <P>Type: String</P>
              */
-            public static final String IM_HANDLE = "im_handle";
+            public static final String IM_HANDLE = ContactsContract.Intents.Insert.IM_HANDLE;
 
             /**
              * The extra field for the IM protocol
              * <P>Type: the result of {@link Contacts.ContactMethods#encodePredefinedImProtocol}
              * or {@link Contacts.ContactMethods#encodeCustomImProtocol}.</P>
              */
-            public static final String IM_PROTOCOL = "im_protocol";
+            public static final String IM_PROTOCOL = ContactsContract.Intents.Insert.IM_PROTOCOL;
 
             /**
              * The extra field for the IM isprimary flag.
              * <P>Type: boolean</P>
              */
-            public static final String IM_ISPRIMARY = "im_isprimary";
+            public static final String IM_ISPRIMARY = ContactsContract.Intents.Insert.IM_ISPRIMARY;
         }
     }
 }
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
new file mode 100644
index 0000000..5cc3315
--- /dev/null
+++ b/core/java/android/provider/ContactsContract.java
@@ -0,0 +1,1418 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.provider;
+
+import java.util.Arrays;
+import java.util.List;
+
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+
+/**
+ * The contract between the contacts provider and applications. Contains definitions
+ * for the supported URIs and columns.
+ *
+ * @hide
+ */
+public final class ContactsContract {
+    /** The authority for the contacts provider */
+    public static final String AUTHORITY = "com.android.contacts";
+    /** A content:// style uri to the authority for the contacts provider */
+    public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+    public interface AccountsColumns {
+        /**
+         * The name of this account data
+         * <P>Type: TEXT</P>
+         */
+        public static final String NAME = "name";
+        /**
+         * The name of this account data
+         * <P>Type: TEXT</P>
+         */
+        public static final String TYPE = "type";
+        /**
+         * The name of this account data
+         * <P>Type: TEXT</P>
+         */
+        public static final String DATA1 = "data1";
+
+        /**
+         * The value for this account data
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATA2 = "data2";
+
+        /**
+         * The value for this account data
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATA3 = "data3";
+
+        /**
+         * The value for this account data
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATA4 = "data4";
+
+        /**
+         * The value for this account data
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATA5 = "data5";
+    }
+
+    /**
+     * Constants for the aggregates table, which contains a record per group
+     * of contact representing the same person.
+     */
+    public static final class Accounts implements BaseColumns, AccountsColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private Accounts()  {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "accounts");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * account data.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contacts_account";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a account
+         */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contacts_account";
+    }
+
+    public interface AggregatesColumns {
+        /**
+         * The display name for the contact.
+         * <P>Type: TEXT</P>
+         */
+        public static final String DISPLAY_NAME = "display_name";
+
+        /**
+         * The number of times a person has been contacted
+         * <P>Type: INTEGER</P>
+         */
+        public static final String TIMES_CONTACTED = "times_contacted";
+
+        /**
+         * The last time a person was contacted.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String LAST_TIME_CONTACTED = "last_time_contacted";
+
+        /**
+         * Is the contact starred?
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String STARRED = "starred";
+
+        /**
+         * A custom ringtone associated with a person. Not always present.
+         * <P>Type: TEXT (URI to the ringtone)</P>
+         */
+        public static final String CUSTOM_RINGTONE = "custom_ringtone";
+
+        /**
+         * Whether the person should always be sent to voicemail. Not always
+         * present.
+         * <P>Type: INTEGER (0 for false, 1 for true)</P>
+         */
+        public static final String SEND_TO_VOICEMAIL = "send_to_voicemail";
+
+        /**
+         * Reference to the row in the data table holding the primary phone number.
+         * <P>Type: INTEGER REFERENCES data(_id)</P>
+         */
+        public static final String PRIMARY_PHONE_ID = "primary_phone_id";
+
+        /**
+         * Reference to the row in the data table holding the primary email address.
+         * <P>Type: INTEGER REFERENCES data(_id)</P>
+         */
+        public static final String PRIMARY_EMAIL_ID = "primary_email_id";
+
+        /**
+         * Reference to the row in the data table holding the photo.
+         * <P>Type: INTEGER REFERENCES data(_id)</P>
+         */
+        public static final String PHOTO_ID = "photo_id";
+    }
+
+    /**
+     * Constants for the aggregates table, which contains a record per group
+     * of contact representing the same person.
+     */
+    public static final class Aggregates implements BaseColumns, AggregatesColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private Aggregates()  {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "aggregates");
+
+        /**
+         * The content:// style URI for this table joined with useful data from
+         * {@link Data} and {@link Presence}.
+         */
+        public static final Uri CONTENT_SUMMARY_URI = Uri.withAppendedPath(AUTHORITY_URI,
+                "aggregates_summary");
+        /**
+         * The content:// style URI used for "type-to-filter" functionality on the
+         * {@link CONTENT_SUMMARY_URI} URI. The filter string will be used to match
+         * various parts of the aggregate name. The filter argument should be passed
+         * as an additional path segment after this URI.
+         */
+        public static final Uri CONTENT_SUMMARY_FILTER_URI = Uri.withAppendedPath(
+                CONTENT_SUMMARY_URI, "filter");
+        /**
+         * The content:// style URI for this table joined with useful data from
+         * {@link Data} and {@link Presence}, filtered to include only starred aggregates
+         * and the most frequently contacted aggregates.
+         */
+        public static final Uri CONTENT_SUMMARY_STREQUENT_URI = Uri.withAppendedPath(
+                CONTENT_SUMMARY_URI, "strequent");
+        /**
+         * The content:// style URI used for "type-to-filter" functionality on the
+         * {@link CONTENT_SUMMARY_STREQUENT_URI} URI. The filter string will be used to match
+         * various parts of the aggregate name. The filter argument should be passed
+         * as an additional path segment after this URI.
+         */
+        public static final Uri CONTENT_SUMMARY_STREQUENT_FILTER_URI = Uri.withAppendedPath(
+                CONTENT_SUMMARY_STREQUENT_URI, "filter");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * people.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person_aggregate";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+         * person.
+         */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person_aggregate";
+
+        /**
+         * A sub-directory of a single contact aggregate that contains all of their
+         * {@link Data} rows.
+         */
+        public static final class Data implements BaseColumns, DataColumns {
+            /**
+             * no public constructor since this is a utility class
+             */
+            private Data() {}
+
+            /**
+             * The directory twig for this sub-table
+             */
+            public static final String CONTENT_DIRECTORY = "data";
+        }
+
+        /**
+         * A sub-directory of a single contact aggregate that contains all aggregation suggestions
+         * (other aggregates).  The aggregation suggestions are computed based on approximate
+         * data matches with this aggregate.
+         */
+        public static final class AggregationSuggestions implements BaseColumns, AggregatesColumns {
+            /**
+             * No public constructor since this is a utility class
+             */
+            private AggregationSuggestions() {}
+
+            /**
+             * The directory twig for this sub-table
+             */
+            public static final String CONTENT_DIRECTORY = "suggestions";
+
+            /**
+             * An optional query parameter that can be supplied to limit the number of returned
+             * suggestions.
+             * <p>
+             * Type: INTEGER
+             */
+            public static final String MAX_SUGGESTIONS = "max_suggestions";
+        }
+    }
+
+
+    /**
+     * Constants for the contacts table, which contains the base contact information.
+     */
+    public static final class Contacts implements BaseColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private Contacts()  {}
+
+        /**
+         * The package name that owns this contact and all of its details. This
+         * package has control over the {@link #IS_RESTRICTED} flag, and can
+         * grant {@link RestrictionExceptions} to other packages.
+         */
+        public static final String PACKAGE = "package";
+
+        /**
+         * Flag indicating that this data entry has been restricted by the owner
+         * {@link #PACKAGE}.
+         */
+        public static final String IS_RESTRICTED = "is_restricted";
+
+        /**
+         * A reference to the {@link Accounts#_ID} that this data belongs to.
+         */
+        public static final String ACCOUNTS_ID = "accounts_id";
+
+        /**
+         * A reference to the {@link Aggregates#_ID} that this data belongs to.
+         */
+        public static final String AGGREGATE_ID = "aggregate_id";
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "contacts");
+
+        /**
+         * The content:// style URL for filtering people by email address. The
+         * filter argument should be passed as an additional path segment after
+         * this URI.
+         *
+         * @hide
+         */
+        public static final Uri CONTENT_FILTER_EMAIL_URI =
+                Uri.withAppendedPath(CONTENT_URI, "filter_email");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * people.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+         * person.
+         */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person";
+
+        /**
+         * A string that uniquely identifies this contact to its source, which is referred to
+         * by the {@link #ACCOUNTS_ID}
+         */
+        public static final String SOURCE_ID = "sourceid";
+
+        /**
+         * An integer that is updated whenever this contact or its data changes.
+         */
+        public static final String VERSION = "version";
+
+        /**
+         * Set to 1 whenever the version changes
+         */
+        public static final String DIRTY = "dirty";
+
+        /**
+         * A sub-directory of a single contact that contains all of their {@link Data} rows.
+         * To access this directory append
+         */
+        public static final class Data implements BaseColumns, DataColumns {
+            /**
+             * no public constructor since this is a utility class
+             */
+            private Data() {}
+
+            /**
+             * The directory twig for this sub-table
+             */
+            public static final String CONTENT_DIRECTORY = "data";
+        }
+    }
+
+    private interface DataColumns {
+        /**
+         * The mime-type of the item represented by this row.
+         */
+        public static final String MIMETYPE = "mimetype";
+
+        /**
+         * A reference to the {@link android.provider.ContactsContract.Contacts#_ID}
+         * that this data belongs to.
+         */
+        public static final String CONTACT_ID = "contact_id";
+
+        /**
+         * Whether this is the primary entry of its kind for the contact it belongs to
+         * <P>Type: INTEGER (if set, non-0 means true)</P>
+         */
+        public static final String IS_PRIMARY = "is_primary";
+
+        /**
+         * Whether this is the primary entry of its kind for the aggregate it belongs to. Any data
+         * record that is "super primary" must also be "primary".
+         * <P>Type: INTEGER (if set, non-0 means true)</P>
+         */
+        public static final String IS_SUPER_PRIMARY = "is_super_primary";
+
+        /**
+         * The version of this data record. This is a read-only value. The data column is
+         * guaranteed to not change without the version going up. This value is monotonically
+         * increasing.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATA_VERSION = "data_version";
+
+        /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+        public static final String DATA1 = "data1";
+        /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+        public static final String DATA2 = "data2";
+        /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+        public static final String DATA3 = "data3";
+        /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+        public static final String DATA4 = "data4";
+        /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+        public static final String DATA5 = "data5";
+        /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+        public static final String DATA6 = "data6";
+        /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+        public static final String DATA7 = "data7";
+        /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+        public static final String DATA8 = "data8";
+        /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+        public static final String DATA9 = "data9";
+        /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+        public static final String DATA10 = "data10";
+    }
+
+    /**
+     * Constants for the data table, which contains data points tied to a contact.
+     * For example, a phone number or email address. Each row in this table contains a type
+     * definition and some generic columns. Each data type can define the meaning for each of
+     * the generic columns.
+     */
+    public static final class Data implements BaseColumns, DataColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private Data() {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "data");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of data.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/data";
+    }
+
+    /**
+     * A table that represents the result of looking up a phone number, for example for caller ID.
+     * The table joins that data row for the phone number with the contact that owns the number.
+     * To perform a lookup you must append the number you want to find to {@link #CONTENT_URI}.
+     */
+    public static final class PhoneLookup implements BaseColumns, DataColumns, AggregatesColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private PhoneLookup() {}
+
+        /**
+         * The content:// style URI for this table. Append the phone number you want to lookup
+         * to this URI and query it to perform a lookup. For example:
+         *
+         * {@code
+         * Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_URI, phoneNumber);
+         * }
+         */
+        public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(AUTHORITY_URI,
+                "phone_lookup");
+    }
+
+    /**
+     * Additional data mixed in with {@link Im.CommonPresenceColumns} to link
+     * back to specific {@link ContactsContract.Aggregates#_ID} entries.
+     */
+    private interface PresenceColumns {
+        /**
+         * Reference to the {@link Aggregates#_ID} this presence references.
+         */
+        public static final String AGGREGATE_ID = "aggregate_id";
+
+        /**
+         * Reference to the {@link Data#_ID} entry that owns this presence.
+         */
+        public static final String DATA_ID = "data_id";
+
+        /**
+         * The IM service the presence is coming from. Formatted using either
+         * {@link Contacts.ContactMethods#encodePredefinedImProtocol} or
+         * {@link Contacts.ContactMethods#encodeCustomImProtocol}.
+         * <p>
+         * Type: STRING
+         */
+        public static final String IM_PROTOCOL = "im_protocol";
+
+        /**
+         * The IM handle the presence item is for. The handle is scoped to the
+         * {@link #IM_PROTOCOL}.
+         * <p>
+         * Type: STRING
+         */
+        public static final String IM_HANDLE = "im_handle";
+
+        /**
+         * The IM account for the local user that the presence data came from.
+         * <p>
+         * Type: STRING
+         */
+        public static final String IM_ACCOUNT = "im_account";
+    }
+
+    public static final class Presence implements BaseColumns, PresenceColumns,
+            Im.CommonPresenceColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private Presence() {
+        }
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "presence");
+
+        /**
+         * Gets the resource ID for the proper presence icon.
+         *
+         * @param status the status to get the icon for
+         * @return the resource ID for the proper presence icon
+         */
+        public static final int getPresenceIconResourceId(int status) {
+            switch (status) {
+                case AVAILABLE:
+                    return android.R.drawable.presence_online;
+                case IDLE:
+                case AWAY:
+                    return android.R.drawable.presence_away;
+                case DO_NOT_DISTURB:
+                    return android.R.drawable.presence_busy;
+                case INVISIBLE:
+                    return android.R.drawable.presence_invisible;
+                case OFFLINE:
+                default:
+                    return android.R.drawable.presence_offline;
+            }
+        }
+
+        /**
+         * Returns the precedence of the status code the higher number being the higher precedence.
+         *
+         * @param status The status code.
+         * @return An integer representing the precedence, 0 being the lowest.
+         */
+        public static final int getPresencePrecedence(int status) {
+            // Keep this function here incase we want to enforce a different precedence than the
+            // natural order of the status constants.
+            return status;
+        }
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * presence details.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-presence";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+         * presence detail.
+         */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im-presence";
+    }
+
+    /**
+     * Container for definitions of common data types stored in the {@link Data} table.
+     */
+    public static final class CommonDataKinds {
+        /**
+         * The {@link Data#PACKAGE} value for common data that should be shown
+         * using a default style.
+         */
+        public static final String PACKAGE_COMMON = "common";
+
+        /**
+         * Columns common across the specific types.
+         */
+        private interface BaseCommonColumns {
+            /**
+             * The package name that defines this type of data.
+             */
+            public static final String PACKAGE = "package";
+
+            /**
+             * The mime-type of the item represented by this row.
+             */
+            public static final String MIMETYPE = "mimetype";
+
+            /**
+             * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} that this
+             * data belongs to.
+             */
+            public static final String CONTACT_ID = "contact_id";
+        }
+
+        /**
+         * Columns common across the specific types.
+         */
+        private interface CommonColumns {
+            /**
+             * The type of data, for example Home or Work.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String TYPE = "data1";
+
+            /**
+             * The data for the contact method.
+             * <P>Type: TEXT</P>
+             */
+            public static final String DATA = "data2";
+
+            /**
+             * The user defined label for the the contact method.
+             * <P>Type: TEXT</P>
+             */
+            public static final String LABEL = "data3";
+        }
+
+        /**
+         * The base types that all "Typed" data kinds support.
+         */
+        public interface BaseTypes {
+
+            /**
+             * A custom type. The custom label should be supplied by user.
+             */
+            public static int TYPE_CUSTOM = 0;
+        }
+
+        /**
+         * Parts of the name.
+         */
+        public static final class StructuredName {
+            private StructuredName() {}
+
+            /** Mime-type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/name";
+
+            /**
+             * The given name for the contact.
+             * <P>Type: TEXT</P>
+             */
+            public static final String GIVEN_NAME = "data1";
+
+            /**
+             * The family name for the contact.
+             * <P>Type: TEXT</P>
+             */
+            public static final String FAMILY_NAME = "data2";
+
+            /**
+             * The contact's honorific prefix, e.g. "Sir"
+             * <P>Type: TEXT</P>
+             */
+            public static final String PREFIX = "data3";
+
+            /**
+             * The contact's middle name
+             * <P>Type: TEXT</P>
+             */
+            public static final String MIDDLE_NAME = "data4";
+
+            /**
+             * The contact's honorific suffix, e.g. "Jr"
+             */
+            public static final String SUFFIX = "data5";
+
+            /**
+             * The phonetic version of the given name for the contact.
+             * <P>Type: TEXT</P>
+             */
+            public static final String PHONETIC_GIVEN_NAME = "data6";
+
+            /**
+             * The phonetic version of the additional name for the contact.
+             * <P>Type: TEXT</P>
+             */
+            public static final String PHONETIC_MIDDLE_NAME = "data7";
+
+            /**
+             * The phonetic version of the family name for the contact.
+             * <P>Type: TEXT</P>
+             */
+            public static final String PHONETIC_FAMILY_NAME = "data8";
+
+            /**
+             * The name that should be used to display the contact.
+             * <P>Type: TEXT</P>
+             */
+            public static final String DISPLAY_NAME = "data9";
+        }
+
+        /**
+         * A nickname.
+         */
+        public static final class Nickname implements BaseTypes {
+            private Nickname() {}
+
+            /** Mime-type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/nickname";
+
+            /**
+             * The type of data, for example Home or Work.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String TYPE = "data1";
+
+            public static final int TYPE_DEFAULT = 1;
+            public static final int TYPE_OTHER_NAME = 2;
+            public static final int TYPE_MAINDEN_NAME = 3;
+            public static final int TYPE_SHORT_NAME = 4;
+            public static final int TYPE_INITIALS = 5;
+
+            /**
+             * The name itself
+             */
+            public static final String NAME = "data2";
+
+            /**
+             * The user provided label, only used if TYPE is {@link #TYPE_CUSTOM}.
+             * <P>Type: TEXT</P>
+             */
+            public static final String LABEL = "data3";
+        }
+
+        /**
+         * Common data definition for telephone numbers.
+         */
+        public static final class Phone implements BaseCommonColumns, CommonColumns, BaseTypes {
+            private Phone() {}
+
+            /** Mime-type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone";
+
+            /**
+             * The MIME type of {@link #CONTENT_URI} providing a directory of
+             * phones.
+             */
+            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/phone";
+
+            /**
+             * The content:// style URI for all data records of the
+             * {@link Phone.CONTENT_ITEM_TYPE} mimetype, combined with the associated contact
+             * and aggregate data.
+             */
+            public static final Uri CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI,
+                    "phones");
+
+            /**
+             * The content:// style URI for filtering data records of the
+             * {@link Phone.CONTENT_ITEM_TYPE} mimetype, combined with the associated contact
+             * and aggregate data. The filter argument should be passed
+             * as an additional path segment after this URI.
+             */
+            public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(CONTENT_URI,
+                    "filter");
+
+            public static final int TYPE_HOME = 1;
+            public static final int TYPE_MOBILE = 2;
+            public static final int TYPE_WORK = 3;
+            public static final int TYPE_FAX_WORK = 4;
+            public static final int TYPE_FAX_HOME = 5;
+            public static final int TYPE_PAGER = 6;
+            public static final int TYPE_OTHER = 7;
+
+            /**
+             * The phone number as the user entered it.
+             * <P>Type: TEXT</P>
+             */
+            public static final String NUMBER = "data2";
+        }
+
+        /**
+         * Common data definition for email addresses.
+         */
+        public static final class Email implements BaseCommonColumns, CommonColumns, BaseTypes {
+            private Email() {}
+
+            /** Mime-type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/email";
+
+            public static final int TYPE_HOME = 1;
+            public static final int TYPE_WORK = 2;
+            public static final int TYPE_OTHER = 3;
+        }
+
+        /**
+         * Common data definition for postal addresses.
+         */
+        public static final class Postal implements BaseCommonColumns, CommonColumns, BaseTypes {
+            private Postal() {}
+
+            /** Mime-type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/postal-address";
+
+            /**
+             * The MIME type of {@link #CONTENT_URI} providing a directory of
+             * postal addresses.
+             */
+            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/postal-address";
+
+            /**
+             * The content:// style URI for all data records of the
+             * {@link Postal.CONTENT_ITEM_TYPE} mimetype, combined with the associated contact
+             * and aggregate data.
+             */
+            public static final Uri CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI,
+                    "postals");
+
+            public static final int TYPE_HOME = 1;
+            public static final int TYPE_WORK = 2;
+            public static final int TYPE_OTHER = 3;
+        }
+
+       /**
+        * Common data definition for IM addresses.
+        */
+        public static final class Im implements BaseCommonColumns, CommonColumns, BaseTypes {
+            private Im() {}
+
+            /** Mime-type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im";
+
+            public static final int TYPE_HOME = 1;
+            public static final int TYPE_WORK = 2;
+            public static final int TYPE_OTHER = 3;
+
+            public static final String PROTOCOL = "data5";
+
+            /**
+             * The predefined IM protocol types. The protocol can either be non-present, one
+             * of these types, or a free-form string. These cases are encoded in the PROTOCOL
+             * column as:
+             * <ul>
+             * <li>null</li>
+             * <li>pre:&lt;an integer, one of the protocols below&gt;</li>
+             * <li>custom:&lt;a string&gt;</li>
+             * </ul>
+             */
+            public static final int PROTOCOL_AIM = 0;
+            public static final int PROTOCOL_MSN = 1;
+            public static final int PROTOCOL_YAHOO = 2;
+            public static final int PROTOCOL_SKYPE = 3;
+            public static final int PROTOCOL_QQ = 4;
+            public static final int PROTOCOL_GOOGLE_TALK = 5;
+            public static final int PROTOCOL_ICQ = 6;
+            public static final int PROTOCOL_JABBER = 7;
+
+            public static String encodePredefinedImProtocol(int protocol) {
+               return "pre:" + protocol;
+            }
+
+            public static String encodeCustomImProtocol(String protocolString) {
+               return "custom:" + protocolString;
+            }
+
+            public static Object decodeImProtocol(String encodedString) {
+               if (encodedString == null) {
+                   return null;
+               }
+
+               if (encodedString.startsWith("pre:")) {
+                   return Integer.parseInt(encodedString.substring(4));
+               }
+
+               if (encodedString.startsWith("custom:")) {
+                   return encodedString.substring(7);
+               }
+
+               throw new IllegalArgumentException(
+                       "the value is not a valid encoded protocol, " + encodedString);
+            }
+        }
+
+        /**
+         * Common data definition for organizations.
+         */
+        public static final class Organization implements BaseCommonColumns, BaseTypes {
+            private Organization() {}
+
+            /** Mime-type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization";
+
+            /**
+             * The type of data, for example Home or Work.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String TYPE = "data1";
+
+            public static final int TYPE_HOME = 1;
+            public static final int TYPE_WORK = 2;
+            public static final int TYPE_OTHER = 3;
+
+            /**
+             * The user provided label, only used if TYPE is {@link #TYPE_CUSTOM}.
+             * <P>Type: TEXT</P>
+             */
+            public static final String LABEL = "data2";
+
+            /**
+             * The company as the user entered it.
+             * <P>Type: TEXT</P>
+             */
+            public static final String COMPANY = "data3";
+
+            /**
+             * The position title at this company as the user entered it.
+             * <P>Type: TEXT</P>
+             */
+            public static final String TITLE = "data4";
+        }
+
+        /**
+         * Photo of the contact.
+         */
+        public static final class Photo implements BaseCommonColumns {
+            private Photo() {}
+
+            /** Mime-type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo";
+
+            /**
+             * Thumbnail photo of the contact. This is the raw bytes of an image
+             * that could be inflated using {@link BitmapFactory}.
+             * <p>
+             * Type: BLOB
+             */
+            public static final String PHOTO = "data1";
+        }
+
+        /**
+         * Notes about the contact.
+         */
+        public static final class Note implements BaseCommonColumns {
+            private Note() {}
+
+            /** Mime-type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/note";
+
+            /**
+             * The note text.
+             * <P>Type: TEXT</P>
+             */
+            public static final String NOTE = "data1";
+        }
+
+        /**
+         * Group Membership.
+         */
+        public static final class GroupMembership implements BaseCommonColumns {
+            private GroupMembership() {}
+
+            /** Mime-type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE =
+                    "vnd.android.cursor.item/group_membership";
+
+            /**
+             * The row id of the group that this group membership refers to. Either this or the
+             * GROUP_SOURCE_ID must be set. If they are both set then they must refer to the same
+             * group.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String GROUP_ROW_ID = "data1";
+
+            /**
+             * The source id of the group that this membership refers to. Either this or the
+             * GROUP_ROW_ID must be set. If they are both set then they must refer to the same
+             * group.
+             * <P>Type: STRING</P>
+             */
+            public static final String GROUP_SOURCE_ID = "data2";
+        }
+    }
+
+    /**
+     * Constants for the contact aggregation exceptions table, which contains
+     * aggregation rules overriding those used by automatic aggregation.  This type only
+     * supports query and update. Neither insert nor delete are supported.
+     */
+    public static final class AggregationExceptions implements BaseColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private AggregationExceptions() {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI =
+                Uri.withAppendedPath(AUTHORITY_URI, "aggregation_exceptions");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of data.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/aggregation_exception";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of an aggregation exception
+         */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/aggregation_exception";
+
+        /**
+         * The type of exception: {@link #TYPE_KEEP_IN}, {@link #TYPE_KEEP_OUT} or
+         * {@link #TYPE_AUTOMATIC}.
+         *
+         * <P>Type: INTEGER</P>
+         */
+        public static final String TYPE = "type";
+
+        /**
+         * Allows the provider to automatically decide whether the aggregate should include
+         * a particular contact or not.
+         */
+        public static final int TYPE_AUTOMATIC = 0;
+
+        /**
+         * Makes sure that the specified contact is included in the specified aggregate.
+         */
+        public static final int TYPE_KEEP_IN = 1;
+
+        /**
+         * Makes sure that the specified contact is NOT included in the specified aggregate.
+         */
+        public static final int TYPE_KEEP_OUT = 2;
+
+        /**
+         * A reference to the {@link Aggregates#_ID} of the aggregate that the rule applies to.
+         */
+        public static final String AGGREGATE_ID = "aggregate_id";
+
+        /**
+         * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} of the
+         * contact that the rule applies to.
+         */
+        public static final String CONTACT_ID = "contact_id";
+    }
+
+    private interface RestrictionExceptionsColumns {
+        /**
+         * Package name of a specific data provider, which will be matched
+         * against {@link Data#PACKAGE}.
+         * <p>
+         * Type: STRING
+         */
+        public static final String PACKAGE_PROVIDER = "package_provider";
+
+        /**
+         * Package name of a specific data client, which will be matched against
+         * the incoming {@link android.os.Binder#getCallingUid()} to decide if
+         * the caller can access values with {@link Data#IS_RESTRICTED} flags.
+         * <p>
+         * Type: STRING
+         */
+        public static final String PACKAGE_CLIENT = "package_client";
+
+        /**
+         * Flag indicating if {@link #PACKAGE_PROVIDER} allows
+         * {@link #PACKAGE_CLIENT} to access restricted {@link Data} rows.
+         * <p>
+         * Type: INTEGER
+         */
+        public static final String ALLOW_ACCESS = "allow_access";
+    }
+
+    /**
+     * Constants for {@link Data} restriction exceptions. Sync adapters who
+     * insert restricted data can use this table to specify exceptions about
+     * which additional packages can access that restricted data.You can only
+     * modify rules for a {@link RestrictionExceptionsColumns#PACKAGE_PROVIDER}
+     * that your {@link android.os.Binder#getCallingUid()} owns.
+     */
+    public static final class RestrictionExceptions implements RestrictionExceptionsColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private RestrictionExceptions() {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI,
+                "restriction_exceptions");
+    }
+
+    /**
+     * Contains helper classes used to create or manage {@link android.content.Intent Intents}
+     * that involve contacts.
+     */
+    public static final class Intents {
+        /**
+         * This is the intent that is fired when a search suggestion is clicked on.
+         */
+        public static final String SEARCH_SUGGESTION_CLICKED =
+                "android.provider.Contacts.SEARCH_SUGGESTION_CLICKED";
+
+        /**
+         * This is the intent that is fired when a search suggestion for dialing a number
+         * is clicked on.
+         */
+        public static final String SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED =
+                "android.provider.Contacts.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED";
+
+        /**
+         * This is the intent that is fired when a search suggestion for creating a contact
+         * is clicked on.
+         */
+        public static final String SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED =
+                "android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED";
+
+        /**
+         * Starts an Activity that lets the user pick a contact to attach an image to.
+         * After picking the contact it launches the image cropper in face detection mode.
+         */
+        public static final String ATTACH_IMAGE =
+                "com.android.contacts.action.ATTACH_IMAGE";
+
+        /**
+         * Takes as input a data URI with a mailto: or tel: scheme. If a single
+         * contact exists with the given data it will be shown. If no contact
+         * exists, a dialog will ask the user if they want to create a new
+         * contact with the provided details filled in. If multiple contacts
+         * share the data the user will be prompted to pick which contact they
+         * want to view.
+         * <p>
+         * For <code>mailto:</code> URIs, the scheme specific portion must be a
+         * raw email address, such as one built using
+         * {@link Uri#fromParts(String, String, String)}.
+         * <p>
+         * For <code>tel:</code> URIs, the scheme specific portion is compared
+         * to existing numbers using the standard caller ID lookup algorithm.
+         * The number must be properly encoded, for example using
+         * {@link Uri#fromParts(String, String, String)}.
+         * <p>
+         * Any extras from the {@link Insert} class will be passed along to the
+         * create activity if there are no contacts to show.
+         * <p>
+         * Passing true for the {@link #EXTRA_FORCE_CREATE} extra will skip
+         * prompting the user when the contact doesn't exist.
+         */
+        public static final String SHOW_OR_CREATE_CONTACT =
+                "com.android.contacts.action.SHOW_OR_CREATE_CONTACT";
+
+        /**
+         * Used with {@link #SHOW_OR_CREATE_CONTACT} to force creating a new
+         * contact if no matching contact found. Otherwise, default behavior is
+         * to prompt user with dialog before creating.
+         * <p>
+         * Type: BOOLEAN
+         */
+        public static final String EXTRA_FORCE_CREATE =
+                "com.android.contacts.action.FORCE_CREATE";
+
+        /**
+         * Used with {@link #SHOW_OR_CREATE_CONTACT} to specify an exact
+         * description to be shown when prompting user about creating a new
+         * contact.
+         * <p>
+         * Type: STRING
+         */
+        public static final String EXTRA_CREATE_DESCRIPTION =
+            "com.android.contacts.action.CREATE_DESCRIPTION";
+
+        /**
+         * Intents related to the Contacts app UI.
+         */
+        public static final class UI {
+            /**
+             * The action for the default contacts list tab.
+             */
+            public static final String LIST_DEFAULT =
+                    "com.android.contacts.action.LIST_DEFAULT";
+
+            /**
+             * The action for the contacts list tab.
+             */
+            public static final String LIST_GROUP_ACTION =
+                    "com.android.contacts.action.LIST_GROUP";
+
+            /**
+             * When in LIST_GROUP_ACTION mode, this is the group to display.
+             */
+            public static final String GROUP_NAME_EXTRA_KEY = "com.android.contacts.extra.GROUP";
+
+            /**
+             * The action for the all contacts list tab.
+             */
+            public static final String LIST_ALL_CONTACTS_ACTION =
+                    "com.android.contacts.action.LIST_ALL_CONTACTS";
+
+            /**
+             * The action for the contacts with phone numbers list tab.
+             */
+            public static final String LIST_CONTACTS_WITH_PHONES_ACTION =
+                    "com.android.contacts.action.LIST_CONTACTS_WITH_PHONES";
+
+            /**
+             * The action for the starred contacts list tab.
+             */
+            public static final String LIST_STARRED_ACTION =
+                    "com.android.contacts.action.LIST_STARRED";
+
+            /**
+             * The action for the frequent contacts list tab.
+             */
+            public static final String LIST_FREQUENT_ACTION =
+                    "com.android.contacts.action.LIST_FREQUENT";
+
+            /**
+             * The action for the "strequent" contacts list tab. It first lists the starred
+             * contacts in alphabetical order and then the frequent contacts in descending
+             * order of the number of times they have been contacted.
+             */
+            public static final String LIST_STREQUENT_ACTION =
+                    "com.android.contacts.action.LIST_STREQUENT";
+
+            /**
+             * A key for to be used as an intent extra to set the activity
+             * title to a custom String value.
+             */
+            public static final String TITLE_EXTRA_KEY =
+                "com.android.contacts.extra.TITLE_EXTRA";
+
+            /**
+             * Activity Action: Display a filtered list of contacts
+             * <p>
+             * Input: Extra field {@link #FILTER_TEXT_EXTRA_KEY} is the text to use for
+             * filtering
+             * <p>
+             * Output: Nothing.
+             */
+            public static final String FILTER_CONTACTS_ACTION =
+                "com.android.contacts.action.FILTER_CONTACTS";
+
+            /**
+             * Used as an int extra field in {@link #FILTER_CONTACTS_ACTION}
+             * intents to supply the text on which to filter.
+             */
+            public static final String FILTER_TEXT_EXTRA_KEY =
+                "com.android.contacts.extra.FILTER_TEXT";
+        }
+
+        /**
+         * Convenience class that contains string constants used
+         * to create contact {@link android.content.Intent Intents}.
+         */
+        public static final class Insert {
+            /** The action code to use when adding a contact */
+            public static final String ACTION = Intent.ACTION_INSERT;
+
+            /**
+             * If present, forces a bypass of quick insert mode.
+             */
+            public static final String FULL_MODE = "full_mode";
+
+            /**
+             * The extra field for the contact name.
+             * <P>Type: String</P>
+             */
+            public static final String NAME = "name";
+
+            // TODO add structured name values here.
+
+            /**
+             * The extra field for the contact phonetic name.
+             * <P>Type: String</P>
+             */
+            public static final String PHONETIC_NAME = "phonetic_name";
+
+            /**
+             * The extra field for the contact company.
+             * <P>Type: String</P>
+             */
+            public static final String COMPANY = "company";
+
+            /**
+             * The extra field for the contact job title.
+             * <P>Type: String</P>
+             */
+            public static final String JOB_TITLE = "job_title";
+
+            /**
+             * The extra field for the contact notes.
+             * <P>Type: String</P>
+             */
+            public static final String NOTES = "notes";
+
+            /**
+             * The extra field for the contact phone number.
+             * <P>Type: String</P>
+             */
+            public static final String PHONE = "phone";
+
+            /**
+             * The extra field for the contact phone number type.
+             * <P>Type: Either an integer value from
+             * {@link android.provider.Contacts.PhonesColumns PhonesColumns},
+             *  or a string specifying a custom label.</P>
+             */
+            public static final String PHONE_TYPE = "phone_type";
+
+            /**
+             * The extra field for the phone isprimary flag.
+             * <P>Type: boolean</P>
+             */
+            public static final String PHONE_ISPRIMARY = "phone_isprimary";
+
+            /**
+             * The extra field for an optional second contact phone number.
+             * <P>Type: String</P>
+             */
+            public static final String SECONDARY_PHONE = "secondary_phone";
+
+            /**
+             * The extra field for an optional second contact phone number type.
+             * <P>Type: Either an integer value from
+             * {@link android.provider.Contacts.PhonesColumns PhonesColumns},
+             *  or a string specifying a custom label.</P>
+             */
+            public static final String SECONDARY_PHONE_TYPE = "secondary_phone_type";
+
+            /**
+             * The extra field for an optional third contact phone number.
+             * <P>Type: String</P>
+             */
+            public static final String TERTIARY_PHONE = "tertiary_phone";
+
+            /**
+             * The extra field for an optional third contact phone number type.
+             * <P>Type: Either an integer value from
+             * {@link android.provider.Contacts.PhonesColumns PhonesColumns},
+             *  or a string specifying a custom label.</P>
+             */
+            public static final String TERTIARY_PHONE_TYPE = "tertiary_phone_type";
+
+            /**
+             * The extra field for the contact email address.
+             * <P>Type: String</P>
+             */
+            public static final String EMAIL = "email";
+
+            /**
+             * The extra field for the contact email type.
+             * <P>Type: Either an integer value from
+             * {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
+             *  or a string specifying a custom label.</P>
+             */
+            public static final String EMAIL_TYPE = "email_type";
+
+            /**
+             * The extra field for the email isprimary flag.
+             * <P>Type: boolean</P>
+             */
+            public static final String EMAIL_ISPRIMARY = "email_isprimary";
+
+            /**
+             * The extra field for an optional second contact email address.
+             * <P>Type: String</P>
+             */
+            public static final String SECONDARY_EMAIL = "secondary_email";
+
+            /**
+             * The extra field for an optional second contact email type.
+             * <P>Type: Either an integer value from
+             * {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
+             *  or a string specifying a custom label.</P>
+             */
+            public static final String SECONDARY_EMAIL_TYPE = "secondary_email_type";
+
+            /**
+             * The extra field for an optional third contact email address.
+             * <P>Type: String</P>
+             */
+            public static final String TERTIARY_EMAIL = "tertiary_email";
+
+            /**
+             * The extra field for an optional third contact email type.
+             * <P>Type: Either an integer value from
+             * {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
+             *  or a string specifying a custom label.</P>
+             */
+            public static final String TERTIARY_EMAIL_TYPE = "tertiary_email_type";
+
+            /**
+             * The extra field for the contact postal address.
+             * <P>Type: String</P>
+             */
+            public static final String POSTAL = "postal";
+
+            /**
+             * The extra field for the contact postal address type.
+             * <P>Type: Either an integer value from
+             * {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
+             *  or a string specifying a custom label.</P>
+             */
+            public static final String POSTAL_TYPE = "postal_type";
+
+            /**
+             * The extra field for the postal isprimary flag.
+             * <P>Type: boolean</P>
+             */
+            public static final String POSTAL_ISPRIMARY = "postal_isprimary";
+
+            /**
+             * The extra field for an IM handle.
+             * <P>Type: String</P>
+             */
+            public static final String IM_HANDLE = "im_handle";
+
+            /**
+             * The extra field for the IM protocol
+             * <P>Type: the result of {@link Contacts.ContactMethods#encodePredefinedImProtocol}
+             * or {@link Contacts.ContactMethods#encodeCustomImProtocol}.</P>
+             */
+            public static final String IM_PROTOCOL = "im_protocol";
+
+            /**
+             * The extra field for the IM isprimary flag.
+             * <P>Type: boolean</P>
+             */
+            public static final String IM_ISPRIMARY = "im_isprimary";
+        }
+    }
+
+}
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 4c58e0d..790fe5c 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -63,7 +63,7 @@
      * that had initiated a download when that download completes. The
      * download's content: uri is specified in the intent's data.
      */
-    public static final String DOWNLOAD_COMPLETED_ACTION =
+    public static final String ACTION_DOWNLOAD_COMPLETED =
             "android.intent.action.DOWNLOAD_COMPLETED";
 
     /**
@@ -76,7 +76,7 @@
      * Note: this is not currently sent for downloads that have completed
      * successfully.
      */
-    public static final String NOTIFICATION_CLICKED_ACTION =
+    public static final String ACTION_NOTIFICATION_CLICKED =
             "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
 
     /**
@@ -84,14 +84,14 @@
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read</P>
      */
-    public static final String URI = "uri";
+    public static final String COLUMN_URI = "uri";
 
     /**
      * The name of the column containing application-specific data.
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read/Write</P>
      */
-    public static final String APP_DATA = "entity";
+    public static final String COLUMN_APP_DATA = "entity";
 
     /**
      * The name of the column containing the flags that indicates whether
@@ -104,7 +104,7 @@
      * <P>Type: BOOLEAN</P>
      * <P>Owner can Init</P>
      */
-    public static final String NO_INTEGRITY = "no_integrity";
+    public static final String COLUMN_NO_INTEGRITY = "no_integrity";
 
     /**
      * The name of the column containing the filename that the initiating
@@ -113,7 +113,7 @@
      * <P>Type: TEXT</P>
      * <P>Owner can Init</P>
      */
-    public static final String FILENAME_HINT = "hint";
+    public static final String COLUMN_FILE_NAME_HINT = "hint";
 
     /**
      * The name of the column containing the filename where the downloaded data
@@ -128,7 +128,7 @@
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read</P>
      */
-    public static final String MIMETYPE = "mimetype";
+    public static final String COLUMN_MIME_TYPE = "mimetype";
 
     /**
      * The name of the column containing the flag that controls the destination
@@ -136,7 +136,7 @@
      * <P>Type: INTEGER</P>
      * <P>Owner can Init</P>
      */
-    public static final String DESTINATION = "destination";
+    public static final String COLUMN_DESTINATION = "destination";
 
     /**
      * The name of the column containing the flags that controls whether the
@@ -145,7 +145,7 @@
      * <P>Type: INTEGER</P>
      * <P>Owner can Init/Read/Write</P>
      */
-    public static final String VISIBILITY = "visibility";
+    public static final String COLUMN_VISIBILITY = "visibility";
 
     /**
      * The name of the column containing the current control state  of the download.
@@ -154,7 +154,7 @@
      * <P>Type: INTEGER</P>
      * <P>Owner can Read</P>
      */
-    public static final String CONTROL = "control";
+    public static final String COLUMN_CONTROL = "control";
 
     /**
      * The name of the column containing the current status of the download.
@@ -163,7 +163,7 @@
      * <P>Type: INTEGER</P>
      * <P>Owner can Read</P>
      */
-    public static final String STATUS = "status";
+    public static final String COLUMN_STATUS = "status";
 
     /**
      * The name of the column containing the date at which some interesting
@@ -172,7 +172,7 @@
      * <P>Type: BIGINT</P>
      * <P>Owner can Read</P>
      */
-    public static final String LAST_MODIFICATION = "lastmod";
+    public static final String COLUMN_LAST_MODIFICATION = "lastmod";
 
     /**
      * The name of the column containing the package name of the application
@@ -181,7 +181,7 @@
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read</P>
      */
-    public static final String NOTIFICATION_PACKAGE = "notificationpackage";
+    public static final String COLUMN_NOTIFICATION_PACKAGE = "notificationpackage";
 
     /**
      * The name of the column containing the component name of the class that
@@ -191,7 +191,7 @@
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read</P>
      */
-    public static final String NOTIFICATION_CLASS = "notificationclass";
+    public static final String COLUMN_NOTIFICATION_CLASS = "notificationclass";
 
     /**
      * If extras are specified when requesting a download they will be provided in the intent that
@@ -199,7 +199,7 @@
      * <P>Type: TEXT</P>
      * <P>Owner can Init</P>
      */
-    public static final String NOTIFICATION_EXTRAS = "notificationextras";
+    public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras";
 
     /**
      * The name of the column contain the values of the cookie to be used for
@@ -208,7 +208,7 @@
      * <P>Type: TEXT</P>
      * <P>Owner can Init</P>
      */
-    public static final String COOKIE_DATA = "cookiedata";
+    public static final String COLUMN_COOKIE_DATA = "cookiedata";
 
     /**
      * The name of the column containing the user agent that the initiating
@@ -216,7 +216,7 @@
      * <P>Type: TEXT</P>
      * <P>Owner can Init</P>
      */
-    public static final String USER_AGENT = "useragent";
+    public static final String COLUMN_USER_AGENT = "useragent";
 
     /**
      * The name of the column containing the referer (sic) that the initiating
@@ -224,7 +224,7 @@
      * <P>Type: TEXT</P>
      * <P>Owner can Init</P>
      */
-    public static final String REFERER = "referer";
+    public static final String COLUMN_REFERER = "referer";
 
     /**
      * The name of the column containing the total size of the file being
@@ -232,7 +232,7 @@
      * <P>Type: INTEGER</P>
      * <P>Owner can Read</P>
      */
-    public static final String TOTAL_BYTES = "total_bytes";
+    public static final String COLUMN_TOTAL_BYTES = "total_bytes";
 
     /**
      * The name of the column containing the size of the part of the file that
@@ -240,7 +240,7 @@
      * <P>Type: INTEGER</P>
      * <P>Owner can Read</P>
      */
-    public static final String CURRENT_BYTES = "current_bytes";
+    public static final String COLUMN_CURRENT_BYTES = "current_bytes";
 
     /**
      * The name of the column where the initiating application can provide the
@@ -252,7 +252,7 @@
      * <P>Type: INTEGER</P>
      * <P>Owner can Init</P>
      */
-    public static final String OTHER_UID = "otheruid";
+    public static final String COLUMN_OTHER_UID = "otheruid";
 
     /**
      * The name of the column where the initiating application can provided the
@@ -261,7 +261,7 @@
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read/Write</P>
      */
-    public static final String TITLE = "title";
+    public static final String COLUMN_TITLE = "title";
 
     /**
      * The name of the column where the initiating application can provide the
@@ -270,7 +270,7 @@
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read/Write</P>
      */
-    public static final String DESCRIPTION = "description";
+    public static final String COLUMN_DESCRIPTION = "description";
 
     /*
      * Lists the destinations that an application can specify for a download.
diff --git a/core/java/android/provider/Im.java b/core/java/android/provider/Im.java
index 19ad158..ef4ff51 100644
--- a/core/java/android/provider/Im.java
+++ b/core/java/android/provider/Im.java
@@ -871,14 +871,22 @@
     }
 
     /**
-     * The common columns for both one-to-one chat messages or group chat messages.
+     * The common columns for messages table
      */
-    public interface BaseMessageColumns {
+    public interface MessageColumns {
         /**
-         * The user this message belongs to
-         * <P>Type: TEXT</P>
+         * The thread_id column stores the contact id of the contact the message belongs to.
+         * For groupchat messages, the thread_id stores the group id, which is the contact id
+         * of the temporary group contact created for the groupchat. So there should be no
+         * collision between groupchat message thread id and regular message thread id. 
          */
-        String CONTACT = "contact";
+        String THREAD_ID = "thread_id";
+
+        /**
+         * The nickname. This is used for groupchat messages to indicate the participant's
+         * nickname. For non groupchat messages, this field should be left empty.
+         */
+        String NICKNAME = "nickname";
 
         /**
          * The body
@@ -917,68 +925,193 @@
          * <P>Type: STRING</P>
          */
         String PACKET_ID = "packet_id";
-    }
-
-    /**
-     * Columns from the Messages table.
-     */
-    public interface MessagesColumns extends BaseMessageColumns{
-        /**
-         * The provider id
-         * <P> Type: INTEGER </P>
-         */
-        String PROVIDER = "provider";
 
         /**
-         * The account id
-         * <P> Type: INTEGER </P>
+         * Is groupchat message or not
+         * <P>Type: INTEGER</P>
          */
-        String ACCOUNT = "account";
+        String IS_GROUP_CHAT = "is_muc";
     }
 
     /**
      * This table contains messages.
      */
-    public static final class Messages implements BaseColumns, MessagesColumns {
+    public static final class Messages implements BaseColumns, MessageColumns {
         /**
          * no public constructor since this is a utility class
          */
         private Messages() {}
 
         /**
-         * Gets the Uri to query messages by contact.
+         * Gets the Uri to query messages by thread id.
          *
-         * @param providerId the provider id of the contact.
+         * @param threadId the thread id of the message.
+         * @return the Uri
+         */
+        public static final Uri getContentUriByThreadId(long threadId) {
+            Uri.Builder builder = CONTENT_URI_MESSAGES_BY_THREAD_ID.buildUpon();
+            ContentUris.appendId(builder, threadId);
+            return builder.build();
+        }
+
+        /**
+         * @deprecated
+         *
+         * Gets the Uri to query messages by account and contact.
+         *
          * @param accountId the account id of the contact.
          * @param username the user name of the contact.
          * @return the Uri
          */
-        public static final Uri getContentUriByContact(long providerId,
-                long accountId, String username) {
-            Uri.Builder builder = CONTENT_URI_MESSAGES_BY.buildUpon();
-            ContentUris.appendId(builder, providerId);
+        public static final Uri getContentUriByContact(long accountId, String username) {
+            Uri.Builder builder = CONTENT_URI_MESSAGES_BY_ACCOUNT_AND_CONTACT.buildUpon();
             ContentUris.appendId(builder, accountId);
             builder.appendPath(username);
             return builder.build();
         }
 
         /**
+         * Gets the Uri to query messages by provider.
+         *
+         * @param providerId the service provider id.
+         * @return the Uri
+         */
+        public static final Uri getContentUriByProvider(long providerId) {
+            Uri.Builder builder = CONTENT_URI_MESSAGES_BY_PROVIDER.buildUpon();
+            ContentUris.appendId(builder, providerId);
+            return builder.build();
+        }
+
+        /**
+         * Gets the Uri to query off the record messages by account.
+         *
+         * @param accountId the account id.
+         * @return the Uri
+         */
+        public static final Uri getContentUriByAccount(long accountId) {
+            Uri.Builder builder = CONTENT_URI_BY_ACCOUNT.buildUpon();
+            ContentUris.appendId(builder, accountId);
+            return builder.build();
+        }
+
+        /**
+         * Gets the Uri to query off the record messages by thread id.
+         *
+         * @param threadId the thread id of the message.
+         * @return the Uri
+         */
+        public static final Uri getOtrMessagesContentUriByThreadId(long threadId) {
+            Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_THREAD_ID.buildUpon();
+            ContentUris.appendId(builder, threadId);
+            return builder.build();
+        }
+
+        /**
+         * @deprecated
+         *
+         * Gets the Uri to query off the record messages by account and contact.
+         *
+         * @param accountId the account id of the contact.
+         * @param username the user name of the contact.
+         * @return the Uri
+         */
+        public static final Uri getOtrMessagesContentUriByContact(long accountId, String username) {
+            Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT_AND_CONTACT.buildUpon();
+            ContentUris.appendId(builder, accountId);
+            builder.appendPath(username);
+            return builder.build();
+        }
+
+        /**
+         * Gets the Uri to query off the record messages by provider.
+         *
+         * @param providerId the service provider id.
+         * @return the Uri
+         */
+        public static final Uri getOtrMessagesContentUriByProvider(long providerId) {
+            Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_PROVIDER.buildUpon();
+            ContentUris.appendId(builder, providerId);
+            return builder.build();
+        }
+
+        /**
+         * Gets the Uri to query off the record messages by account.
+         *
+         * @param accountId the account id.
+         * @return the Uri
+         */
+        public static final Uri getOtrMessagesContentUriByAccount(long accountId) {
+            Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT.buildUpon();
+            ContentUris.appendId(builder, accountId);
+            return builder.build();
+        }
+
+        /**
          * The content:// style URL for this table
          */
         public static final Uri CONTENT_URI =
-            Uri.parse("content://im/messages");
+                Uri.parse("content://im/messages");
 
         /**
-         * The content:// style URL for messages by provider and account
+         * The content:// style URL for messages by thread id
          */
-        public static final Uri CONTENT_URI_MESSAGES_BY =
-            Uri.parse("content://im/messagesBy");
+        public static final Uri CONTENT_URI_MESSAGES_BY_THREAD_ID =
+                Uri.parse("content://im/messagesByThreadId");
+
+        /**
+         * The content:// style URL for messages by account and contact
+         */
+        public static final Uri CONTENT_URI_MESSAGES_BY_ACCOUNT_AND_CONTACT =
+                Uri.parse("content://im/messagesByAcctAndContact");
+
+        /**
+         * The content:// style URL for messages by provider
+         */
+        public static final Uri CONTENT_URI_MESSAGES_BY_PROVIDER =
+                Uri.parse("content://im/messagesByProvider");
+
+        /**
+         * The content:// style URL for messages by account
+         */
+        public static final Uri CONTENT_URI_BY_ACCOUNT =
+                Uri.parse("content://im/messagesByAccount");
+
+        /**
+         * The content:// style url for off the record messages
+         */
+        public static final Uri OTR_MESSAGES_CONTENT_URI =
+                Uri.parse("content://im/otrMessages");
+
+        /**
+         * The content:// style url for off the record messages by thread id
+         */
+        public static final Uri OTR_MESSAGES_CONTENT_URI_BY_THREAD_ID =
+                Uri.parse("content://im/otrMessagesByThreadId");
+
+        /**
+         * The content:// style url for off the record messages by account and contact
+         */
+        public static final Uri OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT_AND_CONTACT =
+                Uri.parse("content://im/otrMessagesByAcctAndContact");
+
+        /**
+         * The content:// style URL for off the record messages by provider
+         */
+        public static final Uri OTR_MESSAGES_CONTENT_URI_BY_PROVIDER =
+                Uri.parse("content://im/otrMessagesByProvider");
+
+        /**
+         * The content:// style URL for off the record messages by account
+         */
+        public static final Uri OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT =
+                Uri.parse("content://im/otrMessagesByAccount");
 
         /**
          * The MIME type of {@link #CONTENT_URI} providing a directory of
          * people.
          */
-        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-messages";
+        public static final String CONTENT_TYPE =
+                "vnd.android.cursor.dir/im-messages";
 
         /**
          * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
@@ -992,6 +1125,11 @@
          */
         public static final String DEFAULT_SORT_ORDER = "date ASC";
 
+        /**
+         * The "contact" column. This is not a real column in the messages table, but a
+         * temoprary column created when querying for messages (joined with the contacts table)
+         */
+        public static final String CONTACT = "contact";
     }
 
     /**
@@ -1119,67 +1257,6 @@
     }
 
     /**
-     * Columns from the GroupMessages table
-     */
-    public interface GroupMessageColumns extends BaseMessageColumns {
-        /**
-         * The group this message belongs to
-         * <p>Type: TEXT</p>
-         */
-        String GROUP = "groupId";
-    }
-
-    /**
-     * This table contains group messages.
-     */
-    public final static class GroupMessages implements BaseColumns,
-            GroupMessageColumns {
-        private GroupMessages() {}
-
-        /**
-         * Gets the Uri to query group messages by group.
-         *
-         * @param groupId the group id.
-         * @return the Uri
-         */
-        public static final Uri getContentUriByGroup(long groupId) {
-            Uri.Builder builder = CONTENT_URI_GROUP_MESSAGES_BY.buildUpon();
-            ContentUris.appendId(builder, groupId);
-            return builder.build();
-        }
-
-        /**
-         * The content:// style URL for this table
-         */
-        public static final Uri CONTENT_URI =
-            Uri.parse("content://im/groupMessages");
-
-        /**
-         * The content:// style URL for group messages by provider and account
-         */
-        public static final Uri CONTENT_URI_GROUP_MESSAGES_BY =
-            Uri.parse("content://im/groupMessagesBy");
-
-        /**
-         * The MIME type of {@link #CONTENT_URI} providing a directory of
-         * group messages.
-         */
-        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-groupMessages";
-
-        /**
-         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
-         * group message.
-         */
-        public static final String CONTENT_ITEM_TYPE =
-                "vnd.android.cursor.item/im-groupMessages";
-
-        /**
-         * The default sort order for this table
-         */
-        public static final String DEFAULT_SORT_ORDER = "date ASC";
-    }
-
-    /**
      * Columns from the Avatars table
      */
     public interface AvatarsColumns {
@@ -1534,6 +1611,9 @@
         /** specifies whether or not to show mobile indicator to friends */
         public static final String SETTING_SHOW_MOBILE_INDICATOR = "mobile_indicator";
 
+        /** specifies whether or not to show as away when device is idle */
+        public static final String SETTING_SHOW_AWAY_ON_IDLE = "show_away_on_idle";
+
         /**
          * Used for reliable message queue (RMQ). This is for storing the last rmq id received
          * from the GTalk server
@@ -1742,6 +1822,17 @@
                     showMobileIndicator);
         }
 
+        /**
+         * A convenience method to set whether or not to show as away when device is idle.
+         *
+         * @param contentResolver The ContentResolver to use to access the setting table.
+         * @param showAway Whether or not to show as away when device is idle.
+         */
+        public static void setShowAwayOnIdle(ContentResolver contentResolver,
+                long providerId, boolean showAway) {
+            putBooleanValue(contentResolver, providerId, SETTING_SHOW_AWAY_ON_IDLE, showAway);
+        }
+
         public static class QueryMap extends ContentQueryMap {
             private ContentResolver mContentResolver;
             private long mProviderId;
@@ -1872,6 +1963,25 @@
             }
 
             /**
+             * Set whether or not to show as away when device is idle.
+             *
+             * @param showAway whether or not to show as away when device is idle.
+             */
+            public void setShowAwayOnIdle(boolean showAway) {
+                ProviderSettings.setShowAwayOnIdle(mContentResolver, mProviderId, showAway);
+            }
+
+            /**
+             * Get whether or not to show as away when device is idle.
+             *
+             * @return Whether or not to show as away when device is idle.
+             */
+            public boolean getShowAwayOnIdle() {
+                return getBoolean(SETTING_SHOW_AWAY_ON_IDLE,
+                        true /* by default show as away on idle*/);
+            }
+
+            /**
              * Convenience function for retrieving a single settings value
              * as a boolean.
              *
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5b54b32..7e10023 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1182,6 +1182,22 @@
         public static final Uri DEFAULT_NOTIFICATION_URI = getUriFor(NOTIFICATION_SOUND);
 
         /**
+         * Persistent store for the system-wide default alarm alert.
+         *
+         * @see #RINGTONE
+         * @see #DEFAULT_ALARM_ALERT_URI
+         */
+        public static final String ALARM_ALERT = "alarm_alert";
+
+        /**
+         * A {@link Uri} that will point to the current default alarm alert at
+         * any given time.
+         *
+         * @see #DEFAULT_ALARM_ALERT_URI
+         */
+        public static final Uri DEFAULT_ALARM_ALERT_URI = getUriFor(ALARM_ALERT);
+
+        /**
          * Setting to enable Auto Replace (AutoText) in text editors. 1 = On, 0 = Off
          */
         public static final String TEXT_AUTO_REPLACE = "auto_replace";
@@ -1912,6 +1928,12 @@
         public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
 
         /**
+         * Whether assisted GPS should be enabled or not.
+         * @hide
+         */
+        public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled";
+
+        /**
          * The Logging ID (a unique 64-bit value) as a hex string.
          * Used as a pseudonymous identifier for logging.
          * @deprecated This identifier is poorly initialized and has
@@ -2673,6 +2695,12 @@
                 "gtalk_nosync_heartbeat_ping_interval_ms";
 
         /**
+         * The maximum heartbeat interval used while on the WIFI network. 
+         */
+        public static final String GTALK_SERVICE_WIFI_MAX_HEARTBEAT_INTERVAL_MS =
+                "gtalk_wifi_max_heartbeat_ping_interval_ms";
+
+        /**
          * How long we wait to receive a heartbeat ping acknowledgement (or another packet)
          * from the GTalk server, before deeming the connection dead.
          */
@@ -2725,6 +2753,21 @@
         public static final String GTALK_USE_BARE_JID_TIMEOUT_MS = "gtalk_use_barejid_timeout_ms";
 
         /**
+         * This is the threshold of retry number when there is an authentication expired failure
+         * for Google Talk. In some situation, e.g. when a Google Apps account is disabled chat
+         * service, the connection keeps failing. This threshold controls when we should stop
+         * the retrying.
+         */
+        public static final String GTALK_MAX_RETRIES_FOR_AUTH_EXPIRED =
+            "gtalk_max_retries_for_auth_expired";
+
+        /**
+         * This is the url for getting the app token for server-to-device data messaging.
+         */
+        public static final String DATA_MESSAGE_GET_APP_TOKEN_URL =
+                "data_messaging_get_app_token_url";
+        
+        /**
          * Enable use of ssl session caching.
          * 'db' - save each session in a (per process) database
          * 'file' - save each session in a (per process) file
diff --git a/core/java/android/provider/SocialContract.java b/core/java/android/provider/SocialContract.java
new file mode 100644
index 0000000..28bf8db
--- /dev/null
+++ b/core/java/android/provider/SocialContract.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.provider;
+
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.ContactsContract.Aggregates;
+import android.provider.ContactsContract.Data;
+
+/**
+ * The contract between the social provider and applications. Contains
+ * definitions for the supported URIs and columns.
+ *
+ * @hide
+ */
+public class SocialContract {
+    /** The authority for the social provider */
+    public static final String AUTHORITY = "com.android.social";
+
+    /** A content:// style uri to the authority for the contacts provider */
+    public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+    private interface ActivitiesColumns {
+        /**
+         * The package name that owns this social activity.
+         * <p>
+         * Type: TEXT
+         */
+        public static final String PACKAGE = "package";
+
+        /**
+         * The mime-type of this social activity.
+         * <p>
+         * Type: TEXT
+         */
+        public static final String MIMETYPE = "mimetype";
+
+        /**
+         * Internal raw identifier for this social activity. This field is
+         * analogous to the <code>atom:id</code> element defined in RFC 4287.
+         * <p>
+         * Type: TEXT
+         */
+        public static final String RAW_ID = "raw_id";
+
+        /**
+         * Reference to another {@link Activities#RAW_ID} that this social activity
+         * is replying to. This field is analogous to the
+         * <code>thr:in-reply-to</code> element defined in RFC 4685.
+         * <p>
+         * Type: TEXT
+         */
+        public static final String IN_REPLY_TO = "in_reply_to";
+
+        /**
+         * Reference to the {@link android.provider.ContactsContract.Contacts#_ID} that authored
+         * this social activity. This field is analogous to the <code>atom:author</code>
+         * element defined in RFC 4287.
+         * <p>
+         * Type: INTEGER
+         */
+        public static final String AUTHOR_CONTACT_ID = "author_contact_id";
+
+        /**
+         * Optional reference to the {@link android.provider.ContactsContract.Contacts#_ID} this
+         * social activity is targeted towards. If more than one direct target, this field may
+         * be left undefined. This field is analogous to the
+         * <code>activity:target</code> element defined in the Atom Activity
+         * Extensions Internet-Draft.
+         * <p>
+         * Type: INTEGER
+         */
+        public static final String TARGET_CONTACT_ID = "target_contact_id";
+
+        /**
+         * Timestamp when this social activity was published, in a
+         * {@link System#currentTimeMillis()} time base. This field is analogous
+         * to the <code>atom:published</code> element defined in RFC 4287.
+         * <p>
+         * Type: INTEGER
+         */
+        public static final String PUBLISHED = "published";
+
+        /**
+         * Timestamp when the original social activity in a thread was
+         * published. For activities that have an in-reply-to field specified, the
+         * content provider will automatically populate this field with the
+         * timestamp of the original activity.
+         * <p>
+         * This field is useful for sorting order of activities that keeps together all
+         * messages in each thread.
+         * <p>
+         * Type: INTEGER
+         */
+        public static final String THREAD_PUBLISHED = "thread_published";
+
+        /**
+         * Title of this social activity. This field is analogous to the
+         * <code>atom:title</code> element defined in RFC 4287.
+         * <p>
+         * Type: TEXT
+         */
+        public static final String TITLE = "title";
+
+        /**
+         * Summary of this social activity. This field is analogous to the
+         * <code>atom:summary</code> element defined in RFC 4287.
+         * <p>
+         * Type: TEXT
+         */
+        public static final String SUMMARY = "summary";
+
+        /**
+         * A URI associated this social activity. This field is analogous to the
+         * <code>atom:link rel="alternate"</code> element defined in RFC 4287.
+         * <p>
+         * Type: TEXT
+         */
+        public static final String LINK = "link";
+
+        /**
+         * Optional thumbnail specific to this social activity. This is the raw
+         * bytes of an image that could be inflated using {@link BitmapFactory}.
+         * <p>
+         * Type: BLOB
+         */
+        public static final String THUMBNAIL = "thumbnail";
+    }
+
+    public static final class Activities implements BaseColumns, ActivitiesColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private Activities() {
+        }
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "activities");
+
+        /**
+         * The content:// URI for this table filtered to the set of social activities
+         * authored by a specific {@link android.provider.ContactsContract.Contacts#_ID}.
+         */
+        public static final Uri CONTENT_AUTHORED_BY_URI =
+            Uri.withAppendedPath(CONTENT_URI, "authored_by");
+
+        /**
+         * The {@link Uri} for the latest social activity performed by any
+         * contact aggregated under the specified {@link Aggregates#_ID}. Will
+         * also join with most-present {@link Presence} for this aggregate.
+         */
+        public static final Uri CONTENT_AGGREGATE_STATUS_URI =
+            Uri.withAppendedPath(AUTHORITY_URI, "aggregate_status");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of social
+         * activities.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/activity";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+         * social activity.
+         */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/activity";
+    }
+
+}
diff --git a/core/java/android/provider/SubscribedFeeds.java b/core/java/android/provider/SubscribedFeeds.java
index 4d430d5..f94b442 100644
--- a/core/java/android/provider/SubscribedFeeds.java
+++ b/core/java/android/provider/SubscribedFeeds.java
@@ -20,6 +20,7 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
+import android.accounts.Account;
 
 /**
  * The SubscribedFeeds provider stores all information about subscribed feeds.
@@ -99,7 +100,7 @@
         /**
          * The default sort order for this table
          */
-        public static final String DEFAULT_SORT_ORDER = "_SYNC_ACCOUNT ASC";
+        public static final String DEFAULT_SORT_ORDER = "_SYNC_ACCOUNT_TYPE, _SYNC_ACCOUNT ASC";
     }
 
     /**
@@ -114,38 +115,36 @@
      * @return  the Uri of the feed that was added
      */
     public static Uri addFeed(ContentResolver resolver,
-            String feed, String account,
+            String feed, Account account,
             String authority, String service) {
         ContentValues values = new ContentValues();
         values.put(SubscribedFeeds.Feeds.FEED, feed);
-        values.put(SubscribedFeeds.Feeds._SYNC_ACCOUNT, account);
+        values.put(SubscribedFeeds.Feeds._SYNC_ACCOUNT, account.mName);
+        values.put(SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE, account.mType);
         values.put(SubscribedFeeds.Feeds.AUTHORITY, authority);
         values.put(SubscribedFeeds.Feeds.SERVICE, service);
         return resolver.insert(SubscribedFeeds.Feeds.CONTENT_URI, values);
     }
 
     public static int deleteFeed(ContentResolver resolver,
-            String feed, String account, String authority) {
+            String feed, Account account, String authority) {
         StringBuilder where = new StringBuilder();
         where.append(SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?");
+        where.append(" AND " + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "=?");
         where.append(" AND " + SubscribedFeeds.Feeds.FEED + "=?");
         where.append(" AND " + SubscribedFeeds.Feeds.AUTHORITY + "=?");
         return resolver.delete(SubscribedFeeds.Feeds.CONTENT_URI,
-                where.toString(), new String[] {account, feed, authority});
+                where.toString(), new String[] {account.mName, account.mType, feed, authority});
     }
 
     public static int deleteFeeds(ContentResolver resolver,
-            String account, String authority) {
+            Account account, String authority) {
         StringBuilder where = new StringBuilder();
         where.append(SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?");
+        where.append(" AND " + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "=?");
         where.append(" AND " + SubscribedFeeds.Feeds.AUTHORITY + "=?");
         return resolver.delete(SubscribedFeeds.Feeds.CONTENT_URI,
-                where.toString(), new String[] {account, authority});
-    }
-
-    public static String gtalkServiceRoutingInfoFromAccountAndResource(
-            String account, String res) {
-        return Uri.parse("gtalk://" + account + "/" + res).toString();
+                where.toString(), new String[] {account.mName, account.mType, authority});
     }
 
     /**
@@ -157,6 +156,12 @@
          * <P>Type: TEXT</P>
          */
         public static final String _SYNC_ACCOUNT = SyncConstValue._SYNC_ACCOUNT;
+
+        /**
+         * The account type.
+         * <P>Type: TEXT</P>
+         */
+        public static final String _SYNC_ACCOUNT_TYPE = SyncConstValue._SYNC_ACCOUNT_TYPE;
     }
 
     /**
@@ -199,6 +204,6 @@
         /**
          * The default sort order for this table
          */
-        public static final String DEFAULT_SORT_ORDER = "_SYNC_ACCOUNT ASC";
+        public static final String DEFAULT_SORT_ORDER = "_SYNC_ACCOUNT_TYPE, _SYNC_ACCOUNT ASC";
     }
 }
diff --git a/core/java/android/provider/SyncConstValue.java b/core/java/android/provider/SyncConstValue.java
index 6eb4398..30966eb 100644
--- a/core/java/android/provider/SyncConstValue.java
+++ b/core/java/android/provider/SyncConstValue.java
@@ -29,6 +29,12 @@
     public static final String _SYNC_ACCOUNT = "_sync_account";
 
     /**
+     * The type of the account that was used to sync the entry to the device.
+     * <P>Type: TEXT</P>
+     */
+    public static final String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
+
+    /**
      * The unique ID for a row assigned by the sync source. NULL if the row has never been synced.
      * <P>Type: TEXT</P>
      */
@@ -68,4 +74,9 @@
      * Used to indicate that this account is not synced
      */
     public static final String NON_SYNCABLE_ACCOUNT = "non_syncable";
+
+    /**
+     * Used to indicate that this account is not synced
+     */
+    public static final String NON_SYNCABLE_ACCOUNT_TYPE = "android.local";
 }
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 4078fa6..a301d7b 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -146,7 +146,13 @@
          * <P>Type: TEXT</P>
          */
         public static final String SERVICE_CENTER = "service_center";
-    }
+
+        /**
+         * Has the message been locked?
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String LOCKED = "locked";
+}
 
     /**
      * Contains all text based SMS messages.
@@ -241,7 +247,7 @@
             if (uri == null) {
                 return false;
             }
-            
+
             boolean markAsUnread = false;
             boolean markAsRead = false;
             switch(folder) {
@@ -268,7 +274,7 @@
             } else if (markAsRead) {
                 values.put(READ, Integer.valueOf(1));
             }
-            
+
             return 1 == SqliteWrapper.update(context, context.getContentResolver(),
                             uri, values, null, null);
         }
@@ -1008,6 +1014,12 @@
          * <P>Type: INTEGER</P>
          */
         public static final String THREAD_ID = "thread_id";
+
+        /**
+         * Has the message been locked?
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String LOCKED = "locked";
     }
 
     /**
@@ -1410,6 +1422,8 @@
              */
             public static final String _DATA = "_data";
 
+            public static final String TEXT = "text";
+
         }
 
         public static final class Rate {
@@ -1487,6 +1501,14 @@
         public static final Uri CONTENT_DRAFT_URI = Uri.parse(
                 "content://mms-sms/draft");
 
+        /***
+         * Pass in a query parameter called "pattern" which is the text
+         * to search for.
+         * The sort order is fixed to be thread_id ASC,date DESC.
+         */
+        public static final Uri SEARCH_URI = Uri.parse(
+                "content://mms-sms/search");
+
         // Constants for message protocol types.
         public static final int SMS_PROTO = 0;
         public static final int MMS_PROTO = 1;
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 5c4e56d..1cf7be9 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -26,14 +26,13 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothError;
 import android.bluetooth.BluetoothIntent;
+import android.bluetooth.BluetoothUuid;
 import android.bluetooth.IBluetoothA2dp;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.media.AudioManager;
-import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
 import android.provider.Settings;
@@ -44,6 +43,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.UUID;
 
 public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
     private static final String TAG = "BluetoothA2dpService";
@@ -58,57 +58,22 @@
     private static final String BLUETOOTH_ENABLED = "bluetooth_enabled";
 
     private static final int MESSAGE_CONNECT_TO = 1;
-    private static final int MESSAGE_DISCONNECT = 2;
+
+    private static final String PROPERTY_STATE = "State";
+
+    private static final String SINK_STATE_DISCONNECTED = "disconnected";
+    private static final String SINK_STATE_CONNECTING = "connecting";
+    private static final String SINK_STATE_CONNECTED = "connected";
+    private static final String SINK_STATE_PLAYING = "playing";
+
+    private static int mSinkCount;
+
 
     private final Context mContext;
     private final IntentFilter mIntentFilter;
-    private HashMap<String, SinkState> mAudioDevices;
+    private HashMap<String, Integer> mAudioDevices;
     private final AudioManager mAudioManager;
-    private final BluetoothDevice mBluetooth;
-
-    // list of disconnected sinks to process after a delay
-    private final ArrayList<String> mPendingDisconnects = new ArrayList<String>();
-    // number of active sinks
-    private int mSinkCount = 0; 
-
-    private class SinkState {
-        public String address;
-        public int state;
-        public SinkState(String a, int s) {address = a; state = s;}
-    }
-
-    public BluetoothA2dpService(Context context) {
-        mContext = context;
-
-        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-
-        mBluetooth = (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
-        if (mBluetooth == null) {
-            throw new RuntimeException("Platform does not support Bluetooth");
-        }
-
-        if (!initNative()) {
-            throw new RuntimeException("Could not init BluetoothA2dpService");
-        }
-
-        mIntentFilter = new IntentFilter(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION);
-        mIntentFilter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION);
-        mIntentFilter.addAction(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
-        mContext.registerReceiver(mReceiver, mIntentFilter);
-
-        if (mBluetooth.isEnabled()) {
-            onBluetoothEnable();
-        }
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            cleanupNative();
-        } finally {
-            super.finalize();
-        }
-    }
+    private final BluetoothDeviceService mBluetoothService;
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -151,6 +116,40 @@
         }
     };
 
+    public BluetoothA2dpService(Context context, BluetoothDeviceService bluetoothService) {
+        mContext = context;
+
+        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+
+        mBluetoothService = bluetoothService;
+        if (mBluetoothService == null) {
+            throw new RuntimeException("Platform does not support Bluetooth");
+        }
+
+        if (!initNative()) {
+            throw new RuntimeException("Could not init BluetoothA2dpService");
+        }
+
+        mIntentFilter = new IntentFilter(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION);
+        mIntentFilter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION);
+        mIntentFilter.addAction(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
+        mContext.registerReceiver(mReceiver, mIntentFilter);
+
+        mAudioDevices = new HashMap<String, Integer>();
+
+        if (mBluetoothService.isEnabled())
+            onBluetoothEnable();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            cleanupNative();
+        } finally {
+            super.finalize();
+        }
+    }
+
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -159,7 +158,7 @@
                 String address = (String)msg.obj;
                 // check bluetooth is still on, device is still preferred, and
                 // nothing is currently connected
-                if (mBluetooth.isEnabled() &&
+                if (mBluetoothService.isEnabled() &&
                         getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF &&
                         lookupSinksMatchingStates(new int[] {
                             BluetoothA2dp.STATE_CONNECTING,
@@ -170,45 +169,85 @@
                     connectSink(address);
                 }
                 break;
-            case MESSAGE_DISCONNECT:
-                handleDeferredDisconnect((String)msg.obj);
-                break;
             }
         }
     };
 
+    private int convertBluezSinkStringtoState(String value) {
+        if (value.equalsIgnoreCase("disconnected"))
+            return BluetoothA2dp.STATE_DISCONNECTED;
+        if (value.equalsIgnoreCase("connecting"))
+            return BluetoothA2dp.STATE_CONNECTING;
+        if (value.equalsIgnoreCase("connected"))
+            return BluetoothA2dp.STATE_CONNECTED;
+        if (value.equalsIgnoreCase("playing"))
+            return BluetoothA2dp.STATE_PLAYING;
+        return -1;
+    }
+
+    private synchronized boolean addAudioSink (String address) {
+        String path = mBluetoothService.getObjectPathFromAddress(address);
+        String propValues[] = (String []) getSinkPropertiesNative(path);
+        if (propValues == null) {
+            Log.e(TAG, "Error while getting AudioSink properties for device: " + address);
+            return false;
+        }
+        Integer state = null;
+        // Properties are name-value pairs
+        for (int i = 0; i < propValues.length; i+=2) {
+            if (propValues[i].equals(PROPERTY_STATE)) {
+                state = new Integer(convertBluezSinkStringtoState(propValues[i+1]));
+                break;
+            }
+        }
+        mAudioDevices.put(address, state);
+        handleSinkStateChange(address, BluetoothA2dp.STATE_DISCONNECTED, state);
+        return true;
+    }
+
     private synchronized void onBluetoothEnable() {
-        mAudioDevices = new HashMap<String, SinkState>();
-        String[] paths = (String[])listHeadsetsNative();
-        if (paths != null) {
-            for (String path : paths) {
-                mAudioDevices.put(path, new SinkState(getAddressNative(path),
-                        isSinkConnectedNative(path) ? BluetoothA2dp.STATE_CONNECTED :
-                                                      BluetoothA2dp.STATE_DISCONNECTED));
+        String devices = mBluetoothService.getProperty("Devices");
+        mSinkCount = 0;
+        if (devices != null) {
+            String [] paths = devices.split(",");
+            for (String path: paths) {
+                String address = mBluetoothService.getAddressFromObjectPath(path);
+                String []uuids = mBluetoothService.getRemoteUuids(address);
+                if (uuids != null)
+                    for (String uuid: uuids) {
+                        UUID remoteUuid = UUID.fromString(uuid);
+                        if (BluetoothUuid.isAudioSink(remoteUuid) ||
+                            BluetoothUuid.isAudioSource(remoteUuid) ||
+                            BluetoothUuid.isAdvAudioDist(remoteUuid)) {
+                            addAudioSink(address);
+                            break;
+                        }
+                    }
             }
         }
         mAudioManager.setParameter(BLUETOOTH_ENABLED, "true");
     }
 
     private synchronized void onBluetoothDisable() {
-        if (mAudioDevices != null) {
-            // copy to allow modification during iteration
-            String[] paths = new String[mAudioDevices.size()];
-            paths = mAudioDevices.keySet().toArray(paths);
-            for (String path : paths) {
-                switch (mAudioDevices.get(path).state) {
+        if (!mAudioDevices.isEmpty()) {
+            String [] addresses = new String[mAudioDevices.size()];
+            addresses = mAudioDevices.keySet().toArray(addresses);
+            for (String address : addresses) {
+                int state = getSinkState(address);
+                switch (state) {
                     case BluetoothA2dp.STATE_CONNECTING:
                     case BluetoothA2dp.STATE_CONNECTED:
                     case BluetoothA2dp.STATE_PLAYING:
-                        disconnectSinkNative(path);
-                        updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
+                        disconnectSinkNative(mBluetoothService.getObjectPathFromAddress(address));
+                        handleSinkStateChange(address,state, BluetoothA2dp.STATE_DISCONNECTED);
                         break;
                     case BluetoothA2dp.STATE_DISCONNECTING:
-                        updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
+                        handleSinkStateChange(address, BluetoothA2dp.STATE_DISCONNECTING,
+                                                BluetoothA2dp.STATE_DISCONNECTED);
                         break;
                 }
             }
-            mAudioDevices = null;
+            mAudioDevices.clear();
         }
         mAudioManager.setBluetoothA2dpOn(false);
         mAudioManager.setParameter(BLUETOOTH_ENABLED, "false");
@@ -221,9 +260,7 @@
         if (!BluetoothDevice.checkBluetoothAddress(address)) {
             return BluetoothError.ERROR;
         }
-        if (mAudioDevices == null) {
-            return BluetoothError.ERROR;
-        }
+
         // ignore if there are any active sinks
         if (lookupSinksMatchingStates(new int[] {
                 BluetoothA2dp.STATE_CONNECTING,
@@ -233,20 +270,11 @@
             return BluetoothError.ERROR;
         }
 
-        String path = lookupPath(address);
-        if (path == null) {
-            path = createHeadsetNative(address);
-            if (DBG) log("new bluez sink: " + address + " (" + path + ")");
-        }
-        if (path == null) {
+        if (mAudioDevices.get(address) == null && !addAudioSink(address))
             return BluetoothError.ERROR;
-        }
 
-        SinkState sink = mAudioDevices.get(path);
-        int state = BluetoothA2dp.STATE_DISCONNECTED;
-        if (sink != null) {
-            state = sink.state;
-        }
+        int state = mAudioDevices.get(address);
+
         switch (state) {
         case BluetoothA2dp.STATE_CONNECTED:
         case BluetoothA2dp.STATE_PLAYING:
@@ -256,11 +284,14 @@
             return BluetoothError.SUCCESS;
         }
 
+        String path = mBluetoothService.getObjectPathFromAddress(address);
+        if (path == null)
+            return BluetoothError.ERROR;
+
         // State is DISCONNECTED
         if (!connectSinkNative(path)) {
             return BluetoothError.ERROR;
         }
-        updateState(path, BluetoothA2dp.STATE_CONNECTING);
         return BluetoothError.SUCCESS;
     }
 
@@ -271,14 +302,12 @@
         if (!BluetoothDevice.checkBluetoothAddress(address)) {
             return BluetoothError.ERROR;
         }
-        if (mAudioDevices == null) {
-            return BluetoothError.ERROR;
-        }
-        String path = lookupPath(address);
+        String path = mBluetoothService.getObjectPathFromAddress(address);
         if (path == null) {
             return BluetoothError.ERROR;
         }
-        switch (mAudioDevices.get(path).state) {
+
+        switch (getSinkState(address)) {
         case BluetoothA2dp.STATE_DISCONNECTED:
             return BluetoothError.ERROR;
         case BluetoothA2dp.STATE_DISCONNECTING:
@@ -289,7 +318,6 @@
         if (!disconnectSinkNative(path)) {
             return BluetoothError.ERROR;
         } else {
-            updateState(path, BluetoothA2dp.STATE_DISCONNECTING);
             return BluetoothError.SUCCESS;
         }
     }
@@ -305,15 +333,10 @@
         if (!BluetoothDevice.checkBluetoothAddress(address)) {
             return BluetoothError.ERROR;
         }
-        if (mAudioDevices == null) {
+        Integer state = mAudioDevices.get(address);
+        if (state == null)
             return BluetoothA2dp.STATE_DISCONNECTED;
-        }
-        for (SinkState sink : mAudioDevices.values()) {
-            if (address.equals(sink.address)) {
-                return sink.state;
-            }
-        }
-        return BluetoothA2dp.STATE_DISCONNECTED;
+        return state;
     }
 
     public synchronized int getSinkPriority(String address) {
@@ -337,135 +360,42 @@
                 BluetoothError.SUCCESS : BluetoothError.ERROR;
     }
 
-    private synchronized void onHeadsetCreated(String path) {
-        updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
-    }
-
-    private synchronized void onHeadsetRemoved(String path) {
-        if (mAudioDevices == null) return;
-        mAudioDevices.remove(path);
-    }
-
-    private synchronized void onSinkConnected(String path) {
-        // if we are reconnected, do not process previous disconnect event.
-        mPendingDisconnects.remove(path);
-
-        if (mAudioDevices == null) return;
-        // bluez 3.36 quietly disconnects the previous sink when a new sink
-        // is connected, so we need to mark all previously connected sinks as
-        // disconnected
-
-        // copy to allow modification during iteration
-        String[] paths = new String[mAudioDevices.size()];
-        paths = mAudioDevices.keySet().toArray(paths);
-        for (String oldPath : paths) {
-            if (path.equals(oldPath)) {
-                continue;
-            }
-            int state = mAudioDevices.get(oldPath).state;
-            if (state == BluetoothA2dp.STATE_CONNECTED || state == BluetoothA2dp.STATE_PLAYING) {
-                updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
-            }
+    private synchronized void onSinkPropertyChanged(String path, String []propValues) {
+        String name = propValues[0];
+        String address = mBluetoothService.getAddressFromObjectPath(path);
+        if (address == null) {
+            Log.e(TAG, "onSinkPropertyChanged: Address of the remote device in null");
+            return;
         }
 
-        updateState(path, BluetoothA2dp.STATE_CONNECTING);
-        mAudioManager.setParameter(A2DP_SINK_ADDRESS, lookupAddress(path));
-        mAudioManager.setBluetoothA2dpOn(true);
-        updateState(path, BluetoothA2dp.STATE_CONNECTED);
-    }
-
-    private synchronized void onSinkDisconnected(String path) {
-        // This is to work around a problem in bluez that results 
-        // sink disconnect events being sent, immediately followed by a reconnect.
-        // To avoid unnecessary audio routing changes, we defer handling
-        // sink disconnects until after a short delay.
-        mPendingDisconnects.add(path);
-        Message msg = Message.obtain(mHandler, MESSAGE_DISCONNECT, path);
-        mHandler.sendMessageDelayed(msg, 2000);
-    }
-
-    private synchronized void handleDeferredDisconnect(String path) {
-        if (mPendingDisconnects.contains(path)) {
-            mPendingDisconnects.remove(path);
-            if (mSinkCount == 1) {
-                mAudioManager.setBluetoothA2dpOn(false);
-            }
-            updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
+        if (mAudioDevices.get(address) == null) {
+            // Ignore this state change, since it means we have got it after
+            // bluetooth has been disabled.
+            return;
+        }
+        if (name.equals(PROPERTY_STATE)) {
+            int state = convertBluezSinkStringtoState(propValues[1]);
+            int prevState = mAudioDevices.get(address);
+            handleSinkStateChange(address, prevState, state);
         }
     }
 
-    private synchronized void onSinkPlaying(String path) {
-        updateState(path, BluetoothA2dp.STATE_PLAYING);
-    }
-
-    private synchronized void onSinkStopped(String path) {
-        updateState(path, BluetoothA2dp.STATE_CONNECTED);
-    }
-
-    private synchronized final String lookupAddress(String path) {
-        if (mAudioDevices == null) return null;
-        SinkState sink = mAudioDevices.get(path);
-        if (sink == null) {
-            Log.w(TAG, "lookupAddress() called for unknown device " + path);
-            updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
-        }
-        String address = mAudioDevices.get(path).address;
-        if (address == null) Log.e(TAG, "Can't find address for " + path);
-        return address;
-    }
-
-    private synchronized final String lookupPath(String address) {
-        if (mAudioDevices == null) return null;
-
-        for (String path : mAudioDevices.keySet()) {
-            if (address.equals(mAudioDevices.get(path).address)) {
-                return path;
-            }
-        }
-        return null;
-    }
-
-    private synchronized List<String> lookupSinksMatchingStates(int[] states) {
-        List<String> sinks = new ArrayList<String>();
-        if (mAudioDevices == null) {
-            return sinks;
-        }
-        for (SinkState sink : mAudioDevices.values()) {
-            for (int state : states) {
-                if (sink.state == state) {
-                    sinks.add(sink.address);
-                    break;
-                }
-            }
-        }
-        return sinks;
-    }
-
-    private synchronized void updateState(String path, int state) {
-        if (mAudioDevices == null) return;
-
-        SinkState s = mAudioDevices.get(path);
-        int prevState;
-        String address;
-        if (s == null) {
-            address = getAddressNative(path);
-            mAudioDevices.put(path, new SinkState(address, state));
-            prevState = BluetoothA2dp.STATE_DISCONNECTED;
-        } else {
-            address = lookupAddress(path);
-            prevState = s.state;
-            s.state = state;
-        }
-
+    private void handleSinkStateChange(String address, int prevState, int state) {
         if (state != prevState) {
-            if (DBG) log("state " + address + " (" + path + ") " + prevState + "->" + state);
-            
-            // keep track of the number of active sinks
-            if (prevState == BluetoothA2dp.STATE_DISCONNECTED) {
-                mSinkCount++;
-            } else if (state == BluetoothA2dp.STATE_DISCONNECTED) {
-                mSinkCount--;
+            if (state == BluetoothA2dp.STATE_DISCONNECTED ||
+                    state == BluetoothA2dp.STATE_DISCONNECTING) {
+                if (prevState == BluetoothA2dp.STATE_CONNECTED ||
+                        prevState == BluetoothA2dp.STATE_PLAYING) {
+                   // disconnecting or disconnected
+                   Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+                   mContext.sendBroadcast(intent);
+                }
+                if (--mSinkCount == 0)
+                    mAudioManager.setBluetoothA2dpOn(false);
+            } else if (state == BluetoothA2dp.STATE_CONNECTED) {
+                mSinkCount ++;
             }
+            mAudioDevices.put(address, state);
 
             Intent intent = new Intent(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
             intent.putExtra(BluetoothIntent.ADDRESS, address);
@@ -473,25 +403,40 @@
             intent.putExtra(BluetoothA2dp.SINK_STATE, state);
             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
 
-            if ((prevState == BluetoothA2dp.STATE_CONNECTED ||
-                 prevState == BluetoothA2dp.STATE_PLAYING) &&
-                    (state != BluetoothA2dp.STATE_CONNECTING &&
-                     state != BluetoothA2dp.STATE_CONNECTED &&
-                     state != BluetoothA2dp.STATE_PLAYING)) {
-                // disconnected
-                intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
-                mContext.sendBroadcast(intent);
+            if (DBG) log("A2DP state : address: " + address + " State:" + prevState + "->" + state);
+
+            if (state == BluetoothA2dp.STATE_CONNECTED) {
+                mAudioManager.setParameter(A2DP_SINK_ADDRESS, address);
+                mAudioManager.setBluetoothA2dpOn(true);
             }
         }
     }
 
+    private synchronized List<String> lookupSinksMatchingStates(int[] states) {
+        List<String> sinks = new ArrayList<String>();
+        if (mAudioDevices.isEmpty()) {
+            return sinks;
+        }
+        for (String path: mAudioDevices.keySet()) {
+            int sinkState = getSinkState(path);
+            for (int state : states) {
+                if (state == sinkState) {
+                    sinks.add(path);
+                    break;
+                }
+            }
+        }
+        return sinks;
+    }
+
+
     @Override
     protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mAudioDevices == null) return;
+        if (mAudioDevices.isEmpty()) return;
         pw.println("Cached audio devices:");
-        for (String path : mAudioDevices.keySet()) {
-            SinkState sink = mAudioDevices.get(path);
-            pw.println(path + " " + sink.address + " " + BluetoothA2dp.stateToString(sink.state));
+        for (String address : mAudioDevices.keySet()) {
+            int state = mAudioDevices.get(address);
+            pw.println(address + " " + BluetoothA2dp.stateToString(state));
         }
     }
 
@@ -501,11 +446,7 @@
 
     private native boolean initNative();
     private native void cleanupNative();
-    private synchronized native String[] listHeadsetsNative();
-    private synchronized native String createHeadsetNative(String address);
-    private synchronized native boolean removeHeadsetNative(String path);
-    private synchronized native String getAddressNative(String path);
     private synchronized native boolean connectSinkNative(String path);
     private synchronized native boolean disconnectSinkNative(String path);
-    private synchronized native boolean isSinkConnectedNative(String path);
+    private synchronized native Object []getSinkPropertiesNative(String path);
 }
diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java
index 8c843ef..77b1b1d 100644
--- a/core/java/android/server/BluetoothDeviceService.java
+++ b/core/java/android/server/BluetoothDeviceService.java
@@ -30,7 +30,6 @@
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothIntent;
 import android.bluetooth.IBluetoothDevice;
-import android.bluetooth.IBluetoothDeviceCallback;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -38,7 +37,6 @@
 import android.content.IntentFilter;
 import android.os.Binder;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -46,6 +44,8 @@
 import android.provider.Settings;
 import android.util.Log;
 
+import com.android.internal.app.IBatteryStats;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
@@ -55,8 +55,6 @@
 import java.util.Iterator;
 import java.util.Map;
 
-import com.android.internal.app.IBatteryStats;
-
 public class BluetoothDeviceService extends IBluetoothDevice.Stub {
     private static final String TAG = "BluetoothDeviceService";
     private static final boolean DBG = true;
@@ -80,10 +78,12 @@
     private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
     private static final int MESSAGE_FINISH_DISABLE = 2;
 
+    private Map<String, String> mProperties;
+    private HashMap <String, Map<String, String>> mRemoteDeviceProperties;
+
     static {
         classInitNative();
     }
-    private native static void classInitNative();
 
     public BluetoothDeviceService(Context context) {
         mContext = context;
@@ -109,8 +109,9 @@
         mIsDiscovering = false;
         mEventLoop = new BluetoothEventLoop(mContext, this);
         registerForAirplaneMode();
+        mProperties = new HashMap<String, String>();
+        mRemoteDeviceProperties = new HashMap<String, Map<String,String>>();
     }
-    private native void initializeNativeDataNative();
 
     @Override
     protected void finalize() throws Throwable {
@@ -123,13 +124,11 @@
             super.finalize();
         }
     }
-    private native void cleanupNativeDataNative();
 
     public boolean isEnabled() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         return mBluetoothState == BluetoothDevice.BLUETOOTH_STATE_ON;
     }
-    private native int isEnabledNative();
 
     public int getBluetoothState() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
@@ -150,8 +149,7 @@
      * @param saveSetting If true, disable BT in settings
      */
     public synchronized boolean disable(boolean saveSetting) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
 
         switch (mBluetoothState) {
         case BluetoothDevice.BLUETOOTH_STATE_OFF:
@@ -180,6 +178,7 @@
             return;
         }
         mEventLoop.stop();
+        tearDownNativeDataNative();
         disableNative();
 
         // mark in progress bondings as cancelled
@@ -188,25 +187,13 @@
                                     BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
         }
 
-        // Remove remoteServiceChannelCallbacks
-        HashMap<String, IBluetoothDeviceCallback> callbacksMap =
-            mEventLoop.getRemoteServiceChannelCallbacks();
-
-        for (Iterator<String> i = callbacksMap.keySet().iterator(); i.hasNext();) {
-            String address = i.next();
-            IBluetoothDeviceCallback callback = callbacksMap.get(address);
-            try {
-                callback.onGetRemoteServiceChannelResult(address, BluetoothError.ERROR_DISABLED);
-            } catch (RemoteException e) {}
-            i.remove();
-        }
-
         // update mode
         Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION);
         intent.putExtra(BluetoothIntent.SCAN_MODE, BluetoothDevice.SCAN_MODE_NONE);
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
 
         mIsDiscovering = false;
+        mProperties.clear();
 
         if (saveSetting) {
             persistBluetoothOnSetting(false);
@@ -270,7 +257,7 @@
         if (!disable(false)) {
             mRestart = false;
         }
-    }   
+    }
 
     private synchronized void setBluetoothState(int state) {
         if (state == mBluetoothState) {
@@ -343,6 +330,9 @@
 
 
             if (res) {
+                if (!setupNativeDataNative()) {
+                    return;
+                }
                 if (mSaveSetting) {
                     persistBluetoothOnSetting(true);
                 }
@@ -369,7 +359,8 @@
 
             if (res) {
                 // Update mode
-                mEventLoop.onModeChanged(getModeNative());
+                String[] propVal = {"Pairable", getProperty("Pairable")};
+                mEventLoop.onPropertyChanged(propVal);
             }
 
             if (mIsAirplaneSensitive && isAirplaneModeOn()) {
@@ -386,9 +377,6 @@
         Binder.restoreCallingIdentity(origCallerIdentityToken);
     }
 
-    private native int enableNative();
-    private native int disableNative();
-
     /* package */ BondState getBondState() {
         return mBondState;
     }
@@ -423,14 +411,19 @@
             if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_TURNING_ON) {
                 return;
             }
-            String[] bonds = listBondingsNative();
+            String []bonds = null;
+            String val = getProperty("Devices");
+            if (val != null) {
+                bonds = val.split(",");
+            }
             if (bonds == null) {
                 return;
             }
             mState.clear();
             if (DBG) log("found " + bonds.length + " bonded devices");
-            for (String address : bonds) {
-                mState.put(address.toUpperCase(), BluetoothDevice.BOND_BONDED);
+            for (String device : bonds) {
+                mState.put(getAddressFromObjectPath(device).toUpperCase(),
+                        BluetoothDevice.BOND_BONDED);
             }
         }
 
@@ -528,7 +521,6 @@
         }
 
     }
-    private native String[] listBondingsNative();
 
     private static String toBondStateString(int bondState) {
         switch (bondState) {
@@ -543,17 +535,49 @@
         }
     }
 
-    public synchronized String getAddress() {
+    /*package*/synchronized void getAllProperties() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return getAddressNative();
-    }
-    private native String getAddressNative();
+        mProperties.clear();
 
-    public synchronized String getName() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return getNameNative();
+        String properties[] = (String [])getAdapterPropertiesNative();
+        // The String Array consists of key-value pairs.
+        if (properties == null) {
+            Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
+            return;
+        }
+
+        for (int i = 0; i < properties.length; i++) {
+            String name = properties[i];
+            String newValue;
+            int len;
+            if (name == null) {
+                Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
+                continue;
+            }
+            if (name.equals("Devices")) {
+                len = Integer.valueOf(properties[++i]);
+                if (len != 0)
+                    newValue = "";
+                else
+                    newValue = null;
+                for (int j = 0; j < len; j++) {
+                    newValue += properties[++i] + ",";
+                }
+            } else {
+                newValue = properties[++i];
+            }
+            mProperties.put(name, newValue);
+        }
+
+        // Add adapter object path property.
+        String adapterPath = getAdapterPathNative();
+        if (adapterPath != null)
+            mProperties.put("ObjectPath", adapterPath + "/dev_");
     }
-    private native String getNameNative();
+
+    /* package */ synchronized void setProperty(String name, String value) {
+        mProperties.put(name, value);
+    }
 
     public synchronized boolean setName(String name) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -561,74 +585,27 @@
         if (name == null) {
             return false;
         }
-        // hcid handles persistance of the bluetooth name
-        return setNameNative(name);
+        return setPropertyString("Name", name);
     }
-    private native boolean setNameNative(String name);
 
-    /**
-     * Returns the user-friendly name of a remote device.  This value is
-     * retrned from our local cache, which is updated during device discovery.
-     * Do not expect to retrieve the updated remote name immediately after
-     * changing the name on the remote device.
-     *
-     * @param address Bluetooth address of remote device.
-     *
-     * @return The user-friendly name of the specified remote device.
-     */
-    public synchronized String getRemoteName(String address) {
+    //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
+    // Either have a single property function with Object as the parameter
+    // or have a function for each property and then obfuscate in the JNI layer.
+    // The following looks dirty.
+    private boolean setPropertyString(String key, String value) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        if (!BluetoothDevice.checkBluetoothAddress(address)) {
-            return null;
-        }
-        return getRemoteNameNative(address);
+        return setAdapterPropertyStringNative(key, value);
     }
-    private native String getRemoteNameNative(String address);
 
-    /* pacakge */ native String getAdapterPathNative();
-
-    public synchronized boolean startDiscovery(boolean resolveNames) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-        return startDiscoveryNative(resolveNames);
-    }
-    private native boolean startDiscoveryNative(boolean resolveNames);
-
-    public synchronized boolean cancelDiscovery() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-        return cancelDiscoveryNative();
-    }
-    private native boolean cancelDiscoveryNative();
-
-    public synchronized boolean isDiscovering() {
+    private boolean setPropertyInteger(String key, int value) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return mIsDiscovering;
+        return setAdapterPropertyIntegerNative(key, value);
     }
 
-    /* package */ void setIsDiscovering(boolean isDiscovering) {
-        mIsDiscovering = isDiscovering;
-    }
-
-    public synchronized boolean startPeriodicDiscovery() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-        return startPeriodicDiscoveryNative();
-    }
-    private native boolean startPeriodicDiscoveryNative();
-
-    public synchronized boolean stopPeriodicDiscovery() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-        return stopPeriodicDiscoveryNative();
-    }
-    private native boolean stopPeriodicDiscoveryNative();
-
-    public synchronized boolean isPeriodicDiscovery() {
+    private boolean setPropertyBoolean(String key, boolean value) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return isPeriodicDiscoveryNative();
+        return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
     }
-    private native boolean isPeriodicDiscoveryNative();
 
     /**
      * Set the discoverability window for the device.  A timeout of zero
@@ -642,9 +619,67 @@
     public synchronized boolean setDiscoverableTimeout(int timeout) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
-        return setDiscoverableTimeoutNative(timeout);
+        return setPropertyInteger("DiscoverableTimeout", timeout);
     }
-    private native boolean setDiscoverableTimeoutNative(int timeout_s);
+
+    public synchronized boolean setScanMode(int mode) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+        boolean pairable = false, discoverable = false;
+        String modeString = scanModeToBluezString(mode);
+        if (modeString.equals("off")) {
+            pairable = false;
+            discoverable = false;
+        } else if (modeString.equals("pariable")) {
+            pairable = true;
+            discoverable = false;
+        } else if (modeString.equals("discoverable")) {
+            pairable = true;
+            discoverable = true;
+        }
+        setPropertyBoolean("Pairable", pairable);
+        setPropertyBoolean("Discoverable", discoverable);
+
+        return true;
+    }
+
+    /*package*/ synchronized String getProperty (String name) {
+        if (!mProperties.isEmpty())
+            return mProperties.get(name);
+        getAllProperties();
+        return mProperties.get(name);
+    }
+
+    public synchronized String getAddress() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return getProperty("Address");
+    }
+
+    public synchronized String getName() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return getProperty("Name");
+    }
+
+    /**
+     * Returns the user-friendly name of a remote device.  This value is
+     * returned from our local cache, which is updated when onPropertyChange
+     * event is received.
+     * Do not expect to retrieve the updated remote name immediately after
+     * changing the name on the remote device.
+     *
+     * @param address Bluetooth address of remote device.
+     *
+     * @return The user-friendly name of the specified remote device.
+     */
+    public synchronized String getRemoteName(String address) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        if (!BluetoothDevice.checkBluetoothAddress(address)) {
+            return null;
+        }
+        Map <String, String> properties = mRemoteDeviceProperties.get(address);
+        if (properties != null) return properties.get("Name");
+        return null;
+    }
 
     /**
      * Get the discoverability window for the device.  A timeout of zero
@@ -656,45 +691,46 @@
      */
     public synchronized int getDiscoverableTimeout() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return getDiscoverableTimeoutNative();
+        String timeout = getProperty("DiscoverableTimeout");
+        if (timeout != null)
+           return Integer.valueOf(timeout);
+        else
+            return -1;
     }
-    private native int getDiscoverableTimeoutNative();
-
-    public synchronized boolean isAclConnected(String address) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        if (!BluetoothDevice.checkBluetoothAddress(address)) {
-            return false;
-        }
-        return isConnectedNative(address);
-    }
-    private native boolean isConnectedNative(String address);
 
     public synchronized int getScanMode() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return bluezStringToScanMode(getModeNative());
-    }
-    private native String getModeNative();
+        if (!isEnabled())
+            return BluetoothError.ERROR;
 
-    public synchronized boolean setScanMode(int mode) {
+        boolean pairable = getProperty("Pairable").equals("true");
+        boolean discoverable = getProperty("Discoverable").equals("true");
+        return bluezStringToScanMode (pairable, discoverable);
+    }
+
+    public synchronized boolean startDiscovery() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
-        String bluezMode = scanModeToBluezString(mode);
-        if (bluezMode != null) {
-            return setModeNative(bluezMode);
-        }
-        return false;
-    }
-    private native boolean setModeNative(String mode);
-
-    public synchronized boolean disconnectRemoteDeviceAcl(String address) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-        if (!BluetoothDevice.checkBluetoothAddress(address)) {
+        if (!isEnabled()) {
             return false;
         }
-        return disconnectRemoteDeviceNative(address);
+        return startDiscoveryNative();
     }
-    private native boolean disconnectRemoteDeviceNative(String address);
+
+    public synchronized boolean cancelDiscovery() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+        return stopDiscoveryNative();
+    }
+
+    public synchronized boolean isDiscovering() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return mIsDiscovering;
+    }
+
+    /* package */ void setIsDiscovering(boolean isDiscovering) {
+        mIsDiscovering = isDiscovering;
+    }
 
     public synchronized boolean createBond(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -719,14 +755,13 @@
             return false;
         }
 
-        if (!createBondingNative(address, 60000 /* 1 minute */)) {
+        if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
             return false;
         }
 
         mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
         return true;
     }
-    private native boolean createBondingNative(String address, int timeout_ms);
 
     public synchronized boolean cancelBondProcess(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -741,10 +776,9 @@
 
         mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
                                 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
-        cancelBondingProcessNative(address);
+        cancelDeviceCreationNative(address);
         return true;
     }
-    private native boolean cancelBondingProcessNative(String address);
 
     public synchronized boolean removeBond(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -752,9 +786,8 @@
         if (!BluetoothDevice.checkBluetoothAddress(address)) {
             return false;
         }
-        return removeBondingNative(address);
+        return removeDeviceNative(getObjectPathFromAddress(address));
     }
-    private native boolean removeBondingNative(String address);
 
     public synchronized String[] listBonds() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
@@ -769,198 +802,78 @@
         return mBondState.getBondState(address.toUpperCase());
     }
 
-    public synchronized String[] listAclConnections() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return listConnectionsNative();
-    }
-    private native String[] listConnectionsNative();
-
-    /**
-     * This method lists all remote devices that this adapter is aware of.
-     * This is a list not only of all most-recently discovered devices, but of
-     * all devices discovered by this adapter up to some point in the past.
-     * Note that many of these devices may not be in the neighborhood anymore,
-     * and attempting to connect to them will result in an error.
-     *
-     * @return An array of strings representing the Bluetooth addresses of all
-     *         remote devices that this adapter is aware of.
-     */
-    public synchronized String[] listRemoteDevices() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return listRemoteDevicesNative();
-    }
-    private native String[] listRemoteDevicesNative();
-
-    /**
-     * Returns the version of the Bluetooth chip. This version is compiled from
-     * the LMP version. In case of EDR the features attribute must be checked.
-     * Example: "Bluetooth 2.0 + EDR".
-     *
-     * @return a String representation of the this Adapter's underlying
-     *         Bluetooth-chip version.
-     */
-    public synchronized String getVersion() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return getVersionNative();
-    }
-    private native String getVersionNative();
-
-    /**
-     * Returns the revision of the Bluetooth chip. This is a vendor-specific
-     * value and in most cases it represents the firmware version. This might
-     * derive from the HCI revision and LMP subversion values or via extra
-     * vendord specific commands.
-     * In case the revision of a chip is not available. This method should
-     * return the LMP subversion value as a string.
-     * Example: "HCI 19.2"
-     *
-     * @return The HCI revision of this adapter.
-     */
-    public synchronized String getRevision() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return getRevisionNative();
-    }
-    private native String getRevisionNative();
-
-    /**
-     * Returns the manufacturer of the Bluetooth chip. If the company id is not
-     * known the sting "Company ID %d" where %d should be replaced with the
-     * numeric value from the manufacturer field.
-     * Example: "Cambridge Silicon Radio"
-     *
-     * @return Manufacturer name.
-     */
-    public synchronized String getManufacturer() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return getManufacturerNative();
-    }
-    private native String getManufacturerNative();
-
-    /**
-     * Returns the company name from the OUI database of the Bluetooth device
-     * address. This function will need a valid and up-to-date oui.txt from
-     * the IEEE. This value will be different from the manufacturer string in
-     * the most cases.
-     * If the oui.txt file is not present or the OUI part of the Bluetooth
-     * address is not listed, it should return the string "OUI %s" where %s is
-     * the actual OUI.
-     *
-     * Example: "Apple Computer"
-     *
-     * @return company name
-     */
-    public synchronized String getCompany() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return getCompanyNative();
-    }
-    private native String getCompanyNative();
-
-    /**
-     * Like getVersion(), but for a remote device.
-     *
-     * @param address The Bluetooth address of the remote device.
-     *
-     * @return remote-device Bluetooth version
-     *
-     * @see #getVersion
-     */
-    public synchronized String getRemoteVersion(String address) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        if (!BluetoothDevice.checkBluetoothAddress(address)) {
-            return null;
+    /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
+        Map<String, String> properties = mRemoteDeviceProperties.get(address);
+        if (properties != null) {
+            return properties.get(property);
+        } else {
+            // Query for remote device properties, again.
+            // We will need to reload the cache when we switch Bluetooth on / off
+            // or if we crash.
+            String objectPath = getObjectPathFromAddress(address);
+            String propValues[] = (String [])getDevicePropertiesNative(objectPath);
+            if (propValues != null) {
+                addRemoteDeviceProperties(address, propValues);
+                return getRemoteDeviceProperty(address, property);
+            }
         }
-        return getRemoteVersionNative(address);
+        Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
+        return null;
     }
-    private native String getRemoteVersionNative(String address);
 
-    /**
-     * Like getRevision(), but for a remote device.
-     *
-     * @param address The Bluetooth address of the remote device.
-     *
-     * @return remote-device HCI revision
-     *
-     * @see #getRevision
-     */
-    public synchronized String getRemoteRevision(String address) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        if (!BluetoothDevice.checkBluetoothAddress(address)) {
-            return null;
+    /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
+        /*
+         * We get a DeviceFound signal every time RSSI changes or name changes.
+         * Don't create a new Map object every time */
+        Map<String, String> propertyValues = mRemoteDeviceProperties.get(address);
+        if (propertyValues != null) {
+            propertyValues.clear();
+        } else {
+            propertyValues = new HashMap<String, String>();
         }
-        return getRemoteRevisionNative(address);
-    }
-    private native String getRemoteRevisionNative(String address);
 
-    /**
-     * Like getManufacturer(), but for a remote device.
-     *
-     * @param address The Bluetooth address of the remote device.
-     *
-     * @return remote-device Bluetooth chip manufacturer
-     *
-     * @see #getManufacturer
-     */
-    public synchronized String getRemoteManufacturer(String address) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        if (!BluetoothDevice.checkBluetoothAddress(address)) {
-            return null;
+        for (int i = 0; i < properties.length; i++) {
+            String name = properties[i];
+            String newValue;
+            int len;
+            if (name == null) {
+                Log.e(TAG, "Error: Remote Device Property at index" + i + "is null");
+                continue;
+            }
+            if (name.equals("UUIDs") || name.equals("Nodes")) {
+                len = Integer.valueOf(properties[++i]);
+                if (len != 0)
+                    newValue = "";
+                else
+                    newValue = null;
+                for (int j = 0; j < len; j++) {
+                    newValue += properties[++i] + ",";
+                }
+            } else {
+                newValue = properties[++i];
+            }
+            propertyValues.put(name, newValue);
         }
-        return getRemoteManufacturerNative(address);
+        mRemoteDeviceProperties.put(address, propertyValues);
     }
-    private native String getRemoteManufacturerNative(String address);
 
-    /**
-     * Like getCompany(), but for a remote device.
-     *
-     * @param address The Bluetooth address of the remote device.
-     *
-     * @return remote-device company
-     *
-     * @see #getCompany
-     */
-    public synchronized String getRemoteCompany(String address) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        if (!BluetoothDevice.checkBluetoothAddress(address)) {
-            return null;
+    /* package */ void removeRemoteDeviceProperties(String address) {
+        mRemoteDeviceProperties.remove(address);
+    }
+
+    /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
+                                                              String value) {
+        Map <String, String> propVal = mRemoteDeviceProperties.get(address);
+        if (propVal != null) {
+            propVal.put(name, value);
+            mRemoteDeviceProperties.put(address, propVal);
+        } else {
+            Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
         }
-        return getRemoteCompanyNative(address);
     }
-    private native String getRemoteCompanyNative(String address);
 
     /**
-     * Returns the date and time when the specified remote device has been seen
-     * by a discover procedure.
-     * Example: "2006-02-08 12:00:00 GMT"
-     *
-     * @return a String with the timestamp.
-     */
-    public synchronized String lastSeen(String address) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        if (!BluetoothDevice.checkBluetoothAddress(address)) {
-            return null;
-        }
-        return lastSeenNative(address);
-    }
-    private native String lastSeenNative(String address);
-
-    /**
-     * Returns the date and time when the specified remote device has last been
-     * connected to
-     * Example: "2006-02-08 12:00:00 GMT"
-     *
-     * @return a String with the timestamp.
-     */
-    public synchronized String lastUsed(String address) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        if (!BluetoothDevice.checkBluetoothAddress(address)) {
-            return null;
-        }
-        return lastUsedNative(address);
-    }
-    private native String lastUsedNative(String address);
-
-    /**
-     * Gets the remote major, minor, and service classes encoded as a 32-bit
+     * Gets the remote major, minor classes encoded as a 32-bit
      * integer.
      *
      * Note: this value is retrieved from cache, because we get it during
@@ -968,120 +881,56 @@
      *
      * @return 32-bit integer encoding the remote major, minor, and service
      *         classes.
-     *
-     * @see #getRemoteMajorClass
-     * @see #getRemoteMinorClass
-     * @see #getRemoteServiceClasses
      */
     public synchronized int getRemoteClass(String address) {
         if (!BluetoothDevice.checkBluetoothAddress(address)) {
             mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
             return BluetoothClass.ERROR;
         }
-        return getRemoteClassNative(address);
+        String val = getRemoteDeviceProperty(address, "Class");
+        if (val == null)
+            return BluetoothClass.ERROR;
+        else {
+            return Integer.valueOf(val);
+        }
     }
-    private native int getRemoteClassNative(String address);
+
 
     /**
      * Gets the remote features encoded as bit mask.
      *
      * Note: This method may be obsoleted soon.
      *
-     * @return byte array of features.
+     * @return String array of 128bit UUIDs
      */
-    public synchronized byte[] getRemoteFeatures(String address) {
+    public synchronized String[] getRemoteUuids(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         if (!BluetoothDevice.checkBluetoothAddress(address)) {
             return null;
         }
-        return getRemoteFeaturesNative(address);
+        String value = getRemoteDeviceProperty(address, "UUIDs");
+        String[] uuids = null;
+        // The UUIDs are stored as a "," separated string.
+        if (value != null)
+             uuids = value.split(",");
+        return uuids;
     }
-    private native byte[] getRemoteFeaturesNative(String address);
 
     /**
-     * This method and {@link #getRemoteServiceRecord} query the SDP service
-     * on a remote device.  They do not interpret the data, but simply return
-     * it raw to the user.  To read more about SDP service handles and records,
-     * consult the Bluetooth core documentation (www.bluetooth.com).
+     * Gets the rfcomm channel associated with the UUID.
      *
-     * @param address Bluetooth address of remote device.
-     * @param match a String match to narrow down the service-handle search.
-     *        The only supported value currently is "hsp" for the headset
-     *        profile.  To retrieve all service handles, simply pass an empty
-     *        match string.
+     * @param address Address of the remote device
+     * @param uuid UUID of the service attribute
      *
-     * @return all service handles corresponding to the string match.
-     *
-     * @see #getRemoteServiceRecord
+     * @return rfcomm channel associated with the service attribute
      */
-    public synchronized int[] getRemoteServiceHandles(String address, String match) {
+    public int getRemoteServiceChannel(String address, String uuid) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         if (!BluetoothDevice.checkBluetoothAddress(address)) {
-            return null;
+            return BluetoothError.ERROR_IPC;
         }
-        if (match == null) {
-            match = "";
-        }
-        return getRemoteServiceHandlesNative(address, match);
+        return getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid, 0x0004);
     }
-    private native int[] getRemoteServiceHandlesNative(String address, String match);
-
-    /**
-     * This method retrieves the service records corresponding to a given
-     * service handle (method {@link #getRemoteServiceHandles} retrieves the
-     * service handles.)
-     *
-     * This method and {@link #getRemoteServiceHandles} do not interpret their
-     * data, but simply return it raw to the user.  To read more about SDP
-     * service handles and records, consult the Bluetooth core documentation
-     * (www.bluetooth.com).
-     *
-     * @param address Bluetooth address of remote device.
-     * @param handle Service handle returned by {@link #getRemoteServiceHandles}
-     *
-     * @return a byte array of all service records corresponding to the
-     *         specified service handle.
-     *
-     * @see #getRemoteServiceHandles
-     */
-    public synchronized byte[] getRemoteServiceRecord(String address, int handle) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        if (!BluetoothDevice.checkBluetoothAddress(address)) {
-            return null;
-        }
-        return getRemoteServiceRecordNative(address, handle);
-    }
-    private native byte[] getRemoteServiceRecordNative(String address, int handle);
-
-    private static final int MAX_OUTSTANDING_ASYNC = 32;
-
-    // AIDL does not yet support short's
-    public synchronized boolean getRemoteServiceChannel(String address, int uuid16,
-            IBluetoothDeviceCallback callback) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        if (!BluetoothDevice.checkBluetoothAddress(address)) {
-            return false;
-        }
-        HashMap<String, IBluetoothDeviceCallback> callbacks =
-            mEventLoop.getRemoteServiceChannelCallbacks();
-        if (callbacks.containsKey(address)) {
-            Log.w(TAG, "SDP request already in progress for " + address);
-            return false;
-        }
-        // Protect from malicious clients - only allow 32 bonding requests per minute.
-        if (callbacks.size() > MAX_OUTSTANDING_ASYNC) {
-            Log.w(TAG, "Too many outstanding SDP requests, dropping request for " + address);
-            return false;
-        }
-        callbacks.put(address, callback);
-
-        if (!getRemoteServiceChannelNative(address, (short)uuid16)) {
-            callbacks.remove(address);
-            return false;
-        }
-        return true;
-    }
-    private native boolean getRemoteServiceChannelNative(String address, short uuid16);
 
     public synchronized boolean setPin(String address, byte[] pin) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -1108,7 +957,6 @@
         }
         return setPinNative(address, pinString, data.intValue());
     }
-    private native boolean setPinNative(String address, String pin, int nativeData);
 
     public synchronized boolean cancelPin(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -1126,7 +974,6 @@
         }
         return cancelPinNative(address, data.intValue());
     }
-    private native boolean cancelPinNative(String address, int natveiData);
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -1190,20 +1037,22 @@
 
         BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
 
-        String[] addresses = listRemoteDevices();
-
         pw.println("\n--Known devices--");
-        for (String address : addresses) {
+        for (String address : mRemoteDeviceProperties.keySet()) {
             pw.printf("%s %10s (%d) %s\n", address,
                        toBondStateString(mBondState.getBondState(address)),
                        mBondState.getAttempt(address),
                        getRemoteName(address));
         }
 
-        addresses = listAclConnections();
+        String value = getProperty("Devices");
+        String []devicesObjectPath = null;
+        if (value != null) {
+            devicesObjectPath = value.split(",");
+        }
         pw.println("\n--ACL connected devices--");
-        for (String address : addresses) {
-            pw.println(address);
+        for (String device : devicesObjectPath) {
+            pw.println(getAddressFromObjectPath(device));
         }
 
         // Rather not do this from here, but no-where else and I need this
@@ -1229,20 +1078,13 @@
         headset.close();
     }
 
-    /* package */ static int bluezStringToScanMode(String mode) {
-        if (mode == null) {
-            return BluetoothError.ERROR;
-        }
-        mode = mode.toLowerCase();
-        if (mode.equals("off")) {
-            return BluetoothDevice.SCAN_MODE_NONE;
-        } else if (mode.equals("connectable")) {
-            return BluetoothDevice.SCAN_MODE_CONNECTABLE;
-        } else if (mode.equals("discoverable")) {
+    /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
+        if (pairable && discoverable)
             return BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
-        } else {
-            return BluetoothError.ERROR;
-        }
+        else if (pairable && !discoverable)
+            return BluetoothDevice.SCAN_MODE_CONNECTABLE;
+        else
+            return BluetoothDevice.SCAN_MODE_NONE;
     }
 
     /* package */ static String scanModeToBluezString(int mode) {
@@ -1257,7 +1099,67 @@
         return null;
     }
 
+    /*package*/ String getAddressFromObjectPath(String objectPath) {
+        String adapterObjectPath = getProperty("ObjectPath");
+        if (adapterObjectPath == null || objectPath == null) {
+            Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
+                    "  or deviceObjectPath:" + objectPath + " is null");
+            return null;
+        }
+        if (!objectPath.startsWith(adapterObjectPath)) {
+            Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
+                    "  is not a prefix of deviceObjectPath:" + objectPath +
+                    "bluetoothd crashed ?");
+            return null;
+        }
+        String address = objectPath.substring(adapterObjectPath.length());
+        if (address != null) return address.replace('_', ':');
+
+        Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
+        return null;
+    }
+
+    /*package*/ String getObjectPathFromAddress(String address) {
+        String path = getProperty("ObjectPath");
+        if (path == null) {
+            Log.e(TAG, "Error: Object Path is null");
+            return null;
+        }
+        path = path + address.replace(":", "_");
+        return path;
+    }
+
     private static void log(String msg) {
         Log.d(TAG, msg);
     }
+
+    private native static void classInitNative();
+    private native void initializeNativeDataNative();
+    private native boolean setupNativeDataNative();
+    private native boolean tearDownNativeDataNative();
+    private native void cleanupNativeDataNative();
+    private native String getAdapterPathNative();
+
+    private native int isEnabledNative();
+    private native int enableNative();
+    private native int disableNative();
+
+    private native Object[] getAdapterPropertiesNative();
+    private native Object[] getDevicePropertiesNative(String objectPath);
+    private native boolean setAdapterPropertyStringNative(String key, String value);
+    private native boolean setAdapterPropertyIntegerNative(String key, int value);
+    private native boolean setAdapterPropertyBooleanNative(String key, int value);
+
+    private native boolean startDiscoveryNative();
+    private native boolean stopDiscoveryNative();
+
+    private native boolean createPairedDeviceNative(String address, int timeout_ms);
+    private native boolean cancelDeviceCreationNative(String address);
+    private native boolean removeDeviceNative(String objectPath);
+    private native int getDeviceServiceChannelNative(String objectPath, String uuid,
+            int attributeId);
+
+    private native boolean cancelPinNative(String address, int nativeData);
+    private native boolean setPinNative(String address, String pin, int nativeData);
+
 }
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 8cc229b..38eb4d7 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -21,15 +21,15 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothError;
 import android.bluetooth.BluetoothIntent;
-import android.bluetooth.IBluetoothDeviceCallback;
+import android.bluetooth.BluetoothUuid;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Message;
-import android.os.RemoteException;
 import android.util.Log;
 
 import java.util.HashMap;
+import java.util.UUID;
 
 /**
  * TODO: Move this to
@@ -47,7 +47,6 @@
     private boolean mStarted;
     private boolean mInterrupted;
     private final HashMap<String, Integer> mPasskeyAgentRequestData;
-    private final HashMap<String, IBluetoothDeviceCallback> mGetRemoteServiceChannelCallbacks;
     private final BluetoothDeviceService mBluetoothService;
     private final Context mContext;
 
@@ -89,10 +88,8 @@
         mBluetoothService = bluetoothService;
         mContext = context;
         mPasskeyAgentRequestData = new HashMap();
-        mGetRemoteServiceChannelCallbacks = new HashMap();
         initializeNativeDataNative();
     }
-    private native void initializeNativeDataNative();
 
     protected void finalize() throws Throwable {
         try {
@@ -101,20 +98,11 @@
             super.finalize();
         }
     }
-    private native void cleanupNativeDataNative();
 
-    /* pacakge */ HashMap<String, IBluetoothDeviceCallback> getRemoteServiceChannelCallbacks() {
-        return mGetRemoteServiceChannelCallbacks;
-    }
-
-    /* pacakge */ HashMap<String, Integer> getPasskeyAgentRequestData() {
+    /* package */ HashMap<String, Integer> getPasskeyAgentRequestData() {
         return mPasskeyAgentRequestData;
     }
 
-    private native void startEventLoopNative();
-    private native void stopEventLoopNative();
-    private native boolean isEventLoopRunningNative();
-
     /* package */ void start() {
 
         if (!isEventLoopRunningNative()) {
@@ -134,79 +122,39 @@
         return isEventLoopRunningNative();
     }
 
-    /*package*/ void onModeChanged(String bluezMode) {
-        int mode = BluetoothDeviceService.bluezStringToScanMode(bluezMode);
-        if (mode >= 0) {
-            Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION);
-            intent.putExtra(BluetoothIntent.SCAN_MODE, mode);
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+    private void onDeviceFound(String address, String[] properties) {
+        if (properties == null) {
+            Log.e(TAG, "ERROR: Remote device properties are null");
+            return;
+        }
+        mBluetoothService.addRemoteDeviceProperties(address, properties);
+        String rssi = mBluetoothService.getRemoteDeviceProperty(address, "RSSI");
+        String classValue = mBluetoothService.getRemoteDeviceProperty(address, "Class");
+        String name = mBluetoothService.getRemoteDeviceProperty(address, "Name");
+
+        if (rssi != null && classValue != null) {
+            Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION);
+            intent.putExtra(BluetoothIntent.ADDRESS, address);
+            intent.putExtra(BluetoothIntent.CLASS, Integer.valueOf(classValue));
+            intent.putExtra(BluetoothIntent.RSSI, (short)Integer.valueOf(rssi).intValue());
+            intent.putExtra(BluetoothIntent.NAME, name);
+
             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+        } else {
+            log ("RSSI: " + rssi + " or ClassValue: " + classValue +
+                    " for remote device: " + address + " is null");
         }
     }
 
-    private void onDiscoveryStarted() {
-        mBluetoothService.setIsDiscovering(true);
-        Intent intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-    }
-    private void onDiscoveryCompleted() {
-        mBluetoothService.setIsDiscovering(false);
-        Intent intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-    }
+    private void onDeviceDisappeared(String address) {
+        mBluetoothService.removeRemoteDeviceProperties(address);
 
-    private void onRemoteDeviceFound(String address, int deviceClass, short rssi) {
-        Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION);
-        intent.putExtra(BluetoothIntent.ADDRESS, address);
-        intent.putExtra(BluetoothIntent.CLASS, deviceClass);
-        intent.putExtra(BluetoothIntent.RSSI, rssi);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-    }
-    private void onRemoteDeviceDisappeared(String address) {
         Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISAPPEARED_ACTION);
         intent.putExtra(BluetoothIntent.ADDRESS, address);
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
     }
-    private void onRemoteClassUpdated(String address, int deviceClass) {
-        Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION);
-        intent.putExtra(BluetoothIntent.ADDRESS, address);
-        intent.putExtra(BluetoothIntent.CLASS, deviceClass);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-    }
-    private void onRemoteDeviceConnected(String address) {
-        Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
-        intent.putExtra(BluetoothIntent.ADDRESS, address);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-    }
-    private void onRemoteDeviceDisconnectRequested(String address) {
-        Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION);
-        intent.putExtra(BluetoothIntent.ADDRESS, address);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-    }
-    private void onRemoteDeviceDisconnected(String address) {
-        Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION);
-        intent.putExtra(BluetoothIntent.ADDRESS, address);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-    }
-    private void onRemoteNameUpdated(String address, String name) {
-        Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
-        intent.putExtra(BluetoothIntent.ADDRESS, address);
-        intent.putExtra(BluetoothIntent.NAME, name);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-    }
-    private void onRemoteNameFailed(String address) {
-        Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_FAILED_ACTION);
-        intent.putExtra(BluetoothIntent.ADDRESS, address);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-    }
-    private void onRemoteNameChanged(String address, String name) {
-        Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
-        intent.putExtra(BluetoothIntent.ADDRESS, address);
-        intent.putExtra(BluetoothIntent.NAME, name);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-    }
 
-    private void onCreateBondingResult(String address, int result) {
+    private void onCreatePairedDeviceResult(String address, int result) {
         address = address.toUpperCase();
         if (result == BluetoothError.SUCCESS) {
             mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
@@ -259,23 +207,125 @@
         mBluetoothService.getBondState().attempt(address);
     }
 
-    private void onBondingCreated(String address) {
-        mBluetoothService.getBondState().setBondState(address.toUpperCase(),
-                                                      BluetoothDevice.BOND_BONDED);
+    private void onDeviceCreated(String deviceObjectPath) {
+        // do nothing.
+        return;
     }
 
-    private void onBondingRemoved(String address) {
-        mBluetoothService.getBondState().setBondState(address.toUpperCase(),
-                BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED);
+    private void onDeviceRemoved(String deviceObjectPath) {
+        String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+        if (address != null)
+            mBluetoothService.getBondState().setBondState(address.toUpperCase(),
+                    BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED);
     }
 
-    private void onNameChanged(String name) {
-        Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION);
-        intent.putExtra(BluetoothIntent.NAME, name);
-        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+    /*package*/ void onPropertyChanged(String[] propValues) {
+        String name = propValues[0];
+        if (name.equals("Name")) {
+            Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION);
+            intent.putExtra(BluetoothIntent.NAME, propValues[1]);
+            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+            mBluetoothService.setProperty(name, propValues[1]);
+        } else if (name.equals("Pairable") || name.equals("Discoverable")) {
+            String pairable = name.equals("Pairable") ? propValues[1] :
+                mBluetoothService.getProperty("Pairable");
+            String discoverable = name.equals("Discoverable") ? propValues[1] :
+                mBluetoothService.getProperty("Discoverable");
+
+            // This shouldn't happen, unless Adapter Properties are null.
+            if (pairable == null || discoverable == null)
+                return;
+
+            int mode = BluetoothDeviceService.bluezStringToScanMode(
+                    pairable.equals("true"),
+                    discoverable.equals("true"));
+            if (mode >= 0) {
+                Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION);
+                intent.putExtra(BluetoothIntent.SCAN_MODE, mode);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+            }
+            mBluetoothService.setProperty(name, propValues[1]);
+        } else if (name.equals("Discovering")) {
+            Intent intent;
+            if (propValues[1].equals("true")) {
+                mBluetoothService.setIsDiscovering(true);
+                intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION);
+            } else {
+                // Stop the discovery.
+                mBluetoothService.cancelDiscovery();
+                mBluetoothService.setIsDiscovering(false);
+                intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION);
+            }
+            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+            mBluetoothService.setProperty(name, propValues[1]);
+        } else if (name.equals("Devices")) {
+            String value = null;
+            int len = Integer.valueOf(propValues[1]);
+            if (len > 0) {
+                value = "";
+                for (int i = 2; i < propValues.length; i++) {
+                    value = value + propValues[i] + ',';
+                }
+            }
+            mBluetoothService.setProperty(name, value);
+        } else if (name.equals("Powered")) {
+            // bluetoothd has restarted, re-read all our properties.
+            // Note: bluez only sends this property change when it restarts.
+            if (propValues[1].equals("true"))
+                onRestartRequired();
+        }
     }
 
-    private void onPasskeyAgentRequest(String address, int nativeData) {
+    private void onDevicePropertyChanged(String deviceObjectPath, String[] propValues) {
+        String name = propValues[0];
+        String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+        if (address == null) {
+            Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null");
+            return;
+        }
+        if (name.equals("Name")) {
+            Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
+            intent.putExtra(BluetoothIntent.ADDRESS, address);
+            intent.putExtra(BluetoothIntent.NAME, propValues[1]);
+            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
+        } else if (name.equals("Class")) {
+            Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION);
+            intent.putExtra(BluetoothIntent.ADDRESS, address);
+            intent.putExtra(BluetoothIntent.CLASS, propValues[1]);
+            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
+        } else if (name.equals("Connected")) {
+            Intent intent = null;
+            if (propValues[1].equals("true")) {
+                intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
+            } else {
+                intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION);
+            }
+            intent.putExtra(BluetoothIntent.ADDRESS, address);
+            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
+        } else if (name.equals("UUIDs")) {
+            String uuid = null;
+            int len = Integer.valueOf(propValues[1]);
+            if (len > 0) {
+                uuid = "";
+                for (int i = 2; i < propValues.length; i++) {
+                    uuid = uuid + propValues[i] + ",";
+                }
+            }
+            mBluetoothService.setRemoteDeviceProperty(address, name, uuid);
+        }
+
+    }
+
+    private void onRequestPinCode(String objectPath, int nativeData) {
+        String address = mBluetoothService.getAddressFromObjectPath(objectPath);
+        if (address == null) {
+            Log.e(TAG, "Unable to get device address in onRequestPinCode, returning null");
+            return;
+        }
         address = address.toUpperCase();
         mPasskeyAgentRequestData.put(address, new Integer(nativeData));
 
@@ -309,21 +359,21 @@
         Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
         intent.putExtra(BluetoothIntent.ADDRESS, address);
         mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+        return;
     }
 
-    private void onPasskeyAgentCancel(String address) {
-        address = address.toUpperCase();
-        mBluetoothService.cancelPin(address);
-        Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION);
-        intent.putExtra(BluetoothIntent.ADDRESS, address);
-        mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
-        mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
-                                                      BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
-    }
+    private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
+        String address = mBluetoothService.getAddressFromObjectPath(objectPath);
+        if (address == null) {
+            Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
+            return false;
+        }
 
-    private boolean onAuthAgentAuthorize(String address, String service, String uuid) {
         boolean authorized = false;
-        if (mBluetoothService.isEnabled() && service.endsWith("service_audio")) {
+        UUID uuid = UUID.fromString(deviceUuid);
+        if (mBluetoothService.isEnabled() && (BluetoothUuid.isAudioSink(uuid) ||
+                                              BluetoothUuid.isAudioSource(uuid) ||
+                                              BluetoothUuid.isAdvAudioDist(uuid))) {
             BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
             authorized = a2dp.getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF;
             if (authorized) {
@@ -332,30 +382,21 @@
                 Log.i(TAG, "Rejecting incoming A2DP connection from " + address);
             }
         } else {
-            Log.i(TAG, "Rejecting incoming " + service + " connection from " + address);
+            Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
         }
         return authorized;
     }
 
-    private void onAuthAgentCancel(String address, String service, String uuid) {
+    private void onAgentCancel() {
         // We immediately response to DBUS Authorize() so this should not
         // usually happen
-        log("onAuthAgentCancel(" + address + ", " + service + ", " + uuid + ")");
-    }
-
-    private void onGetRemoteServiceChannelResult(String address, int channel) {
-        IBluetoothDeviceCallback callback = mGetRemoteServiceChannelCallbacks.get(address);
-        if (callback != null) {
-            mGetRemoteServiceChannelCallbacks.remove(address);
-            try {
-                callback.onGetRemoteServiceChannelResult(address, channel);
-            } catch (RemoteException e) {}
-        }
+        log("onAgentCancel");
     }
 
     private void onRestartRequired() {
         if (mBluetoothService.isEnabled()) {
-            Log.e(TAG, "*** A serious error occured (did hcid crash?) - restarting Bluetooth ***");
+            Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
+                       "restarting Bluetooth ***");
             mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH);
         }
     }
@@ -363,4 +404,10 @@
     private static void log(String msg) {
         Log.d(TAG, msg);
     }
+
+    private native void initializeNativeDataNative();
+    private native void startEventLoopNative();
+    private native void stopEventLoopNative();
+    private native boolean isEventLoopRunningNative();
+    private native void cleanupNativeDataNative();
 }
diff --git a/core/java/android/syncml/pim/vcard/ContactStruct.java b/core/java/android/syncml/pim/vcard/ContactStruct.java
index ecd719d..4b4c394 100644
--- a/core/java/android/syncml/pim/vcard/ContactStruct.java
+++ b/core/java/android/syncml/pim/vcard/ContactStruct.java
@@ -17,6 +17,7 @@
 package android.syncml.pim.vcard;
 
 import android.content.AbstractSyncableContentProvider;
+import android.content.ContentProviderOperation;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -780,17 +781,16 @@
                 personId = ContentUris.parseId(personUri);
             }
         } else {
-            personUri = provider.nonTransactionalInsert(People.CONTENT_URI, contentValues);
+            personUri = provider.insert(People.CONTENT_URI, contentValues);
             if (personUri != null) {
                 personId = ContentUris.parseId(personUri);
                 ContentValues values = new ContentValues();
                 values.put(GroupMembership.PERSON_ID, personId);
                 values.put(GroupMembership.GROUP_ID, myContactsGroupId);
-                Uri resultUri = provider.nonTransactionalInsert(
-                        GroupMembership.CONTENT_URI, values);
+                Uri resultUri = provider.insert(GroupMembership.CONTENT_URI, values);
                 if (resultUri == null) {
                     Log.e(LOG_TAG, "Faild to insert the person to MyContact.");
-                    provider.nonTransactionalDelete(personUri, null, null);
+                    provider.delete(personUri, null, null);
                     personUri = null;
                 }
             }
@@ -830,7 +830,7 @@
                 if (resolver != null) {
                     phoneUri = resolver.insert(Phones.CONTENT_URI, values);
                 } else {
-                    phoneUri = provider.nonTransactionalInsert(Phones.CONTENT_URI, values);
+                    phoneUri = provider.insert(Phones.CONTENT_URI, values);
                 }
                 if (phoneData.isPrimary) {
                     primaryPhoneId = Long.parseLong(phoneUri.getLastPathSegment());
@@ -856,8 +856,7 @@
                 if (resolver != null) {
                     organizationUri = resolver.insert(Organizations.CONTENT_URI, values);
                 } else {
-                    organizationUri = provider.nonTransactionalInsert(
-                            Organizations.CONTENT_URI, values);
+                    organizationUri = provider.insert(Organizations.CONTENT_URI, values);
                 }
                 if (organizationData.isPrimary) {
                     primaryOrganizationId = Long.parseLong(organizationUri.getLastPathSegment());
@@ -883,8 +882,7 @@
                     if (resolver != null) {
                         emailUri = resolver.insert(ContactMethods.CONTENT_URI, values);
                     } else {
-                        emailUri = provider.nonTransactionalInsert(
-                                ContactMethods.CONTENT_URI, values);
+                        emailUri = provider.insert(ContactMethods.CONTENT_URI, values);
                     }
                     if (contactMethod.isPrimary) {
                         primaryEmailId = Long.parseLong(emailUri.getLastPathSegment());
@@ -893,8 +891,7 @@
                     if (resolver != null) {
                         resolver.insert(ContactMethods.CONTENT_URI, values);
                     } else {
-                        provider.nonTransactionalInsert(
-                                ContactMethods.CONTENT_URI, values);
+                        provider.insert(ContactMethods.CONTENT_URI, values);
                     }
                 }
             }
@@ -918,7 +915,7 @@
                     if (resolver != null) {
                         contentValuesArray.add(values);
                     } else {
-                        provider.nonTransactionalInsert(Extensions.CONTENT_URI, values);
+                        provider.insert(Extensions.CONTENT_URI, values);
                     }
                 }
             }
@@ -942,7 +939,7 @@
             if (resolver != null) {
                 resolver.update(personUri, values, null, null);
             } else {
-                provider.nonTransactionalUpdate(personUri, values, null, null);
+                provider.update(personUri, values, null, null);
             }
         }
     }
@@ -960,12 +957,12 @@
     public void pushIntoAbstractSyncableContentProvider(
             AbstractSyncableContentProvider provider, long myContactsGroupId) {
         boolean successful = false;
-        provider.beginTransaction();
+        provider.beginBatch();
         try {
             pushIntoContentProviderOrResolver(provider, myContactsGroupId);
             successful = true;
         } finally {
-            provider.endTransaction(successful);
+            provider.endBatch(successful);
         }
     }
     
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 1a4eb699..9dd8ceb 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -25,7 +25,9 @@
 
 import java.util.Calendar;
 import java.util.Date;
+import java.util.Formatter;
 import java.util.GregorianCalendar;
+import java.util.Locale;
 import java.util.TimeZone;
 
 /**
@@ -1040,6 +1042,31 @@
 
     /**
      * Formats a date or a time range according to the local conventions.
+     * <p>
+     * Note that this is a convenience method. Using it involves creating an
+     * internal {@link java.util.Formatter} instance on-the-fly, which is
+     * somewhat costly in terms of memory and time. This is probably acceptable
+     * if you use the method only rarely, but if you rely on it for formatting a
+     * large number of dates, consider creating and reusing your own
+     * {@link java.util.Formatter} instance and use the version of
+     * {@link #formatDateRange(Context, long, long, int) formatDateRange}
+     * that takes a {@link java.util.Formatter}.
+     * 
+     * @param context the context is required only if the time is shown
+     * @param startMillis the start time in UTC milliseconds
+     * @param endMillis the end time in UTC milliseconds
+     * @param flags a bit mask of options See
+     * {@link #formatDateRange(Context, long, long, int) formatDateRange}
+     * @return a string containing the formatted date/time range.
+     */
+    public static String formatDateRange(Context context, long startMillis,
+            long endMillis, int flags) {
+        Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault());
+        return formatDateRange(context, f, startMillis, endMillis, flags).toString();
+    }
+
+    /**
+     * Formats a date or a time range according to the local conventions.
      * 
      * <p>
      * Example output strings (date formats in these examples are shown using
@@ -1181,14 +1208,17 @@
      * instead of "December 31, 2008".
      * 
      * @param context the context is required only if the time is shown
+     * @param formatter the Formatter used for formatting the date range.
+     * Note: be sure to call setLength(0) on StringBuilder passed to
+     * the Formatter constructor unless you want the results to accumulate.
      * @param startMillis the start time in UTC milliseconds
      * @param endMillis the end time in UTC milliseconds
      * @param flags a bit mask of options
      *   
-     * @return a string containing the formatted date/time range.
+     * @return the formatter with the formatted date/time range appended to the string buffer.
      */
-    public static String formatDateRange(Context context, long startMillis,
-                long endMillis, int flags) {
+    public static Formatter formatDateRange(Context context, Formatter formatter, long startMillis,
+            long endMillis, int flags) {
         Resources res = Resources.getSystem();
         boolean showTime = (flags & FORMAT_SHOW_TIME) != 0;
         boolean showWeekDay = (flags & FORMAT_SHOW_WEEKDAY) != 0;
@@ -1423,8 +1453,7 @@
 
         if (noMonthDay && startMonthNum == endMonthNum) {
             // Example: "January, 2008"
-            String startDateString = startDate.format(defaultDateFormat);
-            return startDateString;
+            return formatter.format("%s", startDate.format(defaultDateFormat));
         }
 
         if (startYear != endYear || noMonthDay) {
@@ -1436,10 +1465,9 @@
 
             // The values that are used in a fullFormat string are specified
             // by position.
-            dateRange = String.format(fullFormat,
+            return formatter.format(fullFormat,
                     startWeekDayString, startDateString, startTimeString,
                     endWeekDayString, endDateString, endTimeString);
-            return dateRange;
         }
 
         // Get the month, day, and year strings for the start and end dates
@@ -1476,12 +1504,11 @@
 
             // The values that are used in a fullFormat string are specified
             // by position.
-            dateRange = String.format(fullFormat,
+            return formatter.format(fullFormat,
                     startWeekDayString, startMonthString, startMonthDayString,
                     startYearString, startTimeString,
                     endWeekDayString, endMonthString, endMonthDayString,
                     endYearString, endTimeString);
-            return dateRange;
         }
 
         if (startDay != endDay) {
@@ -1496,12 +1523,11 @@
 
             // The values that are used in a fullFormat string are specified
             // by position.
-            dateRange = String.format(fullFormat,
+            return formatter.format(fullFormat,
                     startWeekDayString, startMonthString, startMonthDayString,
                     startYearString, startTimeString,
                     endWeekDayString, endMonthString, endMonthDayString,
                     endYearString, endTimeString);
-            return dateRange;
         }
 
         // Same start and end day
@@ -1522,6 +1548,7 @@
             } else {
                 // Example: "10:00 - 11:00 am"
                 String timeFormat = res.getString(com.android.internal.R.string.time1_time2);
+                // Don't use the user supplied Formatter because the result will pollute the buffer.
                 timeString = String.format(timeFormat, startTimeString, endTimeString);
             }
         }
@@ -1545,7 +1572,7 @@
                     fullFormat = res.getString(com.android.internal.R.string.time_date);
                 } else {
                     // Example: "Oct 9"
-                    return dateString;
+                    return formatter.format("%s", dateString);
                 }
             }
         } else if (showWeekDay) {
@@ -1554,16 +1581,15 @@
                 fullFormat = res.getString(com.android.internal.R.string.time_wday);
             } else {
                 // Example: "Tue"
-                return startWeekDayString;
+                return formatter.format("%s", startWeekDayString);
             }
         } else if (showTime) {
-            return timeString;
+            return formatter.format("%s", timeString);
         }
 
         // The values that are used in a fullFormat string are specified
         // by position.
-        dateRange = String.format(fullFormat, timeString, startWeekDayString, dateString);
-        return dateRange;
+        return formatter.format(fullFormat, timeString, startWeekDayString, dateString);
     }
 
     /**
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 24b4f73..7c87248 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -192,17 +192,21 @@
             return decodeObject();
         }
 
+        public byte[] getRawData() {
+            return mBuffer.array();
+        }
+
         /** @return the loggable item at the current position in mBuffer. */
         private Object decodeObject() {
             if (mBuffer.remaining() < 1) return null;
             switch (mBuffer.get()) {
             case INT:
                 if (mBuffer.remaining() < 4) return null;
-                return mBuffer.getInt();
+                return (Integer) mBuffer.getInt();
 
             case LONG:
                 if (mBuffer.remaining() < 8) return null;
-                return mBuffer.getLong();
+                return (Long) mBuffer.getLong();
 
             case STRING:
                 try {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index a224ed3..0e37b26 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -19,7 +19,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
-import android.util.Config;
 
 /**
  * Object used to report movement (mouse, pen, finger, trackball) events.  This
@@ -87,6 +86,7 @@
 
     private long mDownTime;
     private long mEventTime;
+    private long mEventTimeNano;
     private int mAction;
     private float mX;
     private float mY;
@@ -127,6 +127,62 @@
     /**
      * Create a new MotionEvent, filling in all of the basic values that
      * define the motion.
+     * 
+     * @param downTime The time (in ms) when the user originally pressed down to start 
+     * a stream of position events.  This must be obtained from {@link SystemClock#uptimeMillis()}.
+     * @param eventTime  The the time (in ms) when this specific event was generated.  This 
+     * must be obtained from {@link SystemClock#uptimeMillis()}.
+     * @param eventTimeNano  The the time (in ns) when this specific event was generated.  This 
+     * must be obtained from {@link System#nanoTime()}.
+     * @param action The kind of action being performed -- one of either
+     * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or
+     * {@link #ACTION_CANCEL}.
+     * @param x The X coordinate of this event.
+     * @param y The Y coordinate of this event.
+     * @param pressure The current pressure of this event.  The pressure generally 
+     * ranges from 0 (no pressure at all) to 1 (normal pressure), however 
+     * values higher than 1 may be generated depending on the calibration of 
+     * the input device.
+     * @param size A scaled value of the approximate size of the area being pressed when
+     * touched with the finger. The actual value in pixels corresponding to the finger 
+     * touch is normalized with a device specific range of values
+     * and scaled to a value between 0 and 1.
+     * @param metaState The state of any meta / modifier keys that were in effect when
+     * the event was generated.
+     * @param xPrecision The precision of the X coordinate being reported.
+     * @param yPrecision The precision of the Y coordinate being reported.
+     * @param deviceId The id for the device that this event came from.  An id of
+     * zero indicates that the event didn't come from a physical device; other
+     * numbers are arbitrary and you shouldn't depend on the values.
+     * @param edgeFlags A bitfield indicating which edges, if any, where touched by this
+     * MotionEvent.
+     *
+     * @hide
+     */
+    static public MotionEvent obtainNano(long downTime, long eventTime, long eventTimeNano,
+            int action, float x, float y, float pressure, float size, int metaState,
+            float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
+        MotionEvent ev = obtain();
+        ev.mDeviceId = deviceId;
+        ev.mEdgeFlags = edgeFlags;
+        ev.mDownTime = downTime;
+        ev.mEventTime = eventTime;
+        ev.mEventTimeNano = eventTimeNano;
+        ev.mAction = action;
+        ev.mX = ev.mRawX = x;
+        ev.mY = ev.mRawY = y;
+        ev.mPressure = pressure;
+        ev.mSize = size;
+        ev.mMetaState = metaState;
+        ev.mXPrecision = xPrecision;
+        ev.mYPrecision = yPrecision;
+
+        return ev;
+    }
+    
+    /**
+     * Create a new MotionEvent, filling in all of the basic values that
+     * define the motion.
      *
      * @param downTime The time (in ms) when the user originally pressed down to start
      * a stream of position events.  This must be obtained from {@link SystemClock#uptimeMillis()}.
@@ -163,6 +219,7 @@
         ev.mEdgeFlags = edgeFlags;
         ev.mDownTime = downTime;
         ev.mEventTime = eventTime;
+        ev.mEventTimeNano = eventTime * 1000000;
         ev.mAction = action;
         ev.mX = ev.mRawX = x;
         ev.mY = ev.mRawY = y;
@@ -199,6 +256,7 @@
         ev.mEdgeFlags = 0;
         ev.mDownTime = downTime;
         ev.mEventTime = eventTime;
+        ev.mEventTimeNano = eventTime * 1000000;
         ev.mAction = action;
         ev.mX = ev.mRawX = x;
         ev.mY = ev.mRawY = y;
@@ -266,6 +324,7 @@
         ev.mEdgeFlags = o.mEdgeFlags;
         ev.mDownTime = o.mDownTime;
         ev.mEventTime = o.mEventTime;
+        ev.mEventTimeNano = o.mEventTimeNano;
         ev.mAction = o.mAction;
         ev.mX = o.mX;
         ev.mRawX = o.mRawX;
@@ -337,8 +396,26 @@
     }
 
     /**
+<<<<<<< HEAD:core/java/android/view/MotionEvent.java
+     * Returns the time (in ns) when this specific event was generated.
+     * The value is in nanosecond precision but it may not have nanosecond accuracy.
+     *
+     * @hide
+     */
+    public final long getEventTimeNano() {
+        return mEventTimeNano;
+    }
+
+    /**
+     * Returns the X coordinate of this event.  Whole numbers are pixels; the 
+     * value may have a fraction for input devices that are sub-pixel precise. 
+|||||||
+     * Returns the X coordinate of this event.  Whole numbers are pixels; the 
+     * value may have a fraction for input devices that are sub-pixel precise. 
+=======
      * Returns the X coordinate of this event.  Whole numbers are pixels; the
      * value may have a fraction for input devices that are sub-pixel precise.
+>>>>>>> cafdea61a85c8f5d0646cc9413a09346c637f43f:core/java/android/view/MotionEvent.java
      */
     public final float getX() {
         return mX;
@@ -664,6 +741,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeLong(mDownTime);
         out.writeLong(mEventTime);
+        out.writeLong(mEventTimeNano);
         out.writeInt(mAction);
         out.writeFloat(mX);
         out.writeFloat(mY);
@@ -695,6 +773,7 @@
     private void readFromParcel(Parcel in) {
         mDownTime = in.readLong();
         mEventTime = in.readLong();
+        mEventTimeNano = in.readLong();
         mAction = in.readInt();
         mX = in.readFloat();
         mY = in.readFloat();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 45b0f0a7..1e2bc1f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -32,8 +32,9 @@
 import android.util.AttributeSet;
 import android.util.Config;
 import android.util.Log;
-import java.util.ArrayList;
 
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 import java.util.concurrent.locks.ReentrantLock;
 import java.lang.ref.WeakReference;
 
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 65457c5..a12b14a 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -79,6 +79,9 @@
     private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
     private static final boolean WATCH_POINTER = false;
 
+    private static final boolean MEASURE_LATENCY = false;
+    private static LatencyTimer lt;
+
     /**
      * Maximum time we allow the user to roll the trackball enough to generate
      * a key event, before resetting the counters.
@@ -191,6 +194,10 @@
     public ViewRoot(Context context) {
         super();
 
+        if (MEASURE_LATENCY && lt == null) {
+            lt = new LatencyTimer(100, 1000);
+        }
+
         ++sInstanceCount;
 
         // Initialize the statics when this class is first instantiated. This is
@@ -1596,7 +1603,17 @@
             boolean didFinish;
             if (event == null) {
                 try {
+                    long timeBeforeGettingEvents;
+                    if (MEASURE_LATENCY) {
+                        timeBeforeGettingEvents = System.nanoTime();
+                    }
+
                     event = sWindowSession.getPendingPointerMove(mWindow);
+
+                    if (MEASURE_LATENCY && event != null) {
+                        lt.sample("9 Client got events      ", System.nanoTime() - event.getEventTimeNano());
+                        lt.sample("8 Client getting events  ", timeBeforeGettingEvents - event.getEventTimeNano());
+                    }
                 } catch (RemoteException e) {
                 }
                 didFinish = true;
@@ -1619,7 +1636,13 @@
                         captureMotionLog("captureDispatchPointer", event);
                     }
                     event.offsetLocation(0, mCurScrollY);
+                    if (MEASURE_LATENCY) {
+                        lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
+                    }
                     handled = mView.dispatchTouchEvent(event);
+                    if (MEASURE_LATENCY) {
+                        lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
+                    }
                     if (!handled && isDown) {
                         int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
 
@@ -2710,7 +2733,11 @@
 
         public void dispatchPointer(MotionEvent event, long eventTime) {
             final ViewRoot viewRoot = mViewRoot.get();
-            if (viewRoot != null) {
+            if (viewRoot != null) {                
+                if (MEASURE_LATENCY) {
+                    // Note: eventTime is in milliseconds
+                    ViewRoot.lt.sample("* ViewRoot b4 dispatchPtr", System.nanoTime() - eventTime * 1000000);
+                }
                 viewRoot.dispatchPointer(event, eventTime);
             } else {
                 new EventCompletion(mMainLooper, this, null, true, event);
diff --git a/core/java/android/view/ViewStub.java b/core/java/android/view/ViewStub.java
index e159de4..703a38f 100644
--- a/core/java/android/view/ViewStub.java
+++ b/core/java/android/view/ViewStub.java
@@ -23,6 +23,8 @@
 
 import com.android.internal.R;
 
+import java.lang.ref.WeakReference;
+
 /**
  * A ViewStub is an invisible, zero-sized View that can be used to lazily inflate
  * layout resources at runtime.
@@ -68,6 +70,8 @@
     private int mLayoutResource = 0;
     private int mInflatedId;
 
+    private WeakReference<View> mInflatedViewRef;
+
     private OnInflateListener mInflateListener;
 
     public ViewStub(Context context) {
@@ -196,9 +200,15 @@
      */
     @Override
     public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-
-        if (visibility == VISIBLE || visibility == INVISIBLE) {
+        if (mInflatedViewRef != null) {
+            View view = mInflatedViewRef.get();
+            if (view != null) {
+                view.setVisibility(visibility);
+            } else {
+                throw new IllegalStateException("setVisibility called on un-referenced view");
+            }
+        } else if (visibility == VISIBLE || visibility == INVISIBLE) {
+            super.setVisibility(visibility);
             inflate();
         }
     }
@@ -234,6 +244,8 @@
                     parent.addView(view, index);
                 }
 
+                mInflatedViewRef = new WeakReference(view);
+
                 if (mInflateListener != null) {
                     mInflateListener.onInflate(this, view);
                 }
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index d7457a0..2c32d8b 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -56,11 +56,11 @@
     public static final int FEATURE_CONTEXT_MENU = 6;
     /** Flag for custom title. You cannot combine this feature with other title features. */
     public static final int FEATURE_CUSTOM_TITLE = 7;
-    /*  Flag for asking for an OpenGL enabled window.
+    /** Flag for asking for an OpenGL enabled window.
         All 2D graphics will be handled by OpenGL ES.
-        Private for now, until it is better tested (not shipping in 1.0)
+        @hide
     */
-    private static final int FEATURE_OPENGL = 8;
+    public static final int FEATURE_OPENGL = 8;
     /** Flag for setting the progress bar's visibility to VISIBLE */
     public static final int PROGRESS_VISIBILITY_ON = -1;
     /** Flag for setting the progress bar's visibility to GONE */
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index dbd2682..e04ae72 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -109,6 +109,8 @@
             CacheManager.init(context);
             // create CookieSyncManager with current Context
             CookieSyncManager.createInstance(context);
+            // create PluginManager with current Context
+            PluginManager.getInstance(context);
         }
         AssetManager am = context.getAssets();
         nativeCreateFrame(w, am, proxy.getBackForwardList());
@@ -119,7 +121,7 @@
         mDatabase = WebViewDatabase.getInstance(context);
         mWebViewCore = w;
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.BROWSER_FRAME) {
             Log.v(LOGTAG, "BrowserFrame constructor: this=" + this);
         }
     }
@@ -341,7 +343,7 @@
         switch (msg.what) {
             case FRAME_COMPLETED: {
                 if (mSettings.getSavePassword() && hasPasswordField()) {
-                    if (WebView.DEBUG) {
+                    if (DebugFlags.BROWSER_FRAME) {
                         Assert.assertNotNull(mCallbackProxy.getBackForwardList()
                                 .getCurrentItem());
                     }
@@ -490,7 +492,7 @@
             }
             if (mSettings.getSavePassword() && hasPasswordField()) {
                 try {
-                    if (WebView.DEBUG) {
+                    if (DebugFlags.BROWSER_FRAME) {
                         Assert.assertNotNull(mCallbackProxy.getBackForwardList()
                                 .getCurrentItem());
                     }
@@ -538,7 +540,7 @@
         // is this resource the main-frame top-level page?
         boolean isMainFramePage = mIsMainFrame;
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.BROWSER_FRAME) {
             Log.v(LOGTAG, "startLoadingResource: url=" + url + ", method="
                     + method + ", postData=" + postData + ", isHighPriority="
                     + isHighPriority + ", isMainFramePage=" + isMainFramePage);
diff --git a/core/java/android/webkit/CacheLoader.java b/core/java/android/webkit/CacheLoader.java
index 3e1b602..de8f888 100644
--- a/core/java/android/webkit/CacheLoader.java
+++ b/core/java/android/webkit/CacheLoader.java
@@ -17,6 +17,7 @@
 package android.webkit;
 
 import android.net.http.Headers;
+import android.text.TextUtils;
 
 /**
  * This class is a concrete implementation of StreamLoader that uses a
@@ -49,17 +50,22 @@
     @Override
     protected void buildHeaders(Headers headers) {
         StringBuilder sb = new StringBuilder(mCacheResult.mimeType);
-        if (mCacheResult.encoding != null &&
-                mCacheResult.encoding.length() > 0) {
+        if (!TextUtils.isEmpty(mCacheResult.encoding)) {
             sb.append(';');
             sb.append(mCacheResult.encoding);
         }
         headers.setContentType(sb.toString());
 
-        if (mCacheResult.location != null &&
-                mCacheResult.location.length() > 0) {
+        if (!TextUtils.isEmpty(mCacheResult.location)) {
             headers.setLocation(mCacheResult.location);
         }
-    }
 
+        if (!TextUtils.isEmpty(mCacheResult.expiresString)) {
+            headers.setExpires(mCacheResult.expiresString);
+        }
+
+        if (!TextUtils.isEmpty(mCacheResult.contentdisposition)) {
+            headers.setContentDisposition(mCacheResult.contentdisposition);
+        }
+    }
 }
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 7897435..9a02fbe 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -51,7 +51,6 @@
 
     private static final String NO_STORE = "no-store";
     private static final String NO_CACHE = "no-cache";
-    private static final String PRIVATE  = "private";
     private static final String MAX_AGE = "max-age";
 
     private static long CACHE_THRESHOLD = 6 * 1024 * 1024;
@@ -80,12 +79,14 @@
         int httpStatusCode;
         long contentLength;
         long expires;
+        String expiresString;
         String localPath;
         String lastModified;
         String etag;
         String mimeType;
         String location;
         String encoding;
+        String contentdisposition;
 
         // these fields are NOT saved to the database
         InputStream inStream;
@@ -108,6 +109,13 @@
             return expires;
         }
 
+        /**
+         * @hide Pending API council approval
+         */
+        public String getExpiresString() {
+            return expiresString;
+        }
+
         public String getLastModified() {
             return lastModified;
         }
@@ -128,6 +136,13 @@
             return encoding;
         }
 
+        /**
+         * @hide Pending API council approval
+         */
+        public String getContentDisposition() {
+            return contentdisposition;
+        }
+
         // For out-of-package access to the underlying streams.
         public InputStream getInputStream() {
             return inStream;
@@ -321,7 +336,7 @@
             }
         }
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.CACHE_MANAGER) {
             Log.v(LOGTAG, "getCacheFile for url " + url);
         }
 
@@ -340,7 +355,7 @@
      * @hide - hide createCacheFile since it has a parameter of type headers, which is
      * in a hidden package.
      */
-    // can be called from any thread
+    // only called from WebCore thread
     public static CacheResult createCacheFile(String url, int statusCode,
             Headers headers, String mimeType, boolean forceCache) {
         if (!forceCache && mDisabled) {
@@ -349,17 +364,25 @@
 
         // according to the rfc 2616, the 303 response MUST NOT be cached.
         if (statusCode == 303) {
+            // remove the saved cache if there is any
+            mDataBase.removeCache(url);
             return null;
         }
 
         // like the other browsers, do not cache redirects containing a cookie
         // header.
         if (checkCacheRedirect(statusCode) && !headers.getSetCookie().isEmpty()) {
+            // remove the saved cache if there is any
+            mDataBase.removeCache(url);
             return null;
         }
 
         CacheResult ret = parseHeaders(statusCode, headers, mimeType);
-        if (ret != null) {
+        if (ret == null) {
+            // this should only happen if the headers has "no-store" in the
+            // cache-control. remove the saved cache if there is any
+            mDataBase.removeCache(url);
+        } else {
             setupFiles(url, ret);
             try {
                 ret.outStream = new FileOutputStream(ret.outFile);
@@ -415,7 +438,7 @@
 
         mDataBase.addCache(url, cacheRet);
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.CACHE_MANAGER) {
             Log.v(LOGTAG, "saveCacheFile for url " + url);
         }
     }
@@ -596,21 +619,27 @@
         if (location != null) ret.location = location;
 
         ret.expires = -1;
-        String expires = headers.getExpires();
-        if (expires != null) {
+        ret.expiresString = headers.getExpires();
+        if (ret.expiresString != null) {
             try {
-                ret.expires = HttpDateTime.parse(expires);
+                ret.expires = HttpDateTime.parse(ret.expiresString);
             } catch (IllegalArgumentException ex) {
                 // Take care of the special "-1" and "0" cases
-                if ("-1".equals(expires) || "0".equals(expires)) {
+                if ("-1".equals(ret.expiresString)
+                        || "0".equals(ret.expiresString)) {
                     // make it expired, but can be used for history navigation
                     ret.expires = 0;
                 } else {
-                    Log.e(LOGTAG, "illegal expires: " + expires);
+                    Log.e(LOGTAG, "illegal expires: " + ret.expiresString);
                 }
             }
         }
 
+        String contentDisposition = headers.getContentDisposition();
+        if (contentDisposition != null) {
+            ret.contentdisposition = contentDisposition;
+        }
+
         String lastModified = headers.getLastModified();
         if (lastModified != null) ret.lastModified = lastModified;
 
@@ -628,7 +657,7 @@
                 // must be re-validated on every load. It does not mean that
                 // the content can not be cached. set to expire 0 means it
                 // can only be used in CACHE_MODE_CACHE_ONLY case
-                if (NO_CACHE.equals(controls[i]) || PRIVATE.equals(controls[i])) {
+                if (NO_CACHE.equals(controls[i])) {
                     ret.expires = 0;
                 } else if (controls[i].startsWith(MAX_AGE)) {
                     int separator = controls[i].indexOf('=');
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 17d3f94..9a8c3c0 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -72,36 +72,38 @@
     private final Context mContext;
 
     // Message Ids
-    private static final int PAGE_STARTED         = 100;
-    private static final int RECEIVED_ICON        = 101;
-    private static final int RECEIVED_TITLE       = 102;
-    private static final int OVERRIDE_URL         = 103;
-    private static final int AUTH_REQUEST         = 104;
-    private static final int SSL_ERROR            = 105;
-    private static final int PROGRESS             = 106;
-    private static final int UPDATE_VISITED       = 107;
-    private static final int LOAD_RESOURCE        = 108;
-    private static final int CREATE_WINDOW        = 109;
-    private static final int CLOSE_WINDOW         = 110;
-    private static final int SAVE_PASSWORD        = 111;
-    private static final int JS_ALERT             = 112;
-    private static final int JS_CONFIRM           = 113;
-    private static final int JS_PROMPT            = 114;
-    private static final int JS_UNLOAD            = 115;
-    private static final int ASYNC_KEYEVENTS      = 116;
-    private static final int TOO_MANY_REDIRECTS   = 117;
-    private static final int DOWNLOAD_FILE        = 118;
-    private static final int REPORT_ERROR         = 119;
-    private static final int RESEND_POST_DATA     = 120;
-    private static final int PAGE_FINISHED        = 121;
-    private static final int REQUEST_FOCUS        = 122;
-    private static final int SCALE_CHANGED        = 123;
-    private static final int RECEIVED_CERTIFICATE = 124;
-    private static final int SWITCH_OUT_HISTORY   = 125;
-    private static final int JS_TIMEOUT           = 126;
+    private static final int PAGE_STARTED              = 100;
+    private static final int RECEIVED_ICON             = 101;
+    private static final int RECEIVED_TITLE            = 102;
+    private static final int OVERRIDE_URL              = 103;
+    private static final int AUTH_REQUEST              = 104;
+    private static final int SSL_ERROR                 = 105;
+    private static final int PROGRESS                  = 106;
+    private static final int UPDATE_VISITED            = 107;
+    private static final int LOAD_RESOURCE             = 108;
+    private static final int CREATE_WINDOW             = 109;
+    private static final int CLOSE_WINDOW              = 110;
+    private static final int SAVE_PASSWORD             = 111;
+    private static final int JS_ALERT                  = 112;
+    private static final int JS_CONFIRM                = 113;
+    private static final int JS_PROMPT                 = 114;
+    private static final int JS_UNLOAD                 = 115;
+    private static final int ASYNC_KEYEVENTS           = 116;
+    private static final int TOO_MANY_REDIRECTS        = 117;
+    private static final int DOWNLOAD_FILE             = 118;
+    private static final int REPORT_ERROR              = 119;
+    private static final int RESEND_POST_DATA          = 120;
+    private static final int PAGE_FINISHED             = 121;
+    private static final int REQUEST_FOCUS             = 122;
+    private static final int SCALE_CHANGED             = 123;
+    private static final int RECEIVED_CERTIFICATE      = 124;
+    private static final int SWITCH_OUT_HISTORY        = 125;
+    private static final int EXCEEDED_DATABASE_QUOTA   = 126;
+    private static final int JS_TIMEOUT                = 127;
+    private static final int ADD_MESSAGE_TO_CONSOLE    = 128;
 
     // Message triggered by the client to resume execution
-    private static final int NOTIFY               = 200;
+    private static final int NOTIFY                    = 200;
 
     // Result transportation object for returning results across thread
     // boundaries.
@@ -145,6 +147,16 @@
     }
 
     /**
+     * Get the WebChromeClient.
+     * @return the current WebChromeClient instance.
+     *
+     *@hide pending API council approval.
+     */
+    public WebChromeClient getWebChromeClient() {
+       return mWebChromeClient;
+    }
+
+    /**
      * Set the client DownloadListener.
      * @param client An implementation of DownloadListener.
      */
@@ -389,6 +401,23 @@
                 }
                 break;
 
+            case EXCEEDED_DATABASE_QUOTA:
+                if (mWebChromeClient != null) {
+                    HashMap<String, Object> map =
+                            (HashMap<String, Object>) msg.obj;
+                    String databaseIdentifier =
+                            (String) map.get("databaseIdentifier");
+                    String url = (String) map.get("url");
+                    long currentQuota =
+                            ((Long) map.get("currentQuota")).longValue();
+                    WebStorage.QuotaUpdater quotaUpdater =
+                        (WebStorage.QuotaUpdater) map.get("quotaUpdater");
+
+                    mWebChromeClient.onExceededDatabaseQuota(url,
+                            databaseIdentifier, currentQuota, quotaUpdater);
+                }
+                break;
+
             case JS_ALERT:
                 if (mWebChromeClient != null) {
                     final JsResult res = (JsResult) msg.obj;
@@ -563,6 +592,13 @@
             case SWITCH_OUT_HISTORY:
                 mWebView.switchOutDrawHistory();
                 break;
+
+            case ADD_MESSAGE_TO_CONSOLE:
+                String message = msg.getData().getString("message");
+                String sourceID = msg.getData().getString("sourceID");
+                int lineNumber = msg.getData().getInt("lineNumber");
+                mWebChromeClient.addMessageToConsole(message, lineNumber, sourceID);
+                break;
         }
     }
 
@@ -834,7 +870,7 @@
             String password, Message resumeMsg) {
         // resumeMsg should be null at this point because we want to create it
         // within the CallbackProxy.
-        if (WebView.DEBUG) {
+        if (DebugFlags.CALLBACK_PROXY) {
             junit.framework.Assert.assertNull(resumeMsg);
         }
         resumeMsg = obtainMessage(NOTIFY);
@@ -1037,6 +1073,60 @@
     }
 
     /**
+     * Called by WebViewCore to inform the Java side that the current origin
+     * has overflowed it's database quota. Called in the WebCore thread so
+     * posts a message to the UI thread that will prompt the WebChromeClient
+     * for what to do. On return back to C++ side, the WebCore thread will
+     * sleep pending a new quota value.
+     * @param url The URL that caused the quota overflow.
+     * @param databaseIdentifier The identifier of the database that the
+     *     transaction that caused the overflow was running on.
+     * @param currentQuota The current quota the origin is allowed.
+     * @param quotaUpdater An instance of a class encapsulating a callback
+     *     to WebViewCore to run when the decision to allow or deny more
+     *     quota has been made.
+     */
+    public void onExceededDatabaseQuota(
+            String url, String databaseIdentifier, long currentQuota,
+            WebStorage.QuotaUpdater quotaUpdater) {
+        if (mWebChromeClient == null) {
+            quotaUpdater.updateQuota(currentQuota);
+            return;
+        }
+
+        Message exceededQuota = obtainMessage(EXCEEDED_DATABASE_QUOTA);
+        HashMap<String, Object> map = new HashMap();
+        map.put("databaseIdentifier", databaseIdentifier);
+        map.put("url", url);
+        map.put("currentQuota", currentQuota);
+        map.put("quotaUpdater", quotaUpdater);
+        exceededQuota.obj = map;
+        sendMessage(exceededQuota);
+    }
+
+    /**
+     * Called by WebViewCore when we have a message to be added to the JavaScript
+     * error console. Sends a message to the Java side with the details.
+     * @param message The message to add to the console.
+     * @param lineNumber The lineNumber of the source file on which the error
+     *     occurred.
+     * @param sourceID The filename of the source file in which the error
+     *     occurred.
+     * @hide pending API counsel.
+     */
+    public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+        if (mWebChromeClient == null) {
+            return;
+        }
+
+        Message msg = obtainMessage(ADD_MESSAGE_TO_CONSOLE);
+        msg.getData().putString("message", message);
+        msg.getData().putString("sourceID", sourceID);
+        msg.getData().putInt("lineNumber", lineNumber);
+        sendMessage(msg);
+    }
+
+    /**
      * @hide pending API council approval
      */
     public boolean onJsTimeout() {
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index e8c2279..7b91724 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -262,7 +262,7 @@
         if (!mAcceptCookie || uri == null) {
             return;
         }
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.COOKIE_MANAGER) {
             Log.v(LOGTAG, "setCookie: uri: " + uri + " value: " + value);
         }
 
@@ -427,12 +427,12 @@
             }
         }
         if (ret.length() > 0) {
-            if (WebView.LOGV_ENABLED) {
+            if (DebugFlags.COOKIE_MANAGER) {
                 Log.v(LOGTAG, "getCookie: uri: " + uri + " value: " + ret);
             }
             return ret.toString();
         } else {
-            if (WebView.LOGV_ENABLED) {
+            if (DebugFlags.COOKIE_MANAGER) {
                 Log.v(LOGTAG, "getCookie: uri: " + uri
                         + " But can't find cookie.");
             }
@@ -588,7 +588,7 @@
             Iterator<ArrayList<Cookie>> listIter = cookieLists.iterator();
             while (listIter.hasNext() && count < MAX_RAM_COOKIES_COUNT) {
                 ArrayList<Cookie> list = listIter.next();
-                if (WebView.DEBUG) {
+                if (DebugFlags.COOKIE_MANAGER) {
                     Iterator<Cookie> iter = list.iterator();
                     while (iter.hasNext() && count < MAX_RAM_COOKIES_COUNT) {
                         Cookie cookie = iter.next();
@@ -608,7 +608,7 @@
 
         ArrayList<Cookie> retlist = new ArrayList<Cookie>();
         if (mapSize >= MAX_RAM_DOMAIN_COUNT || count >= MAX_RAM_COOKIES_COUNT) {
-            if (WebView.DEBUG) {
+            if (DebugFlags.COOKIE_MANAGER) {
                 Log.v(LOGTAG, count + " cookies used " + byteCount
                         + " bytes with " + mapSize + " domains");
             }
@@ -616,7 +616,7 @@
             int toGo = mapSize / 10 + 1;
             while (toGo-- > 0){
                 String domain = domains[toGo].toString();
-                if (WebView.LOGV_ENABLED) {
+                if (DebugFlags.COOKIE_MANAGER) {
                     Log.v(LOGTAG, "delete domain: " + domain
                             + " from RAM cache");
                 }
diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java
index 8d66529..14375d2 100644
--- a/core/java/android/webkit/CookieSyncManager.java
+++ b/core/java/android/webkit/CookieSyncManager.java
@@ -24,30 +24,39 @@
 import java.util.Iterator;
 
 /**
- * The class CookieSyncManager is used to synchronize the browser cookies
- * between RAM and FLASH. To get the best performance, browser cookie is saved
- * in RAM. We use a separate thread to sync the cookies between RAM and FLASH on
- * a timer base.
+ * The CookieSyncManager is used to synchronize the browser cookie store
+ * between RAM and permanent storage. To get the best performance, browser cookies are
+ * saved in RAM. A separate thread saves the cookies between, driven by a timer.
  * <p>
+ *
  * To use the CookieSyncManager, the host application has to call the following
- * when the application starts.
+ * when the application starts:
  * <p>
- * CookieSyncManager.createInstance(context)
+ *
+ * <pre class="prettyprint">CookieSyncManager.createInstance(context)</pre><p>
+ *
+ * To set up for sync, the host application has to call<p>
+ * <pre class="prettyprint">CookieSyncManager.getInstance().startSync()</pre><p>
+ *
+ * in Activity.onResume(), and call
  * <p>
- * To set up for sync, the host application has to call
- * <p>
- * CookieSyncManager.getInstance().startSync()
- * <p>
- * in its Activity.onResume(), and call
- * <p>
+ *
+ * <pre class="prettyprint">
  * CookieSyncManager.getInstance().stopSync()
- * <p>
- * in its Activity.onStop().
- * <p>
+ * </pre><p>
+ *
+ * in Activity.onPause().<p>
+ *
  * To get instant sync instead of waiting for the timer to trigger, the host can
  * call
  * <p>
- * CookieSyncManager.getInstance().sync()
+ * <pre class="prettyprint">CookieSyncManager.getInstance().sync()</pre><p>
+ *
+ * The sync interval is 5 minutes, so you will want to force syncs
+ * manually anyway, for instance in {@link
+ * WebViewClient#onPageFinished}. Note that even sync() happens
+ * asynchronously, so don't do it just as your activity is shutting
+ * down.
  */
 public final class CookieSyncManager extends WebSyncManager {
 
@@ -90,7 +99,7 @@
     }
 
     /**
-     * Package level api, called from CookieManager Get all the cookies which
+     * Package level api, called from CookieManager. Get all the cookies which
      * matches a given base domain.
      * @param domain
      * @return A list of Cookie
@@ -161,7 +170,7 @@
     }
 
     protected void syncFromRamToFlash() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.COOKIE_SYNC_MANAGER) {
             Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash STARTS");
         }
 
@@ -178,7 +187,7 @@
                 CookieManager.getInstance().deleteLRUDomain();
         syncFromRamToFlash(lruList);
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.COOKIE_SYNC_MANAGER) {
             Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash DONE");
         }
     }
diff --git a/core/java/android/webkit/DataLoader.java b/core/java/android/webkit/DataLoader.java
index dcdc949..6c5d10d 100644
--- a/core/java/android/webkit/DataLoader.java
+++ b/core/java/android/webkit/DataLoader.java
@@ -16,12 +16,10 @@
 
 package android.webkit;
 
-import org.apache.http.protocol.HTTP;
-
-import android.net.http.Headers;
-
 import java.io.ByteArrayInputStream;
 
+import org.apache.harmony.luni.util.Base64;
+
 /**
  * This class is a concrete implementation of StreamLoader that uses the
  * content supplied as a URL as the source for the stream. The mimetype
@@ -30,8 +28,6 @@
  */
 class DataLoader extends StreamLoader {
 
-    private String mContentType;  // Content mimetype, if supplied in URL
-
     /**
      * Constructor uses the dataURL as the source for an InputStream
      * @param dataUrl data: URL string optionally containing a mimetype
@@ -41,16 +37,20 @@
         super(loadListener);
 
         String url = dataUrl.substring("data:".length());
-        String content;
+        byte[] data = null;
         int commaIndex = url.indexOf(',');
         if (commaIndex != -1) {
-            mContentType = url.substring(0, commaIndex);
-            content = url.substring(commaIndex + 1);
+            String contentType = url.substring(0, commaIndex);
+            data = url.substring(commaIndex + 1).getBytes();
+            loadListener.parseContentTypeHeader(contentType);
+            if ("base64".equals(loadListener.transferEncoding())) {
+                data = Base64.decode(data);
+            }
         } else {
-            content = url;
+            data = url.getBytes();
         }
-        mDataStream = new ByteArrayInputStream(content.getBytes());
-        mContentLength = content.length();
+        mDataStream = new ByteArrayInputStream(data);
+        mContentLength = data.length;
     }
 
     @Override
@@ -60,10 +60,7 @@
     }
 
     @Override
-    protected void buildHeaders(Headers headers) {
-        if (mContentType != null) {
-            headers.setContentType(mContentType);
-        }
+    protected void buildHeaders(android.net.http.Headers h) {
     }
 
     /**
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
new file mode 100644
index 0000000..8e25395
--- /dev/null
+++ b/core/java/android/webkit/DebugFlags.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+/**
+ * This class is a container for all of the debug flags used in the Java
+ * components of webkit.  These flags must be final in order to ensure that
+ * the compiler optimizes the code that uses them out of the final executable.
+ *
+ * The name of each flags maps directly to the name of the class in which that
+ * flag is used.
+ *
+ */
+class DebugFlags {
+
+    public static final boolean BROWSER_FRAME = false;
+    public static final boolean CACHE_MANAGER = false;
+    public static final boolean CALLBACK_PROXY = false;
+    public static final boolean COOKIE_MANAGER = false;
+    public static final boolean COOKIE_SYNC_MANAGER = false;
+    public static final boolean FRAME_LOADER = false;
+    public static final boolean J_WEB_CORE_JAVA_BRIDGE = false;// HIGHLY VERBOSE
+    public static final boolean LOAD_LISTENER = false;
+    public static final boolean NETWORK = false;
+    public static final boolean SSL_ERROR_HANDLER = false;
+    public static final boolean STREAM_LOADER = false;
+    public static final boolean URL_UTIL = false;
+    public static final boolean WEB_BACK_FORWARD_LIST = false;
+    public static final boolean WEB_SETTINGS = false;
+    public static final boolean WEB_SYNC_MANAGER = false;
+    public static final boolean WEB_TEXT_VIEW = false;
+    public static final boolean WEB_VIEW = false;
+    public static final boolean WEB_VIEW_CORE = false;
+
+}
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 66ab021..f98c5d3 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -120,7 +120,7 @@
         } else if (handleLocalFile(url, mListener, mSettings)) {
             return true;
         }
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.FRAME_LOADER) {
             Log.v(LOGTAG, "FrameLoader.executeLoad: url protocol not supported:"
                     + mListener.url());
         }
@@ -180,7 +180,7 @@
             return true;
         }
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.FRAME_LOADER) {
             Log.v(LOGTAG, "FrameLoader: http " + mMethod + " load for: "
                     + mListener.url());
         }
@@ -211,7 +211,7 @@
      * setup a load from the byte stream in a CacheResult.
      */
     private void startCacheLoad(CacheResult result) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.FRAME_LOADER) {
             Log.v(LOGTAG, "FrameLoader: loading from cache: "
                   + mListener.url());
         }
@@ -285,7 +285,7 @@
             // of it's state. If it is not in the cache, then go to the 
             // network.
             case WebSettings.LOAD_CACHE_ELSE_NETWORK: {
-                if (WebView.LOGV_ENABLED) {
+                if (DebugFlags.FRAME_LOADER) {
                     Log.v(LOGTAG, "FrameLoader: checking cache: "
                             + mListener.url());
                 }
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
new file mode 100644
index 0000000..973cb010
--- /dev/null
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.widget.MediaController;
+import android.widget.VideoView;
+
+import java.util.HashMap;
+
+/**
+ * <p>A View that displays Videos. Instances of this class
+ * are created on the WebCore thread. However, their code
+ * executes on the UI thread. Right now there is only one
+ * such view for fullscreen video rendering.
+ *
+ */
+class HTML5VideoViewProxy extends Handler {
+    // Logging tag.
+    private static final String LOGTAG = "HTML5VideoViewProxy";
+
+    // Message Ids
+    private static final int INIT              = 100;
+    private static final int PLAY              = 101;
+
+    // The singleton instance.
+    private static HTML5VideoViewProxy sInstance;
+    // The VideoView driven via this proxy.
+    private VideoView mVideoView;
+    // The context object used to initialize the VideoView and the
+    // MediaController.
+    private Context mContext;
+
+    /**
+     * Private constructor.
+     * @param context is the application context.
+     */
+    private HTML5VideoViewProxy(Context context) {
+        // This handler is for the main (UI) thread.
+        super(Looper.getMainLooper());
+        // Save the context object.
+        mContext = context;
+        // Send a message to the UI thread to create the VideoView.
+        // This need to be done on the UI thread, or else the
+        // event Handlers used by the VideoView and MediaController
+        // will be attached to the wrong thread.
+        Message message = obtainMessage(INIT);
+        sendMessage(message);
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        // This executes on the UI thread.
+        switch (msg.what) {
+            case INIT:
+                // Create the video view and set a default controller.
+                mVideoView = new VideoView(mContext);
+                mVideoView.setMediaController(new MediaController(mContext));
+                break;
+            case PLAY:
+                // Check if the fullscreen video view is currently playing.
+                // If it is, ignore the message.
+                if (!mVideoView.isPlaying()) {
+                    HashMap<String, Object> map =
+                        (HashMap<String, Object>) msg.obj;
+                    String url = (String) map.get("url");
+                    WebView webview = (WebView) map.get("webview");
+                    WebChromeClient client = webview.getWebChromeClient();
+                    if (client != null) {
+                        mVideoView.setVideoURI(Uri.parse(url));
+                        mVideoView.start();
+                        client.onShowCustomView(mVideoView);
+                    }
+                }
+                break;
+        }
+    }
+
+    /**
+     * Play a video stream.
+     * @param url is the URL of the video stream.
+     * @param webview is the WebViewCore that is requesting the playback.
+     */
+    public void play(String url, WebViewCore webviewCore) {
+        // We need to know the webview that is requesting the playback.
+        Message message = obtainMessage(PLAY);
+        HashMap<String, Object> map = new HashMap();
+        map.put("url", url);
+        map.put("webview", webviewCore.getWebView());
+        message.obj = map;
+        sendMessage(message);
+    }
+
+    /**
+     * The factory for HTML5VideoViewProxy instances. Right now,
+     * it only produces a singleton.
+     * @param webViewCore is the WebViewCore that is requesting the proxy.
+     *
+     * @return the HTML5VideoViewProxy singleton.
+     */
+    public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore) {
+        if (sInstance == null) {
+            sInstance = new HTML5VideoViewProxy(webViewCore.getWebView().getContext());
+        }
+        return sInstance;
+    }
+}
diff --git a/core/java/android/webkit/HttpDateTime.java b/core/java/android/webkit/HttpDateTime.java
index c6ec2d2..48b2081f 100644
--- a/core/java/android/webkit/HttpDateTime.java
+++ b/core/java/android/webkit/HttpDateTime.java
@@ -47,14 +47,16 @@
      * Wdy, DD Mon YYYY HH:MM:SS
      * Wdy Mon (SP)D HH:MM:SS YYYY
      * Wdy Mon DD HH:MM:SS YYYY GMT
+     * 
+     * HH can be H if the first digit is zero.
      */
     private static final String HTTP_DATE_RFC_REGEXP =
             "([0-9]{1,2})[- ]([A-Za-z]{3,3})[- ]([0-9]{2,4})[ ]"
-            + "([0-9][0-9]:[0-9][0-9]:[0-9][0-9])";
+            + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])";
 
     private static final String HTTP_DATE_ANSIC_REGEXP =
             "[ ]([A-Za-z]{3,3})[ ]+([0-9]{1,2})[ ]"
-            + "([0-9][0-9]:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})";
+            + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})";
 
     /**
      * The compiled version of the HTTP-date regular expressions.
@@ -65,6 +67,12 @@
             Pattern.compile(HTTP_DATE_ANSIC_REGEXP);
 
     private static class TimeOfDay {
+        TimeOfDay(int h, int m, int s) {
+            this.hour = h;
+            this.minute = m;
+            this.second = s;
+        }
+        
         int hour;
         int minute;
         int second;
@@ -76,7 +84,7 @@
         int date = 1;
         int month = Calendar.JANUARY;
         int year = 1970;
-        TimeOfDay timeOfDay = new TimeOfDay();
+        TimeOfDay timeOfDay;
 
         Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString);
         if (rfcMatcher.find()) {
@@ -183,13 +191,22 @@
     }
 
     private static TimeOfDay getTime(String timeString) {
-        TimeOfDay time = new TimeOfDay();
-        time.hour = (timeString.charAt(0) - '0') * 10
-                + (timeString.charAt(1) - '0');
-        time.minute = (timeString.charAt(3) - '0') * 10
-                + (timeString.charAt(4) - '0');
-        time.second = (timeString.charAt(6) - '0') * 10
-                + (timeString.charAt(7) - '0');
-        return time;
+        // HH might be H
+        int i = 0;
+        int hour = timeString.charAt(i++) - '0';
+        if (timeString.charAt(i) != ':')
+            hour = hour * 10 + (timeString.charAt(i++) - '0');
+        // Skip ':'
+        i++;
+        
+        int minute = (timeString.charAt(i++) - '0') * 10
+                    + (timeString.charAt(i++) - '0');
+        // Skip ':'
+        i++;
+        
+        int second = (timeString.charAt(i++) - '0') * 10
+                  + (timeString.charAt(i++) - '0');
+
+        return new TimeOfDay(hour, minute, second);        
     }
 }
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index 5c0bd93..7c071be 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -34,9 +34,16 @@
     // Instant timer is used to implement a timer that needs to fire almost
     // immediately.
     private boolean mHasInstantTimer;
+
     // Reference count the pause/resume of timers
     private int mPauseTimerRefCount;
 
+    private boolean mTimerPaused;
+    private boolean mHasDeferredTimers;
+
+    /* package */
+    static final int REFRESH_PLUGINS = 100;
+
     /**
      * Construct a new JWebCoreJavaBridge to interface with
      * WebCore timers and cookies.
@@ -51,6 +58,17 @@
     }
 
     /**
+     * Call native timer callbacks.
+     */
+    private void fireSharedTimer() { 
+        PerfChecker checker = new PerfChecker();
+        // clear the flag so that sharedTimerFired() can set a new timer
+        mHasInstantTimer = false;
+        sharedTimerFired();
+        checker.responseAlert("sharedTimer");
+    }
+
+    /**
      * handleMessage
      * @param msg The dispatched message.
      *
@@ -60,16 +78,21 @@
     public void handleMessage(Message msg) {
         switch (msg.what) {
             case TIMER_MESSAGE: {
-                PerfChecker checker = new PerfChecker();
-                // clear the flag so that sharedTimerFired() can set a new timer
-                mHasInstantTimer = false;
-                sharedTimerFired();
-                checker.responseAlert("sharedTimer");
+                if (mTimerPaused) {
+                    mHasDeferredTimers = true;
+                } else {
+                    fireSharedTimer();
+                }
                 break;
             }
             case FUNCPTR_MESSAGE:
                 nativeServiceFuncPtrQueue();
                 break;
+            case REFRESH_PLUGINS:
+                nativeUpdatePluginDirectories(PluginManager.getInstance(null)
+                        .getPluginDirecoties(), ((Boolean) msg.obj)
+                        .booleanValue());
+                break;
         }
     }
     
@@ -86,7 +109,8 @@
      */
     public void pause() {
         if (--mPauseTimerRefCount == 0) {
-            setDeferringTimers(true);
+            mTimerPaused = true;
+            mHasDeferredTimers = false;
         }
     }
 
@@ -95,7 +119,11 @@
      */
     public void resume() {
         if (++mPauseTimerRefCount == 1) {
-            setDeferringTimers(false);
+           mTimerPaused = false;
+           if (mHasDeferredTimers) {
+               mHasDeferredTimers = false;
+               fireSharedTimer();
+           }
         }
     }
 
@@ -108,10 +136,9 @@
     /**
      * Store a cookie string associated with a url.
      * @param url The url to be used as a key for the cookie.
-     * @param docUrl The policy base url used by WebCore.
      * @param value The cookie string to be stored.
      */
-    private void setCookies(String url, String docUrl, String value) {
+    private void setCookies(String url, String value) {
         if (value.contains("\r") || value.contains("\n")) {
             // for security reason, filter out '\r' and '\n' from the cookie
             int size = value.length();
@@ -152,11 +179,18 @@
     }
 
     /**
+     * Returns an array of plugin directoies
+     */
+    private String[] getPluginDirectories() {
+        return PluginManager.getInstance(null).getPluginDirecoties();
+    }
+
+    /**
      * setSharedTimer
      * @param timemillis The relative time when the timer should fire
      */
     private void setSharedTimer(long timemillis) {
-        if (WebView.LOGV_ENABLED) Log.v(LOGTAG, "setSharedTimer " + timemillis);
+        if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) Log.v(LOGTAG, "setSharedTimer " + timemillis);
 
         if (timemillis <= 0) {
             // we don't accumulate the sharedTimer unless it is a delayed
@@ -180,11 +214,12 @@
      * Stop the shared timer.
      */
     private void stopSharedTimer() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) {
             Log.v(LOGTAG, "stopSharedTimer removing all timers");
         }
         removeMessages(TIMER_MESSAGE);
         mHasInstantTimer = false;
+        mHasDeferredTimers = false;
     }
 
     private String[] getKeyStrengthList() {
@@ -199,6 +234,7 @@
     private native void nativeConstructor();
     private native void nativeFinalize();
     private native void sharedTimerFired();
-    private native void setDeferringTimers(boolean defer);
+    private native void nativeUpdatePluginDirectories(String[] directories,
+            boolean reload);
     public native void setNetworkOnLine(boolean online);
 }
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index 07e03ff..6f228a4 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -43,8 +43,6 @@
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
 
-import org.apache.commons.codec.binary.Base64;
-
 class LoadListener extends Handler implements EventHandler {
 
     private static final String LOGTAG = "webkit";
@@ -135,15 +133,13 @@
 
     LoadListener(Context context, BrowserFrame frame, String url,
             int nativeLoader, boolean synchronous, boolean isMainPageLoader) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener constructor url=" + url);
         }
         mContext = context;
         mBrowserFrame = frame;
         setUrl(url);
         mNativeLoader = nativeLoader;
-        mMimeType = "";
-        mEncoding = "";
         mSynchronous = synchronous;
         if (synchronous) {
             mMessageQueue = new Vector<Message>();
@@ -286,7 +282,7 @@
      * directly
      */
     public void headers(Headers headers) {
-        if (WebView.LOGV_ENABLED) Log.v(LOGTAG, "LoadListener.headers");
+        if (DebugFlags.LOAD_LISTENER) Log.v(LOGTAG, "LoadListener.headers");
         sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS, headers));
     }
 
@@ -294,8 +290,6 @@
     private void handleHeaders(Headers headers) {
         if (mCancelled) return;
         mHeaders = headers;
-        mMimeType = "";
-        mEncoding = "";
 
         ArrayList<String> cookies = headers.getSetCookie();
         for (int i = 0; i < cookies.size(); ++i) {
@@ -315,24 +309,21 @@
 
             // If we have one of "generic" MIME types, try to deduce
             // the right MIME type from the file extension (if any):
-            if (mMimeType.equalsIgnoreCase("text/plain") ||
-                    mMimeType.equalsIgnoreCase("application/octet-stream")) {
+            if (mMimeType.equals("text/plain") ||
+                    mMimeType.equals("application/octet-stream")) {
 
                 String newMimeType = guessMimeTypeFromExtension();
                 if (newMimeType != null) {
                     mMimeType = newMimeType;
                 }
-            } else if (mMimeType.equalsIgnoreCase("text/vnd.wap.wml")) {
+            } else if (mMimeType.equals("text/vnd.wap.wml")) {
                 // As we don't support wml, render it as plain text
                 mMimeType = "text/plain";
             } else {
-                // XXX: Until the servers send us either correct xhtml or
-                // text/html, treat application/xhtml+xml as text/html.
                 // It seems that xhtml+xml and vnd.wap.xhtml+xml mime
                 // subtypes are used interchangeably. So treat them the same.
-                if (mMimeType.equalsIgnoreCase("application/xhtml+xml") ||
-                        mMimeType.equals("application/vnd.wap.xhtml+xml")) {
-                    mMimeType = "text/html";
+                if (mMimeType.equals("application/vnd.wap.xhtml+xml")) {
+                    mMimeType = "application/xhtml+xml";
                 }
             }
         } else {
@@ -433,7 +424,7 @@
      */
     public void status(int majorVersion, int minorVersion,
             int code, /* Status-Code value */ String reasonPhrase) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener: from: " + mUrl
                     + " major: " + majorVersion
                     + " minor: " + minorVersion
@@ -447,6 +438,9 @@
         status.put("reason", reasonPhrase);
         // New status means new data. Clear the old.
         mDataBuilder.clear();
+        mMimeType = "";
+        mEncoding = "";
+        mTransferEncoding = "";
         sendMessageInternal(obtainMessage(MSG_STATUS, status));
     }
 
@@ -490,7 +484,7 @@
      * directly
      */
     public void error(int id, String description) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.error url:" +
                     url() + " id:" + id + " description:" + description);
         }
@@ -518,23 +512,10 @@
      * mDataBuilder is a thread-safe structure.
      */
     public void data(byte[] data, int length) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.data(): url: " + url());
         }
 
-        // Decode base64 data
-        // Note: It's fine that we only decode base64 here and not in the other
-        // data call because the only caller of the stream version is not
-        // base64 encoded.
-        if ("base64".equalsIgnoreCase(mTransferEncoding)) {
-            if (length < data.length) {
-                byte[] trimmedData = new byte[length];
-                System.arraycopy(data, 0, trimmedData, 0, length);
-                data = trimmedData;
-            }
-            data = Base64.decodeBase64(data);
-            length = data.length;
-        }
         // Synchronize on mData because commitLoad may write mData to WebCore
         // and we don't want to replace mData or mDataLength at the same time
         // as a write.
@@ -556,7 +537,7 @@
      * directly
      */
     public void endData() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.endData(): url: " + url());
         }
         sendMessageInternal(obtainMessage(MSG_CONTENT_FINISHED));
@@ -609,7 +590,7 @@
                 // before calling it.
                 if (mCacheLoader != null) {
                     mCacheLoader.load();
-                    if (WebView.LOGV_ENABLED) {
+                    if (DebugFlags.LOAD_LISTENER) {
                         Log.v(LOGTAG, "LoadListener cache load url=" + url());
                     }
                     return;
@@ -659,7 +640,7 @@
                     CacheManager.HEADER_KEY_IFNONEMATCH) &&
                     !headers.containsKey(
                             CacheManager.HEADER_KEY_IFMODIFIEDSINCE)) {
-                if (WebView.LOGV_ENABLED) {
+                if (DebugFlags.LOAD_LISTENER) {
                     Log.v(LOGTAG, "FrameLoader: HTTP URL in cache " +
                             "and usable: " + url());
                 }
@@ -678,7 +659,7 @@
      * directly
      */
     public void handleSslErrorRequest(SslError error) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG,
                     "LoadListener.handleSslErrorRequest(): url:" + url() +
                     " primary error: " + error.getPrimaryError() +
@@ -744,7 +725,7 @@
      * are null, cancel the request.
      */
     void handleAuthResponse(String username, String password) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.handleAuthResponse: url: " + mUrl
                     + " username: " + username
                     + " password: " + password);
@@ -841,7 +822,7 @@
     }
 
     void attachRequestHandle(RequestHandle requestHandle) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.attachRequestHandle(): " +
                     "requestHandle: " +  requestHandle);
         }
@@ -849,7 +830,7 @@
     }
 
     void detachRequestHandle() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.detachRequestHandle(): " +
                     "requestHandle: " + mRequestHandle);
         }
@@ -888,7 +869,7 @@
      */
     static boolean willLoadFromCache(String url) {
         boolean inCache = CacheManager.getCacheFile(url, null) != null;
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "willLoadFromCache: " + url + " in cache: " + 
                     inCache);
         }
@@ -909,6 +890,10 @@
         return mMimeType;
     }
 
+    String transferEncoding() {
+        return mTransferEncoding;
+    }
+
     /*
      * Return the size of the content being downloaded. This represents the
      * full content size, even under the situation where the download has been
@@ -963,8 +948,7 @@
         // pass content-type content-length and content-encoding
         final int nativeResponse = nativeCreateResponse(
                 mUrl, statusCode, mStatusText,
-                mMimeType, mContentLength, mEncoding,
-                mCacheResult == null ? 0 : mCacheResult.expires / 1000);
+                mMimeType, mContentLength, mEncoding);
         if (mHeaders != null) {
             mHeaders.getHeaders(new Headers.HeaderCallback() {
                     public void header(String name, String value) {
@@ -1088,7 +1072,7 @@
      * EventHandler's method call.
      */
     public void cancel() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             if (mRequestHandle == null) {
                 Log.v(LOGTAG, "LoadListener.cancel(): no requestHandle");
             } else {
@@ -1220,7 +1204,7 @@
             tearDown();
         }
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.onRedirect(): redirect to: " +
                     redirectTo);
         }
@@ -1233,8 +1217,8 @@
     private static final Pattern CONTENT_TYPE_PATTERN =
             Pattern.compile("^((?:[xX]-)?[a-zA-Z\\*]+/[\\w\\+\\*-]+[\\.[\\w\\+-]+]*)$");
 
-    private void parseContentTypeHeader(String contentType) {
-        if (WebView.LOGV_ENABLED) {
+    /* package */ void parseContentTypeHeader(String contentType) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.parseContentTypeHeader: " +
                     "contentType: " + contentType);
         }
@@ -1255,13 +1239,14 @@
                     mEncoding = contentType.substring(i + 1);
                 }
                 // Trim excess whitespace.
-                mEncoding = mEncoding.trim();
+                mEncoding = mEncoding.trim().toLowerCase();
 
                 if (i < contentType.length() - 1) {
                     // for data: uri the mimeType and encoding have
                     // the form image/jpeg;base64 or text/plain;charset=utf-8
                     // or text/html;charset=utf-8;base64
-                    mTransferEncoding = contentType.substring(i + 1).trim();
+                    mTransferEncoding =
+                            contentType.substring(i + 1).trim().toLowerCase();
                 }
             } else {
                 mMimeType = contentType;
@@ -1281,6 +1266,8 @@
                 guessMimeType();
             }
         }
+        // Ensure mMimeType is lower case.
+        mMimeType = mMimeType.toLowerCase();
     }
 
     /**
@@ -1411,7 +1398,7 @@
             mMimeType = "text/html";
             String newMimeType = guessMimeTypeFromExtension();
             if (newMimeType != null) {
-                mMimeType =  newMimeType;
+                mMimeType = newMimeType;
             }
         }
     }
@@ -1421,23 +1408,12 @@
      */
     private String guessMimeTypeFromExtension() {
         // PENDING: need to normalize url
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "guessMimeTypeFromExtension: mURL = " + mUrl);
         }
 
-        String mimeType =
-                MimeTypeMap.getSingleton().getMimeTypeFromExtension(
-                        MimeTypeMap.getFileExtensionFromUrl(mUrl));
-
-        if (mimeType != null) {
-            // XXX: Until the servers send us either correct xhtml or
-            // text/html, treat application/xhtml+xml as text/html.
-            if (mimeType.equals("application/xhtml+xml")) {
-                mimeType = "text/html";
-            }
-        }
-
-        return mimeType;
+        return MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+                MimeTypeMap.getFileExtensionFromUrl(mUrl));
     }
 
     /**
@@ -1456,7 +1432,7 @@
      * Cycle through our messages for synchronous loads.
      */
     /* package */ void loadSynchronousMessages() {
-        if (WebView.DEBUG && !mSynchronous) {
+        if (DebugFlags.LOAD_LISTENER && !mSynchronous) {
             throw new AssertionError();
         }
         // Note: this can be called twice if it is a synchronous network load,
@@ -1483,12 +1459,11 @@
      * @param expectedLength An estimate of the content length or the length
      *                       given by the server.
      * @param encoding HTTP encoding.
-     * @param expireTime HTTP expires converted to seconds since the epoch.
      * @return The native response pointer.
      */
     private native int nativeCreateResponse(String url, int statusCode,
             String statusText, String mimeType, long expectedLength,
-            String encoding, long expireTime);
+            String encoding);
 
     /**
      * Add a response header to the native object.
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index 85c2275..096f38a 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -358,6 +358,7 @@
             sMimeTypeMap.loadEntry("application/x-x509-ca-cert", "crt", false);
             sMimeTypeMap.loadEntry("application/x-xcf", "xcf", false);
             sMimeTypeMap.loadEntry("application/x-xfig", "fig", false);
+            sMimeTypeMap.loadEntry("application/xhtml+xml", "xhtml", false);
             sMimeTypeMap.loadEntry("audio/basic", "snd", false);
             sMimeTypeMap.loadEntry("audio/midi", "mid", false);
             sMimeTypeMap.loadEntry("audio/midi", "midi", false);
diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java
index c9b80ce..8c2b09b 100644
--- a/core/java/android/webkit/Network.java
+++ b/core/java/android/webkit/Network.java
@@ -132,7 +132,7 @@
      * XXX: Must be created in the same thread as WebCore!!!!!
      */
     private Network(Context context) {
-        if (WebView.DEBUG) {
+        if (DebugFlags.NETWORK) {
             Assert.assertTrue(Thread.currentThread().
                     getName().equals(WebViewCore.THREAD_NAME));
         }
@@ -232,7 +232,7 @@
      * connecting through the proxy.
      */
     public synchronized void setProxyUsername(String proxyUsername) {
-        if (WebView.DEBUG) {
+        if (DebugFlags.NETWORK) {
             Assert.assertTrue(isValidProxySet());
         }
 
@@ -252,7 +252,7 @@
      * connecting through the proxy.
      */
     public synchronized void setProxyPassword(String proxyPassword) {
-        if (WebView.DEBUG) {
+        if (DebugFlags.NETWORK) {
             Assert.assertTrue(isValidProxySet());
         }
 
@@ -266,7 +266,7 @@
      * @return True iff succeeds.
      */
     public boolean saveState(Bundle outState) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.NETWORK) {
             Log.v(LOGTAG, "Network.saveState()");
         }
 
@@ -280,7 +280,7 @@
      * @return True iff succeeds.
      */
     public boolean restoreState(Bundle inState) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.NETWORK) {
             Log.v(LOGTAG, "Network.restoreState()");
         }
 
@@ -300,7 +300,7 @@
      * @param loader The loader that resulted in SSL errors.
      */
     public void handleSslErrorRequest(LoadListener loader) {
-        if (WebView.DEBUG) Assert.assertNotNull(loader);
+        if (DebugFlags.NETWORK) Assert.assertNotNull(loader);
         if (loader != null) {
             mSslErrorHandler.handleSslErrorRequest(loader);
         }
@@ -313,7 +313,7 @@
      * authentication request.
      */
     public void handleAuthRequest(LoadListener loader) {
-        if (WebView.DEBUG) Assert.assertNotNull(loader);
+        if (DebugFlags.NETWORK) Assert.assertNotNull(loader);
         if (loader != null) {
             mHttpAuthHandler.handleAuthRequest(loader);
         }
diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java
new file mode 100644
index 0000000..e4a44b92
--- /dev/null
+++ b/core/java/android/webkit/PluginManager.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.Signature;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Log;
+
+/**
+ * Class for managing the relationship between the {@link WebView} and installed
+ * plugins in the system. You can find this class through
+ * {@link PluginManager#getInstance}.
+ * 
+ * @hide pending API solidification
+ */
+public class PluginManager {
+
+    /**
+     * Service Action: A plugin wishes to be loaded in the WebView must provide
+     * {@link android.content.IntentFilter IntentFilter} that accepts this
+     * action in their AndroidManifest.xml.
+     * <p>
+     * TODO: we may change this to a new PLUGIN_ACTION if this is going to be
+     * public.
+     */
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
+    public static final String PLUGIN_ACTION = "android.webkit.PLUGIN";
+
+    /**
+     * A plugin wishes to be loaded in the WebView must provide this permission
+     * in their AndroidManifest.xml.
+     */
+    public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN";
+
+    private static final String LOGTAG = "webkit";
+
+    private static PluginManager mInstance = null;
+
+    private final Context mContext;
+
+    private PluginManager(Context context) {
+        mContext = context;
+    }
+
+    public static synchronized PluginManager getInstance(Context context) {
+        if (mInstance == null) {
+            if (context == null) {
+                throw new IllegalStateException(
+                        "First call to PluginManager need a valid context.");
+            }
+            mInstance = new PluginManager(context);
+        }
+        return mInstance;
+    }
+
+    /**
+     * Signal the WebCore thread to refresh its list of plugins. Use this if the
+     * directory contents of one of the plugin directories has been modified and
+     * needs its changes reflecting. May cause plugin load and/or unload.
+     * 
+     * @param reloadOpenPages Set to true to reload all open pages.
+     */
+    public void refreshPlugins(boolean reloadOpenPages) {
+        BrowserFrame.sJavaBridge.obtainMessage(
+                JWebCoreJavaBridge.REFRESH_PLUGINS, reloadOpenPages)
+                .sendToTarget();
+    }
+
+    String[] getPluginDirecoties() {
+        ArrayList<String> directories = new ArrayList<String>();
+        PackageManager pm = mContext.getPackageManager();
+        List<ResolveInfo> plugins = pm.queryIntentServices(new Intent(
+                PLUGIN_ACTION), PackageManager.GET_SERVICES);
+        for (ResolveInfo info : plugins) {
+            ServiceInfo serviceInfo = info.serviceInfo;
+            if (serviceInfo == null) {
+                Log.w(LOGTAG, "Ignore bad plugin");
+                continue;
+            }
+            PackageInfo pkgInfo;
+            try {
+                pkgInfo = pm.getPackageInfo(serviceInfo.packageName,
+                        PackageManager.GET_PERMISSIONS
+                                | PackageManager.GET_SIGNATURES);
+            } catch (NameNotFoundException e) {
+                Log.w(LOGTAG, "Cant find plugin: " + serviceInfo.packageName);
+                continue;
+            }
+            if (pkgInfo == null) {
+                continue;
+            }
+            String directory = pkgInfo.applicationInfo.dataDir + "/lib";
+            if (directories.contains(directory)) {
+                continue;
+            }
+            String permissions[] = pkgInfo.requestedPermissions;
+            if (permissions == null) {
+                continue;
+            }
+            boolean permissionOk = false;
+            for (String permit : permissions) {
+                if (PLUGIN_PERMISSION.equals(permit)) {
+                    permissionOk = true;
+                    break;
+                }
+            }
+            if (!permissionOk) {
+                continue;
+            }
+            Signature signatures[] = pkgInfo.signatures;
+            if (signatures == null) {
+                continue;
+            }
+            boolean signatureMatch = false;
+            for (Signature signature : signatures) {
+                // TODO: check signature against Google provided one
+                signatureMatch = true;
+                break;
+            }
+            if (!signatureMatch) {
+                continue;
+            }
+            directories.add(directory);
+        }
+        // hack for gears for now
+        String gears = mContext.getDir("plugins", 0).getPath();
+        if (!directories.contains(gears)) {
+            directories.add(gears);
+        }
+        return directories.toArray(new String[directories.size()]);
+    }
+}
diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java
index 5f84bbe..cc1e750 100644
--- a/core/java/android/webkit/SslErrorHandler.java
+++ b/core/java/android/webkit/SslErrorHandler.java
@@ -120,7 +120,7 @@
      * Handles SSL error(s) on the way up to the user.
      */
     /* package */ synchronized void handleSslErrorRequest(LoadListener loader) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.SSL_ERROR_HANDLER) {
             Log.v(LOGTAG, "SslErrorHandler.handleSslErrorRequest(): " +
                   "url=" + loader.url());
         }
@@ -157,14 +157,14 @@
 
             SslError error = loader.sslError();
 
-            if (WebView.DEBUG) {
+            if (DebugFlags.SSL_ERROR_HANDLER) {
                 Assert.assertNotNull(error);
             }
 
             int primary = error.getPrimaryError();
             String host = loader.host();
 
-            if (WebView.DEBUG) {
+            if (DebugFlags.SSL_ERROR_HANDLER) {
                 Assert.assertTrue(host != null && primary != 0);
             }
 
@@ -205,11 +205,11 @@
      */
     /* package */ synchronized void handleSslErrorResponse(boolean proceed) {
         LoadListener loader = mLoaderQueue.poll();
-        if (WebView.DEBUG) {
+        if (DebugFlags.SSL_ERROR_HANDLER) {
             Assert.assertNotNull(loader);
         }
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.SSL_ERROR_HANDLER) {
             Log.v(LOGTAG, "SslErrorHandler.handleSslErrorResponse():"
                   + " proceed: " + proceed
                   + " url:" + loader.url());
@@ -221,13 +221,13 @@
                 int primary = loader.sslError().getPrimaryError();
                 String host = loader.host();
 
-                if (WebView.DEBUG) {
+                if (DebugFlags.SSL_ERROR_HANDLER) {
                     Assert.assertTrue(host != null && primary != 0);
                 }
                 boolean hasKey = mSslPrefTable.containsKey(host);
                 if (!hasKey ||
                     primary > mSslPrefTable.getInt(host)) {
-                    mSslPrefTable.putInt(host, new Integer(primary));
+                    mSslPrefTable.putInt(host, primary);
                 }
             }
             loader.handleSslErrorResponse(proceed);
diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java
index 705157c..eab3350 100644
--- a/core/java/android/webkit/StreamLoader.java
+++ b/core/java/android/webkit/StreamLoader.java
@@ -102,7 +102,7 @@
                 // to pass data to the loader
                 mData = new byte[8192];
                 sendHeaders();
-                while (!sendData());
+                while (!sendData() && !mHandler.cancelled());
                 closeStreamAndSendEndData();
                 mHandler.loadSynchronousMessages();
             }
@@ -113,9 +113,13 @@
      * @see android.os.Handler#handleMessage(android.os.Message)
      */
     public void handleMessage(Message msg) {
-        if (WebView.DEBUG && mHandler.isSynchronous()) {
+        if (DebugFlags.STREAM_LOADER && mHandler.isSynchronous()) {
             throw new AssertionError();
         }
+        if (mHandler.cancelled()) {
+            closeStreamAndSendEndData();
+            return;
+        }
         switch(msg.what) {
             case MSG_STATUS:
                 if (setupStreamAndSendStatus()) {
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index d6ac3e9..de70fc2 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -61,7 +61,7 @@
             webAddress = new WebAddress(inUrl);
         } catch (ParseException ex) {
 
-            if (WebView.LOGV_ENABLED) {
+            if (DebugFlags.URL_UTIL) {
                 Log.v(LOGTAG, "smartUrlFilter: failed to parse url = " + inUrl);
             }
             return retVal;
diff --git a/core/java/android/webkit/WebBackForwardList.java b/core/java/android/webkit/WebBackForwardList.java
index ffd6a11..62a5531 100644
--- a/core/java/android/webkit/WebBackForwardList.java
+++ b/core/java/android/webkit/WebBackForwardList.java
@@ -137,7 +137,7 @@
         // when removing the first item, we can assert that the index is 0.
         // This lets us change the current index without having to query the
         // native BackForwardList.
-        if (WebView.DEBUG && (index != 0)) {
+        if (DebugFlags.WEB_BACK_FORWARD_LIST && (index != 0)) {
             throw new AssertionError();
         }
         final WebHistoryItem h = mArray.remove(index);
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 9d9763c..19e39ed 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Bitmap;
 import android.os.Message;
+import android.view.View;
 
 public class WebChromeClient {
 
@@ -44,6 +45,23 @@
     public void onReceivedIcon(WebView view, Bitmap icon) {}
 
     /**
+     * Notify the host application that the current page would
+     * like to show a custom View.
+     * @param view is the View object to be shown.
+     *
+     * @hide pending council approval
+     */
+    public void onShowCustomView(View view) {}
+
+    /**
+     * Notify the host application that the current page would
+     * like to hide its custom view.
+     *
+     * @hide pending council approval
+     */
+    public void onHideCustomView() {}
+
+    /**
      * Request the host application to create a new Webview. The host
      * application should handle placement of the new WebView in the view
      * system. The default behavior returns null.
@@ -158,6 +176,23 @@
         return false;
     }
 
+   /**
+    * Tell the client that the database quota for the origin has been exceeded.
+    * @param url The URL that triggered the notification
+    * @param databaseIdentifier The identifier of the database that caused the
+    *     quota overflow.
+    * @param currentQuota The current quota for the origin.
+    * @param quotaUpdater A callback to inform the WebCore thread that a new
+    *     quota is available. This callback must always be executed at some
+    *     point to ensure that the sleeping WebCore thread is woken up.
+    */
+    public void onExceededDatabaseQuota(String url, String databaseIdentifier,
+        long currentQuota, WebStorage.QuotaUpdater quotaUpdater) {
+        // This default implementation passes the current quota back to WebCore.
+        // WebCore will interpret this that new quota was declined.
+        quotaUpdater.updateQuota(currentQuota);
+    }
+
     /**
      * Tell the client that a JavaScript execution timeout has occured. And the
      * client may decide whether or not to interrupt the execution. If the
@@ -172,4 +207,15 @@
     public boolean onJsTimeout() {
         return true;
     }
+
+    /**
+     * Add a JavaScript error message to the console. Clients should override
+     * this to process the log message as they see fit.
+     * @param message The error message to report.
+     * @param lineNumber The line number of the error.
+     * @param sourceID The name of the source file that caused the error.
+     * @hide pending API council.
+     */
+    public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+    }
 }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index ec671d5..0ee3a60 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -130,9 +130,13 @@
     private boolean mSyncPending = false;
     // Custom handler that queues messages until the WebCore thread is active.
     private final EventHandler mEventHandler;
+
     // Private settings so we don't have to go into native code to
     // retrieve the values. After setXXX, postSync() needs to be called.
-    // XXX: The default values need to match those in WebSettings.cpp
+    //
+    // The default values need to match those in WebSettings.cpp
+    // If the defaults change, please also update the JavaDocs so developers
+    // know what they are.
     private LayoutAlgorithm mLayoutAlgorithm = LayoutAlgorithm.NARROW_COLUMNS;
     private Context         mContext;
     private TextSize        mTextSize = TextSize.NORMAL;
@@ -146,7 +150,6 @@
     private String          mUserAgent;
     private boolean         mUseDefaultUserAgent;
     private String          mAcceptLanguage;
-    private String          mPluginsPath = "";
     private int             mMinimumFontSize = 8;
     private int             mMinimumLogicalFontSize = 8;
     private int             mDefaultFontSize = 16;
@@ -156,6 +159,7 @@
     private boolean         mBlockNetworkLoads;
     private boolean         mJavaScriptEnabled = false;
     private boolean         mPluginsEnabled = false;
+    private long            mWebStorageDefaultQuota = 0;
     private boolean         mJavaScriptCanOpenWindowsAutomatically = false;
     private boolean         mUseDoubleTree = false;
     private boolean         mUseWideViewport = false;
@@ -175,6 +179,11 @@
     private boolean         mSupportZoom = true;
     private boolean         mBuiltInZoomControls = false;
     private boolean         mAllowFileAccess = true;
+    private String          mDatabasePath = "";
+    private boolean         mDatabaseEnabled = false;
+    private String          mAppCachePath = "";
+    private boolean         mAppCacheEnabled = false;
+    private boolean         mDomStorageEnabled = false;
 
     // Class to handle messages before WebCore is ready.
     private class EventHandler {
@@ -243,13 +252,13 @@
 
     // User agent strings.
     private static final String DESKTOP_USERAGENT =
-            "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en)"
-            + " AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2"
-            + " Safari/525.20.1";
+            "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)"
+            + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0"
+            + " Safari/530.17";
     private static final String IPHONE_USERAGENT = 
-            "Mozilla/5.0 (iPhone; U; CPU iPhone 2_1 like Mac OS X; en)"
-            + " AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2"
-            + " Mobile/5F136 Safari/525.20.1";
+            "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
+            + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
+            + " Mobile/7A341 Safari/528.16";
     private static Locale sLocale;
     private static Object sLockForLocaleSettings;
     
@@ -507,24 +516,21 @@
     }
 
     /**
-     * Tell the WebView to use the double tree rendering algorithm.
-     * @param use True if the WebView is to use double tree rendering, false
-     *            otherwise.
+     * @deprecated This setting controlled a rendering optimization
+     * that is no longer present. Setting it now has no effect.
      */
+    @Deprecated
     public synchronized void setUseDoubleTree(boolean use) {
-        if (mUseDoubleTree != use) {
-            mUseDoubleTree = use;
-            postSync();
-        }
+        return;
     }
 
     /**
-     * Return true if the WebView is using the double tree rendering algorithm.
-     * @return True if the WebView is using the double tree rendering
-     *         algorithm.
+     * @deprecated This setting controlled a rendering optimization
+     * that is no longer present. Setting it now has no effect.
      */
+    @Deprecated
     public synchronized boolean getUseDoubleTree() {
-        return mUseDoubleTree;
+        return false;
     }
 
     /**
@@ -629,7 +635,7 @@
     }
 
     /**
-     * Return the current layout algorithm.
+     * Return the current layout algorithm. The default is NARROW_COLUMNS.
      * @return LayoutAlgorithm enum value describing the layout algorithm
      *         being used.
      * @see WebSettings.LayoutAlgorithm
@@ -650,7 +656,7 @@
     }
 
     /**
-     * Get the standard font family name.
+     * Get the standard font family name. The default is "sans-serif".
      * @return The standard font family name as a string.
      */
     public synchronized String getStandardFontFamily() {
@@ -669,7 +675,7 @@
     }
 
     /**
-     * Get the fixed font family name.
+     * Get the fixed font family name. The default is "monospace".
      * @return The fixed font family name as a string.
      */
     public synchronized String getFixedFontFamily() {
@@ -696,7 +702,7 @@
     }
 
     /**
-     * Set the serif font family name.
+     * Set the serif font family name. The default is "sans-serif".
      * @param font A font family name.
      */
     public synchronized void setSerifFontFamily(String font) {
@@ -707,7 +713,7 @@
     }
 
     /**
-     * Get the serif font family name.
+     * Get the serif font family name. The default is "serif".
      * @return The serif font family name as a string.
      */
     public synchronized String getSerifFontFamily() {
@@ -726,7 +732,7 @@
     }
 
     /**
-     * Get the cursive font family name.
+     * Get the cursive font family name. The default is "cursive".
      * @return The cursive font family name as a string.
      */
     public synchronized String getCursiveFontFamily() {
@@ -745,7 +751,7 @@
     }
 
     /**
-     * Get the fantasy font family name.
+     * Get the fantasy font family name. The default is "fantasy".
      * @return The fantasy font family name as a string.
      */
     public synchronized String getFantasyFontFamily() {
@@ -766,7 +772,7 @@
     }
 
     /**
-     * Get the minimum font size.
+     * Get the minimum font size. The default is 8.
      * @return A non-negative integer between 1 and 72.
      */
     public synchronized int getMinimumFontSize() {
@@ -787,7 +793,7 @@
     }
 
     /**
-     * Get the minimum logical font size.
+     * Get the minimum logical font size. The default is 8.
      * @return A non-negative integer between 1 and 72.
      */
     public synchronized int getMinimumLogicalFontSize() {
@@ -808,7 +814,7 @@
     }
 
     /**
-     * Get the default font size.
+     * Get the default font size. The default is 16.
      * @return A non-negative integer between 1 and 72.
      */
     public synchronized int getDefaultFontSize() {
@@ -829,7 +835,7 @@
     }
 
     /**
-     * Get the default fixed font size.
+     * Get the default fixed font size. The default is 16.
      * @return A non-negative integer between 1 and 72.
      */
     public synchronized int getDefaultFixedFontSize() {
@@ -849,6 +855,7 @@
 
     /**
      * Return true if the WebView will load image resources automatically.
+     * The default is true.
      * @return True if the WebView loads images automatically.
      */
     public synchronized boolean getLoadsImagesAutomatically() {
@@ -868,16 +875,16 @@
     }
 
     /**
-     * Return true if the WebView will block network image.
+     * Return true if the WebView will block network image. The default is false.
      * @return True if the WebView blocks network image.
      */
     public synchronized boolean getBlockNetworkImage() {
         return mBlockNetworkImage;
     }
-    
+
     /**
      * @hide
-     * Tell the WebView to block all network load requests. 
+     * Tell the WebView to block all network load requests.
      * @param flag True if the WebView should block all network loads
      */
     public synchronized void setBlockNetworkLoads(boolean flag) {
@@ -890,13 +897,14 @@
     /**
      * @hide
      * Return true if the WebView will block all network loads.
+     * The default is false.
      * @return True if the WebView blocks all network loads.
      */
     public synchronized boolean getBlockNetworkLoads() {
         return mBlockNetworkLoads;
     }
-    
-    
+
+
     private void verifyNetworkAccess() {
         if (!mBlockNetworkLoads) {
             if (mContext.checkPermission("android.permission.INTERNET", 
@@ -932,19 +940,116 @@
     }
 
     /**
-     * Set a custom path to plugins used by the WebView. The client
-     * must ensure it exists before this call.
-     * @param pluginsPath String path to the directory containing plugins.
+     * TODO: need to add @Deprecated
      */
     public synchronized void setPluginsPath(String pluginsPath) {
-        if (pluginsPath != null && !pluginsPath.equals(mPluginsPath)) {
-            mPluginsPath = pluginsPath;
+    }
+
+    /**
+     * @hide
+     * Set the default quota for WebStorage DBs
+     * @param quota the default quota in bytes
+     */
+    public synchronized void setWebStorageDefaultQuota(long quota) {
+        if (mWebStorageDefaultQuota != quota) {
+            mWebStorageDefaultQuota = quota;
             postSync();
         }
     }
 
     /**
-     * Return true if javascript is enabled.
+     * Set the path to where database storage API databases should be saved.
+     * This will update WebCore when the Sync runs in the C++ side.
+     * @param databasePath String path to the directory where databases should
+     *     be saved. May be the empty string but should never be null.
+     */
+    public synchronized void setDatabasePath(String databasePath) {
+        if (databasePath != null && !databasePath.equals(mDatabasePath)) {
+            mDatabasePath = databasePath;
+            postSync();
+        }
+    }
+
+    /**
+     * Tell the WebView to enable Application Caches API.
+     * @param flag True if the WebView should enable Application Caches.
+     * @hide pending api council approval
+     */
+    public synchronized void setAppCacheEnabled(boolean flag) {
+        if (mAppCacheEnabled != flag) {
+            mAppCacheEnabled = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * Set a custom path to the Application Caches files. The client
+     * must ensure it exists before this call.
+     * @param appCachePath String path to the directory containing Application
+     * Caches files. The appCache path can be the empty string but should not
+     * be null. Passing null for this parameter will result in a no-op.
+     * @hide pending api council approval
+     */
+    public synchronized void setAppCachePath(String appCachePath) {
+        if (appCachePath != null && !appCachePath.equals(mAppCachePath)) {
+            mAppCachePath = appCachePath;
+            postSync();
+        }
+    }
+
+    /**
+     * Set whether the database storage API is enabled.
+     * @param flag boolean True if the WebView should use the database storage
+     *     API.
+     */
+    public synchronized void setDatabaseEnabled(boolean flag) {
+       if (mDatabaseEnabled != flag) {
+           mDatabaseEnabled = flag;
+           postSync();
+       }
+    }
+
+    /**
+     * Set whether the DOM storage API is enabled.
+     * @param flag boolean True if the WebView should use the DOM storage
+     *     API.
+     * @hide pending API council.
+     */
+    public synchronized void setDomStorageEnabled(boolean flag) {
+       if (mDomStorageEnabled != flag) {
+           mDomStorageEnabled = flag;
+           postSync();
+       }
+    }
+
+    /**
+     * Returns true if the DOM Storage API's are enabled.
+     * @return True if the DOM Storage API's are enabled.
+     * @hide pending API council.
+     */
+    public synchronized boolean getDomStorageEnabled() {
+       return mDomStorageEnabled;
+    }
+
+    /**
+     * Return the path to where database storage API databases are saved for
+     * the current WebView.
+     * @return the String path to the database storage API databases.
+     */
+    public synchronized String getDatabasePath() {
+        return mDatabasePath;
+    }
+
+    /**
+     * Returns true if database storage API is enabled.
+     * @return True if the database storage API is enabled.
+     */
+    public synchronized boolean getDatabaseEnabled() {
+        return mDatabaseEnabled;
+    }
+
+    /**
+     * Return true if javascript is enabled. <b>Note: The default is false.</b>
      * @return True if javascript is enabled.
      */
     public synchronized boolean getJavaScriptEnabled() {
@@ -960,11 +1065,19 @@
     }
 
     /**
-     * Return the current path used for plugins in the WebView.
-     * @return The string path to the WebView plugins.
+     * TODO: need to add @Deprecated
      */
     public synchronized String getPluginsPath() {
-        return mPluginsPath;
+        return "";
+    }
+
+    /**
+     * @hide
+     * Return the default quota for WebStorage DBs
+     * @return the default quota in bytes
+     */
+    public synchronized long getWebStorageDefaultQuota() {
+        return mWebStorageDefaultQuota;
     }
 
     /**
@@ -981,7 +1094,8 @@
     }
 
     /**
-     * Return true if javascript can open windows automatically.
+     * Return true if javascript can open windows automatically. The default
+     * is false.
      * @return True if javascript can open windows automatically during
      *         window.open().
      */
@@ -1001,7 +1115,7 @@
     }
 
     /**
-     * Get the default text encoding name.
+     * Get the default text encoding name. The default is "Latin-1".
      * @return The default text encoding name as a string.
      */
     public synchronized String getDefaultTextEncodingName() {
@@ -1090,8 +1204,8 @@
 
     /**
      * Set the priority of the Render thread. Unlike the other settings, this
-     * one only needs to be called once per process.
-     * 
+     * one only needs to be called once per process. The default is NORMAL.
+     *
      * @param priority RenderPriority, can be normal, high or low.
      */
     public synchronized void setRenderPriority(RenderPriority priority) {
@@ -1145,7 +1259,7 @@
     /*package*/
     synchronized void syncSettingsAndCreateHandler(BrowserFrame frame) {
         mBrowserFrame = frame;
-        if (WebView.DEBUG) {
+        if (DebugFlags.WEB_SETTINGS) {
             junit.framework.Assert.assertTrue(frame.mNativeFrame != 0);
         }
         nativeSync(frame.mNativeFrame);
diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java
new file mode 100644
index 0000000..0488691
--- /dev/null
+++ b/core/java/android/webkit/WebStorage.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.HashMap;
+import java.util.Vector;
+
+/**
+ * Functionality for manipulating the webstorage databases.
+ */
+public final class WebStorage {
+
+    /**
+     * Encapsulates a callback function to be executed when a new quota is made
+     * available. We primarily want this to allow us to call back the sleeping
+     * WebCore thread from outside the WebViewCore class (as the native call
+     * is private). It is imperative that this the setDatabaseQuota method is
+     * executed once a decision to either allow or deny new quota is made,
+     * otherwise the WebCore thread will remain asleep.
+     */
+    public interface QuotaUpdater {
+        public void updateQuota(long newQuota);
+    };
+
+    // Log tag
+    private static final String TAG = "webstorage";
+
+    // Global instance of a WebStorage
+    private static WebStorage sWebStorage;
+
+    // We keep a copy of the origins, quotas and usages
+    // that we protect via a lock and update in syncValues()
+    private static Lock mLock = new ReentrantLock();
+    private static Condition mCacheUpdated = mLock.newCondition();
+
+    // Message ids
+    static final int UPDATE = 0;
+    static final int SET_QUOTA_ORIGIN = 1;
+    static final int DELETE_ORIGIN = 2;
+    static final int DELETE_ALL = 3;
+
+    private Vector <String> mOrigins;
+    private HashMap <String, Long> mQuotas = new HashMap<String, Long>();
+    private HashMap <String, Long> mUsages = new HashMap<String, Long>();
+
+    private Handler mHandler = null;
+
+    private class Origin {
+        String mOrigin = null;
+        long mQuota = 0;
+
+        public Origin(String origin, long quota) {
+            mOrigin = origin;
+            mQuota = quota;
+        }
+
+        public Origin(String origin) {
+            mOrigin = origin;
+        }
+
+        public String getOrigin() {
+            return mOrigin;
+        }
+
+        public long getQuota() {
+            return mQuota;
+        }
+    }
+
+    /**
+     * @hide
+     * Message handler
+     */
+    public void createHandler() {
+        if (mHandler == null) {
+            mHandler = new Handler() {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case SET_QUOTA_ORIGIN: {
+                            Origin website = (Origin) msg.obj;
+                            nativeSetQuotaForOrigin(website.getOrigin(),
+                                                    website.getQuota());
+                            syncValues();
+                            } break;
+
+                        case DELETE_ORIGIN: {
+                            Origin website = (Origin) msg.obj;
+                            nativeDeleteOrigin(website.getOrigin());
+                            syncValues();
+                            } break;
+
+                        case DELETE_ALL:
+                            nativeDeleteAllDatabases();
+                            syncValues();
+                            break;
+
+                        case UPDATE:
+                            syncValues();
+                            break;
+                    }
+                }
+            };
+        }
+    }
+
+    /**
+     * @hide
+     * Returns a list of origins having a database
+     */
+    public Vector getOrigins() {
+        Vector ret = null;
+        mLock.lock();
+        try {
+            update();
+            mCacheUpdated.await();
+            ret = mOrigins;
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Exception while waiting on the updated origins", e);
+        } finally {
+            mLock.unlock();
+        }
+        return ret;
+    }
+
+    /**
+     * @hide
+     * Returns the use for a given origin
+     */
+    public long getUsageForOrigin(String origin) {
+        long ret = 0;
+        if (origin == null) {
+          return ret;
+        }
+        mLock.lock();
+        try {
+            update();
+            mCacheUpdated.await();
+            Long usage = mUsages.get(origin);
+            if (usage != null) {
+                ret = usage.longValue();
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Exception while waiting on the updated origins", e);
+        } finally {
+            mLock.unlock();
+        }
+        return ret;
+    }
+
+    /**
+     * @hide
+     * Returns the quota for a given origin
+     */
+    public long getQuotaForOrigin(String origin) {
+        long ret = 0;
+        if (origin == null) {
+          return ret;
+        }
+        mLock.lock();
+        try {
+            update();
+            mCacheUpdated.await();
+            Long quota = mQuotas.get(origin);
+            if (quota != null) {
+                ret = quota.longValue();
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Exception while waiting on the updated origins", e);
+        } finally {
+            mLock.unlock();
+        }
+        return ret;
+    }
+
+    /**
+     * @hide
+     * Set the quota for a given origin
+     */
+    public void setQuotaForOrigin(String origin, long quota) {
+        if (origin != null) {
+            if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+                nativeSetQuotaForOrigin(origin, quota);
+                syncValues();
+            } else {
+                postMessage(Message.obtain(null, SET_QUOTA_ORIGIN,
+                    new Origin(origin, quota)));
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * Delete a given origin
+     */
+    public void deleteOrigin(String origin) {
+        if (origin != null) {
+            if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+                nativeDeleteOrigin(origin);
+                syncValues();
+            } else {
+                postMessage(Message.obtain(null, DELETE_ORIGIN,
+                    new Origin(origin)));
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * Delete all databases
+     */
+    public void deleteAllDatabases() {
+        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+            nativeDeleteAllDatabases();
+            syncValues();
+        } else {
+            postMessage(Message.obtain(null, DELETE_ALL));
+        }
+    }
+
+    /**
+     * Utility function to send a message to our handler
+     */
+    private void postMessage(Message msg) {
+        if (mHandler != null) {
+            mHandler.sendMessage(msg);
+        }
+    }
+
+    /**
+     * @hide
+     * Get the global instance of WebStorage.
+     * @return A single instance of WebStorage.
+     */
+    public static WebStorage getInstance() {
+      if (sWebStorage == null) {
+          sWebStorage = new WebStorage();
+      }
+      return sWebStorage;
+    }
+
+    /**
+     * @hide
+     * Post a Sync request
+     */
+    public void update() {
+        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+            syncValues();
+        } else {
+            postMessage(Message.obtain(null, UPDATE));
+        }
+    }
+
+    /**
+     * Run on the webcore thread
+     * sync the local cached values with the real ones
+     */
+    private void syncValues() {
+        mLock.lock();
+        Vector tmp = nativeGetOrigins();
+        mOrigins = new Vector<String>();
+        mQuotas.clear();
+        mUsages.clear();
+        for (int i = 0; i < tmp.size(); i++) {
+            String origin = (String) tmp.get(i);
+            mOrigins.add(origin);
+            mQuotas.put(origin, new Long(nativeGetQuotaForOrigin(origin)));
+            mUsages.put(origin, new Long(nativeGetUsageForOrigin(origin)));
+        }
+        mCacheUpdated.signal();
+        mLock.unlock();
+    }
+
+    // Native functions
+    private static native Vector nativeGetOrigins();
+    private static native long nativeGetUsageForOrigin(String origin);
+    private static native long nativeGetQuotaForOrigin(String origin);
+    private static native void nativeSetQuotaForOrigin(String origin, long quota);
+    private static native void nativeDeleteOrigin(String origin);
+    private static native void nativeDeleteAllDatabases();
+}
diff --git a/core/java/android/webkit/WebSyncManager.java b/core/java/android/webkit/WebSyncManager.java
index ded17ed..d3ec603 100644
--- a/core/java/android/webkit/WebSyncManager.java
+++ b/core/java/android/webkit/WebSyncManager.java
@@ -47,7 +47,7 @@
         @Override
         public void handleMessage(Message msg) {
             if (msg.what == SYNC_MESSAGE) {
-                if (WebView.LOGV_ENABLED) {
+                if (DebugFlags.WEB_SYNC_MANAGER) {
                     Log.v(LOGTAG, "*** WebSyncManager sync ***");
                 }
                 syncFromRamToFlash();
@@ -94,7 +94,7 @@
      * sync() forces sync manager to sync now
      */
     public void sync() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.WEB_SYNC_MANAGER) {
             Log.v(LOGTAG, "*** WebSyncManager sync ***");
         }
         if (mHandler == null) {
@@ -109,7 +109,7 @@
      * resetSync() resets sync manager's timer
      */
     public void resetSync() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.WEB_SYNC_MANAGER) {
             Log.v(LOGTAG, "*** WebSyncManager resetSync ***");
         }
         if (mHandler == null) {
@@ -124,7 +124,7 @@
      * startSync() requests sync manager to start sync
      */
     public void startSync() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.WEB_SYNC_MANAGER) {
             Log.v(LOGTAG, "***  WebSyncManager startSync ***, Ref count:" + 
                     mStartSyncRefCount);
         }
@@ -142,7 +142,7 @@
      * the queue to break the sync loop
      */
     public void stopSync() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.WEB_SYNC_MANAGER) {
             Log.v(LOGTAG, "*** WebSyncManager stopSync ***, Ref count:" + 
                     mStartSyncRefCount);
         }
diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/WebTextView.java
similarity index 74%
rename from core/java/android/webkit/TextDialog.java
rename to core/java/android/webkit/WebTextView.java
index 99de56d..25a20f2 100644
--- a/core/java/android/webkit/TextDialog.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -17,28 +17,21 @@
 package android.webkit;
 
 import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.RectShape;
 import android.text.Editable;
 import android.text.InputFilter;
 import android.text.Selection;
 import android.text.Spannable;
-import android.text.TextPaint;
 import android.text.TextUtils;
 import android.text.method.MovementMethod;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
+import android.util.Log;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputConnection;
 import android.widget.AbsoluteLayout.LayoutParams;
 import android.widget.ArrayAdapter;
 import android.widget.AutoCompleteTextView;
@@ -47,11 +40,13 @@
 import java.util.ArrayList;
 
 /**
- * TextDialog is a specialized version of EditText used by WebView
+ * WebTextView is a specialized version of EditText used by WebView
  * to overlay html textfields (and textareas) to use our standard
  * text editing.
  */
-/* package */ class TextDialog extends AutoCompleteTextView {
+/* package */ class WebTextView extends AutoCompleteTextView {
+
+    static final String LOGTAG = "webtextview";
 
     private WebView         mWebView;
     private boolean         mSingle;
@@ -62,9 +57,6 @@
     // on the enter key.  The method for blocking unmatched key ups prevents
     // the shift key from working properly.
     private boolean         mGotEnterDown;
-    // mScrollToAccommodateCursor being set to false prevents us from scrolling
-    // the cursor on screen when using the trackball to select a textfield.
-    private boolean         mScrollToAccommodateCursor;
     private int             mMaxLength;
     // Keep track of the text before the change so we know whether we actually
     // need to send down the DOM events.
@@ -79,39 +71,18 @@
     private static final InputFilter[] NO_FILTERS = new InputFilter[0];
 
     /**
-     * Create a new TextDialog.
-     * @param   context The Context for this TextDialog.
+     * Create a new WebTextView.
+     * @param   context The Context for this WebTextView.
      * @param   webView The WebView that created this.
      */
-    /* package */ TextDialog(Context context, WebView webView) {
+    /* package */ WebTextView(Context context, WebView webView) {
         super(context);
         mWebView = webView;
-        ShapeDrawable background = new ShapeDrawable(new RectShape());
-        Paint shapePaint = background.getPaint();
-        shapePaint.setStyle(Paint.Style.STROKE);
-        ColorDrawable color = new ColorDrawable(Color.WHITE);
-        Drawable[] array = new Drawable[2];
-        array[0] = color;
-        array[1] = background;
-        LayerDrawable layers = new LayerDrawable(array);
-        // Hide WebCore's text behind this and allow the WebView
-        // to draw its own focusring.
-        setBackgroundDrawable(layers);
-        // Align the text better with the text behind it, so moving
-        // off of the textfield will not appear to move the text.
-        setPadding(3, 2, 0, 0);
         mMaxLength = -1;
-        // Turn on subpixel text, and turn off kerning, so it better matches
-        // the text in webkit.
-        TextPaint paint = getPaint();
-        int flags = paint.getFlags() | Paint.SUBPIXEL_TEXT_FLAG |
-                Paint.ANTI_ALIAS_FLAG & ~Paint.DEV_KERN_TEXT_FLAG;
-        paint.setFlags(flags);
-        // Set the text color to black, regardless of the theme.  This ensures
-        // that other applications that use embedded WebViews will properly
-        // display the text in textfields.
-        setTextColor(Color.BLACK);
         setImeOptions(EditorInfo.IME_ACTION_NONE);
+        // Allow webkit's drawing to show through
+        setWillNotDraw(true);
+        setCursorVisible(false);
     }
 
     @Override
@@ -125,7 +96,7 @@
         Spannable text = (Spannable) getText();
         int oldLength = text.length();
         // Normally the delete key's dom events are sent via onTextChanged.
-        // However, if the length is zero, the text did not change, so we 
+        // However, if the length is zero, the text did not change, so we
         // go ahead and pass the key down immediately.
         if (KeyEvent.KEYCODE_DEL == keyCode && 0 == oldLength) {
             sendDomEvent(event);
@@ -151,6 +122,10 @@
             if (isPopupShowing()) {
                 return super.dispatchKeyEvent(event);
             }
+            if (!mWebView.nativeCursorMatchesFocus()) {
+                return down ? mWebView.onKeyDown(keyCode, event) : mWebView
+                        .onKeyUp(keyCode, event);
+            }
             // Center key should be passed to a potential onClick
             if (!down) {
                 mWebView.shortPressOnTextField();
@@ -158,6 +133,20 @@
             // Pass to super to handle longpress.
             return super.dispatchKeyEvent(event);
         }
+        boolean isArrowKey = false;
+        switch(keyCode) {
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                if (!mWebView.nativeCursorMatchesFocus()) {
+                    return down ? mWebView.onKeyDown(keyCode, event) : mWebView
+                            .onKeyUp(keyCode, event);
+
+                }
+                isArrowKey = true;
+                break;
+        }
 
         // Ensure there is a layout so arrow keys are handled properly.
         if (getLayout() == null) {
@@ -177,7 +166,7 @@
             oldText = "";
         }
         if (super.dispatchKeyEvent(event)) {
-            // If the TextDialog handled the key it was either an alphanumeric
+            // If the WebTextView handled the key it was either an alphanumeric
             // key, a delete, or a movement within the text. All of those are
             // ok to pass to javascript.
 
@@ -187,27 +176,15 @@
             // so do not pass down to javascript, and instead
             // return true.  If it is an arrow key or a delete key, we can go
             // ahead and pass it down.
-            boolean isArrowKey;
-            switch(keyCode) {
-                case KeyEvent.KEYCODE_DPAD_LEFT:
-                case KeyEvent.KEYCODE_DPAD_RIGHT:
-                case KeyEvent.KEYCODE_DPAD_UP:
-                case KeyEvent.KEYCODE_DPAD_DOWN:
-                    isArrowKey = true;
-                    break;
-                case KeyEvent.KEYCODE_ENTER:
-                    // For multi-line text boxes, newlines will
-                    // trigger onTextChanged for key down (which will send both
-                    // key up and key down) but not key up.
-                    mGotEnterDown = true;
-                default:
-                    isArrowKey = false;
-                    break;
+            if (KeyEvent.KEYCODE_ENTER == keyCode) {
+                // For multi-line text boxes, newlines will
+                // trigger onTextChanged for key down (which will send both
+                // key up and key down) but not key up.
+                mGotEnterDown = true;
             }
             if (maxedOut && !isArrowKey && keyCode != KeyEvent.KEYCODE_DEL) {
                 if (oldEnd == oldStart) {
                     // Return true so the key gets dropped.
-                    mScrollToAccommodateCursor = true;
                     return true;
                 } else if (!oldText.equals(getText().toString())) {
                     // FIXME: This makes the text work properly, but it
@@ -221,36 +198,33 @@
                     int newEnd = Selection.getSelectionEnd(span);
                     mWebView.replaceTextfieldText(0, oldLength, span.toString(),
                             newStart, newEnd);
-                    mScrollToAccommodateCursor = true;
                     return true;
                 }
             }
+            /* FIXME:
+             * In theory, we would like to send the events for the arrow keys.
+             * However, the TextView can arbitrarily change the selection (i.e.
+             * long press followed by using the trackball).  Therefore, we keep
+             * in sync with the TextView via onSelectionChanged.  If we also
+             * send the DOM event, we lose the correct selection.
             if (isArrowKey) {
                 // Arrow key does not change the text, but we still want to send
                 // the DOM events.
                 sendDomEvent(event);
             }
-            mScrollToAccommodateCursor = true;
+             */
             return true;
         }
-        // FIXME: TextViews return false for up and down key events even though
-        // they change the selection. Since we don't want the get out of sync
-        // with WebCore's notion of the current selection, reset the selection
-        // to what it was before the key event.
-        Selection.setSelection(text, oldStart, oldEnd);
         // Ignore the key up event for newlines. This prevents
         // multiple newlines in the native textarea.
         if (mGotEnterDown && !down) {
             return true;
         }
         // if it is a navigation key, pass it to WebView
-        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
-                || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
-                || keyCode == KeyEvent.KEYCODE_DPAD_UP
-                || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+        if (isArrowKey) {
             // WebView check the trackballtime in onKeyDown to avoid calling
-            // native from both trackball and key handling. As this is called 
-            // from TextDialog, we always want WebView to check with native. 
+            // native from both trackball and key handling. As this is called
+            // from WebTextView, we always want WebView to check with native.
             // Reset trackballtime to ensure it.
             mWebView.resetTrackballTime();
             return down ? mWebView.onKeyDown(keyCode, event) : mWebView
@@ -260,9 +234,9 @@
     }
 
     /**
-     *  Create a fake touch up event at (x,y) with respect to this TextDialog.
+     *  Create a fake touch up event at (x,y) with respect to this WebTextView.
      *  This is used by WebView to act as though a touch event which happened
-     *  before we placed the TextDialog actually hit it, so that it can place
+     *  before we placed the WebTextView actually hit it, so that it can place
      *  the cursor accordingly.
      */
     /* package */ void fakeTouchEvent(float x, float y) {
@@ -279,31 +253,46 @@
     }
 
     /**
-     *  Determine whether this TextDialog currently represents the node
+     *  Determine whether this WebTextView currently represents the node
      *  represented by ptr.
      *  @param  ptr Pointer to a node to compare to.
-     *  @return boolean Whether this TextDialog already represents the node
+     *  @return boolean Whether this WebTextView already represents the node
      *          pointed to by ptr.
      */
     /* package */ boolean isSameTextField(int ptr) {
         return ptr == mNodePointer;
     }
 
-    @Override
-    public boolean onPreDraw() {
-        if (getLayout() == null) {
-            measure(mWidthSpec, mHeightSpec);
+    @Override public InputConnection onCreateInputConnection(
+            EditorInfo outAttrs) {
+        InputConnection connection = super.onCreateInputConnection(outAttrs);
+        if (mWebView != null) {
+            // Use the name of the textfield + the url.  Use backslash as an
+            // arbitrary separator.
+            outAttrs.fieldName = mWebView.nativeFocusCandidateName() + "\\"
+                    + mWebView.getUrl();
         }
-        return super.onPreDraw();
+        return connection;
     }
-    
+
+    @Override
+    protected void onSelectionChanged(int selStart, int selEnd) {
+        if (mWebView != null) {
+            if (DebugFlags.WEB_TEXT_VIEW) {
+                Log.v(LOGTAG, "onSelectionChanged selStart=" + selStart
+                        + " selEnd=" + selEnd);
+            }
+            mWebView.setSelection(selStart, selEnd);
+        }
+    }
+
     @Override
     protected void onTextChanged(CharSequence s,int start,int before,int count){
         super.onTextChanged(s, start, before, count);
         String postChange = s.toString();
         // Prevent calls to setText from invoking onTextChanged (since this will
         // mean we are on a different textfield).  Also prevent the change when
-        // going from a textfield with a string of text to one with a smaller 
+        // going from a textfield with a string of text to one with a smaller
         // limit on text length from registering the onTextChanged event.
         if (mPreChange == null || mPreChange.equals(postChange) ||
                 (mMaxLength > -1 && mPreChange.length() > mMaxLength &&
@@ -311,8 +300,7 @@
             return;
         }
         mPreChange = postChange;
-        // This was simply a delete or a cut, so just delete the 
-        // selection.
+        // This was simply a delete or a cut, so just delete the selection.
         if (before > 0 && 0 == count) {
             mWebView.deleteSelection(start, start + before);
             // For this and all changes to the text, update our cache
@@ -337,24 +325,27 @@
                     start + count - charactersFromKeyEvents,
                     start + count - charactersFromKeyEvents);
         } else {
-            // This corrects the selection which may have been affected by the 
+            // This corrects the selection which may have been affected by the
             // trackball or auto-correct.
+            if (DebugFlags.WEB_TEXT_VIEW) {
+                Log.v(LOGTAG, "onTextChanged start=" + start
+                        + " start + before=" + (start + before));
+            }
             mWebView.setSelection(start, start + before);
         }
-        updateCachedTextfield();
-        if (cannotUseKeyEvents) {
-            return;
-        }
-        int length = events.length;
-        for (int i = 0; i < length; i++) {
-            // We never send modifier keys to native code so don't send them
-            // here either.
-            if (!KeyEvent.isModifierKey(events[i].getKeyCode())) {
-                sendDomEvent(events[i]);
+        if (!cannotUseKeyEvents) {
+            int length = events.length;
+            for (int i = 0; i < length; i++) {
+                // We never send modifier keys to native code so don't send them
+                // here either.
+                if (!KeyEvent.isModifierKey(events[i].getKeyCode())) {
+                    sendDomEvent(events[i]);
+                }
             }
         }
+        updateCachedTextfield();
     }
-    
+
     @Override
     public boolean onTrackballEvent(MotionEvent event) {
         if (isPopupShowing()) {
@@ -363,29 +354,23 @@
         if (event.getAction() != MotionEvent.ACTION_MOVE) {
             return false;
         }
+        // If the Cursor is not on the text input, webview should handle the
+        // trackball
+        if (!mWebView.nativeCursorMatchesFocus()) {
+            return mWebView.onTrackballEvent(event);
+        }
         Spannable text = (Spannable) getText();
         MovementMethod move = getMovementMethod();
         if (move != null && getLayout() != null &&
             move.onTrackballEvent(this, text, event)) {
-            // Need to pass down the selection, which has changed.
-            // FIXME: This should work, but does not, so we set the selection
-            // in onTextChanged.
-            //int start = Selection.getSelectionStart(text);
-            //int end = Selection.getSelectionEnd(text);
-            //mWebView.setSelection(start, end);
+            // Selection is changed in onSelectionChanged
             return true;
         }
-        // If the user is in a textfield, and the movement method is not
-        // handling the trackball events, it means they are at the end of the
-        // field and continuing to move the trackball.  In this case, we should
-        // not scroll the cursor on screen bc the user may be attempting to
-        // scroll the page, possibly in the opposite direction of the cursor.
-        mScrollToAccommodateCursor = false;
         return false;
     }
 
     /**
-     * Remove this TextDialog from its host WebView, and return
+     * Remove this WebTextView from its host WebView, and return
      * focus to the host.
      */
     /* package */ void remove() {
@@ -394,11 +379,6 @@
                 getWindowToken(), 0);
         mWebView.removeView(this);
         mWebView.requestFocus();
-        mScrollToAccommodateCursor = false;
-    }
-
-    /* package */ void enableScrollOnScreen(boolean enable) {
-        mScrollToAccommodateCursor = enable;
     }
 
     /* package */ void bringIntoView() {
@@ -407,14 +387,6 @@
         }
     }
 
-    @Override
-    public boolean requestRectangleOnScreen(Rect rectangle) {
-        if (mScrollToAccommodateCursor) {
-            return super.requestRectangleOnScreen(rectangle);
-        }
-        return false;
-    }
-    
     /**
      *  Send the DOM events for the specified event.
      *  @param event    KeyEvent to be translated into a DOM event.
@@ -425,7 +397,7 @@
 
     /**
      *  Always use this instead of setAdapter, as this has features specific to
-     *  the TextDialog.
+     *  the WebTextView.
      */
     public void setAdapterCustom(AutoCompleteAdapter adapter) {
         if (adapter != null) {
@@ -491,16 +463,16 @@
 
     /**
      *  Set the pointer for this node so it can be determined which node this
-     *  TextDialog represents.
+     *  WebTextView represents.
      *  @param  ptr Integer representing the pointer to the node which this
-     *          TextDialog represents.
+     *          WebTextView represents.
      */
     /* package */ void setNodePointer(int ptr) {
         mNodePointer = ptr;
     }
 
     /**
-     * Determine the position and size of TextDialog, and add it to the
+     * Determine the position and size of WebTextView, and add it to the
      * WebView's view heirarchy.  All parameters are presumed to be in
      * view coordinates.  Also requests Focus and sets the cursor to not
      * request to be in view.
@@ -551,8 +523,8 @@
     }
 
     /**
-     * Set the text for this TextDialog, and set the selection to (start, end)
-     * @param   text    Text to go into this TextDialog.
+     * Set the text for this WebTextView, and set the selection to (start, end)
+     * @param   text    Text to go into this WebTextView.
      * @param   start   Beginning of the selection.
      * @param   end     End of the selection.
      */
@@ -569,6 +541,10 @@
         } else if (start > length) {
             start = length;
         }
+        if (DebugFlags.WEB_TEXT_VIEW) {
+            Log.v(LOGTAG, "setText start=" + start
+                    + " end=" + end);
+        }
         Selection.setSelection(span, start, end);
     }
 
@@ -583,7 +559,7 @@
         edit.replace(0, edit.length(), text);
         updateCachedTextfield();
     }
-    
+
     /**
      *  Update the cache to reflect the current text.
      */
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 429f0f9..e061a4c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -58,7 +58,7 @@
 import android.view.ViewTreeObserver;
 import android.view.animation.AlphaAnimation;
 import android.view.inputmethod.InputMethodManager;
-import android.webkit.TextDialog.AutoCompleteAdapter;
+import android.webkit.WebTextView.AutoCompleteAdapter;
 import android.webkit.WebViewCore.EventHub;
 import android.widget.AbsoluteLayout;
 import android.widget.Adapter;
@@ -80,11 +80,10 @@
 import java.io.IOException;
 import java.net.URLDecoder;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 /**
- * <p>A View that displays web pages. This class is the basis upon which you 
+ * <p>A View that displays web pages. This class is the basis upon which you
  * can roll your own web browser or simply display some online content within your Activity.
  * It uses the WebKit rendering engine to display
  * web pages and includes methods to navigate forward and backward
@@ -93,12 +92,109 @@
  * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
  * (introduced in API version 3).
  * <p>Note that, in order for your Activity to access the Internet and load web pages
- * in a WebView, you must add the <var>INTERNET</var> permissions to your 
+ * in a WebView, you must add the <var>INTERNET</var> permissions to your
  * Android Manifest file:</p>
  * <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
+ *
  * <p>This must be a child of the <code>&lt;manifest></code> element.</p>
+ *
+ * <h3>Basic usage</h3>
+ *
+ * <p>By default, a WebView provides no browser-like widgets, does not
+ * enable JavaScript and errors will be ignored. If your goal is only
+ * to display some HTML as a part of your UI, this is probably fine;
+ * the user won't need to interact with the web page beyond reading
+ * it, and the web page won't need to interact with the user. If you
+ * actually want a fully blown web browser, then you probably want to
+ * invoke the Browser application with your URL rather than show it
+ * with a WebView. See {@link android.content.Intent} for more information.</p>
+ *
+ * <pre class="prettyprint">
+ * WebView webview = new WebView(this);
+ * setContentView(webview);
+ *
+ * // Simplest usage: note that an exception will NOT be thrown
+ * // if there is an error loading this page (see below).
+ * webview.loadUrl("http://slashdot.org/");
+ *
+ * // Of course you can also load from any string:
+ * String summary = "&lt;html>&lt;body>You scored &lt;b>192</b> points.&lt;/body>&lt;/html>";
+ * webview.loadData(summary, "text/html", "utf-8");
+ * // ... although note that there are restrictions on what this HTML can do.
+ * // See the JavaDocs for loadData and loadDataWithBaseUrl for more info.
+ * </pre>
+ *
+ * <p>A WebView has several customization points where you can add your
+ * own behavior. These are:</p>
+ *
+ * <ul>
+ *   <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
+ *       This class is called when something that might impact a
+ *       browser UI happens, for instance, progress updates and
+ *       JavaScript alerts are sent here.
+ *   </li>
+ *   <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
+ *       It will be called when things happen that impact the
+ *       rendering of the content, eg, errors or form submissions. You
+ *       can also intercept URL loading here.</li>
+ *   <li>Via the {@link android.webkit.WebSettings} class, which contains
+ *       miscellaneous configuration. </li>
+ *   <li>With the {@link android.webkit.WebView#addJavascriptInterface} method.
+ *       This lets you bind Java objects into the WebView so they can be
+ *       controlled from the web pages JavaScript.</li>
+ * </ul>
+ *
+ * <p>Here's a more complicated example, showing error handling,
+ *    settings, and progress notification:</p>
+ *
+ * <pre class="prettyprint">
+ * // Let's display the progress in the activity title bar, like the
+ * // browser app does.
+ * getWindow().requestFeature(Window.FEATURE_PROGRESS);
+ *
+ * webview.getSettings().setJavaScriptEnabled(true);
+ *
+ * final Activity activity = this;
+ * webview.setWebChromeClient(new WebChromeClient() {
+ *   public void onProgressChanged(WebView view, int progress) {
+ *     // Activities and WebViews measure progress with different scales.
+ *     // The progress meter will automatically disappear when we reach 100%
+ *     activity.setProgress(progress * 1000);
+ *   }
+ * });
+ * webview.setWebViewClient(new WebViewClient() {
+ *   public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
+ *     Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
+ *   }
+ * });
+ *
+ * webview.loadUrl("http://slashdot.org/");
+ * </pre>
+ *
+ * <h3>Cookie and window management</h3>
+ *
+ * <p>For obvious security reasons, your application has its own
+ * cache, cookie store etc - it does not share the Browser
+ * applications data. Cookies are managed on a separate thread, so
+ * operations like index building don't block the UI
+ * thread. Follow the instructions in {@link android.webkit.CookieSyncManager}
+ * if you want to use cookies in your application.
+ * </p>
+ *
+ * <p>By default, requests by the HTML to open new windows are
+ * ignored. This is true whether they be opened by JavaScript or by
+ * the target attribute on a link. You can customize your
+ * WebChromeClient to provide your own behaviour for opening multiple windows,
+ * and render them in whatever manner you want.</p>
+ *
+ * <p>Standard behavior for an Activity is to be destroyed and
+ * recreated when the devices orientation is changed. This will cause
+ * the WebView to reload the current page. If you don't want that, you
+ * can set your Activity to handle the orientation and keyboardHidden
+ * changes, and then just leave the WebView alone. It'll automatically
+ * re-orient itself as appropriate.</p>
  */
-public class WebView extends AbsoluteLayout 
+public class WebView extends AbsoluteLayout
         implements ViewTreeObserver.OnGlobalFocusChangeListener,
         ViewGroup.OnHierarchyChangeListener {
 
@@ -108,62 +204,66 @@
     // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
     private boolean mAutoRedraw;
 
-    // keep debugging parameters near the top of the file
     static final String LOGTAG = "webview";
-    static final boolean DEBUG = false;
-    static final boolean LOGV_ENABLED = DEBUG;
 
-    private class ExtendedZoomControls extends FrameLayout {
+    static class ScaleLimitData {
+        int mMinScale;
+        int mMaxScale;
+    }
+
+    private static class ExtendedZoomControls extends FrameLayout {
         public ExtendedZoomControls(Context context, AttributeSet attrs) {
             super(context, attrs);
             LayoutInflater inflater = (LayoutInflater)
                     context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
             inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true);
-            mZoomControls = (ZoomControls) findViewById(com.android.internal.R.id.zoomControls);
+            mPlusMinusZoomControls = (ZoomControls) findViewById(
+                    com.android.internal.R.id.zoomControls);
             mZoomMagnify = (ImageView) findViewById(com.android.internal.R.id.zoomMagnify);
         }
-        
+
         public void show(boolean showZoom, boolean canZoomOut) {
-            mZoomControls.setVisibility(showZoom ? View.VISIBLE : View.GONE);
+            mPlusMinusZoomControls.setVisibility(
+                    showZoom ? View.VISIBLE : View.GONE);
             mZoomMagnify.setVisibility(canZoomOut ? View.VISIBLE : View.GONE);
             fade(View.VISIBLE, 0.0f, 1.0f);
         }
-        
+
         public void hide() {
             fade(View.GONE, 1.0f, 0.0f);
         }
-        
+
         private void fade(int visibility, float startAlpha, float endAlpha) {
             AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
             anim.setDuration(500);
             startAnimation(anim);
             setVisibility(visibility);
         }
-        
+
         public void setIsZoomMagnifyEnabled(boolean isEnabled) {
             mZoomMagnify.setEnabled(isEnabled);
         }
-        
+
         public boolean hasFocus() {
-            return mZoomControls.hasFocus() || mZoomMagnify.hasFocus();
+            return mPlusMinusZoomControls.hasFocus() || mZoomMagnify.hasFocus();
         }
-        
+
         public void setOnZoomInClickListener(OnClickListener listener) {
-            mZoomControls.setOnZoomInClickListener(listener);
+            mPlusMinusZoomControls.setOnZoomInClickListener(listener);
         }
-            
+
         public void setOnZoomOutClickListener(OnClickListener listener) {
-            mZoomControls.setOnZoomOutClickListener(listener);
+            mPlusMinusZoomControls.setOnZoomOutClickListener(listener);
         }
-            
+
         public void setOnZoomMagnifyClickListener(OnClickListener listener) {
             mZoomMagnify.setOnClickListener(listener);
         }
 
-        ZoomControls mZoomControls;
-        ImageView mZoomMagnify;
+        ZoomControls    mPlusMinusZoomControls;
+        ImageView       mZoomMagnify;
     }
-    
+
     /**
      *  Transportation object for returning WebView across thread boundaries.
      */
@@ -203,7 +303,7 @@
     private WebViewCore mWebViewCore;
     // Handler for dispatching UI messages.
     /* package */ final Handler mPrivateHandler = new PrivateHandler();
-    private TextDialog mTextEntry;
+    private WebTextView mWebTextView;
     // Used to ignore changes to webkit text that arrives to the UI side after
     // more key events.
     private int mTextGeneration;
@@ -229,9 +329,18 @@
 
     /**
      * The minimum elapsed time before sending another ACTION_MOVE event to
-     * WebViewCore
+     * WebViewCore. This really should be tuned for each type of the devices.
+     * For example in Google Map api test case, it takes Dream device at least
+     * 150ms to do a full cycle in the WebViewCore by processing a touch event,
+     * triggering the layout and drawing the picture. While the same process
+     * takes 60+ms on the current high speed device. If we make
+     * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
+     * to WebViewCore queue and the real layout and draw events will be pushed
+     * to further, which slows down the refresh rate. Choose 50 to favor the
+     * current high speed devices. For Dream like devices, 100 is a better
+     * choice. Maybe make this in the buildspec later.
      */
-    private static final int TOUCH_SENT_INTERVAL = 100;
+    private static final int TOUCH_SENT_INTERVAL = 50;
 
     /**
      * Helper class to get velocity for fling
@@ -266,12 +375,11 @@
     // take control of touch events unless it says no for touch down event.
     private boolean mPreventDrag;
 
-    // If updateTextEntry gets called while we are out of focus, use this 
-    // variable to remember to do it next time we gain focus.
-    private boolean mNeedsUpdateTextEntry = false;
-    
-    // Whether or not to draw the focus ring.
-    private boolean mDrawFocusRing = true;
+    // Whether or not to draw the cursor ring.
+    private boolean mDrawCursorRing = true;
+
+    // true if onPause has been called (and not onResume)
+    private boolean mIsPaused;
 
     /**
      * Customizable constant
@@ -291,7 +399,7 @@
     // needed to avoid flinging after a pause of no movement
     private static final int MIN_FLING_TIME = 250;
     // The time that the Zoom Controls are visible before fading away
-    private static final long ZOOM_CONTROLS_TIMEOUT = 
+    private static final long ZOOM_CONTROLS_TIMEOUT =
             ViewConfiguration.getZoomControlsTimeout();
     // The amount of content to overlap between two screens when going through
     // pages with the space bar, in pixels.
@@ -312,7 +420,7 @@
     private int mContentWidth;   // cache of value from WebViewCore
     private int mContentHeight;  // cache of value from WebViewCore
 
-    // Need to have the separate control for horizontal and vertical scrollbar 
+    // Need to have the separate control for horizontal and vertical scrollbar
     // style than the View's single scrollbar style
     private boolean mOverlayHorizontalScrollbar = true;
     private boolean mOverlayVerticalScrollbar = false;
@@ -327,51 +435,49 @@
 
     private boolean mWrapContent;
 
-    // true if we should call webcore to draw the content, false means we have
-    // requested something but it isn't ready to draw yet.
-    private WebViewCore.FocusData mFocusData;
     /**
      * Private message ids
      */
-    private static final int REMEMBER_PASSWORD = 1;
-    private static final int NEVER_REMEMBER_PASSWORD = 2;
-    private static final int SWITCH_TO_SHORTPRESS = 3;
-    private static final int SWITCH_TO_LONGPRESS = 4;
-    private static final int UPDATE_TEXT_ENTRY_ADAPTER = 6;
-    private static final int SWITCH_TO_ENTER = 7;
-    private static final int RESUME_WEBCORE_UPDATE = 8;
+    private static final int REMEMBER_PASSWORD          = 1;
+    private static final int NEVER_REMEMBER_PASSWORD    = 2;
+    private static final int SWITCH_TO_SHORTPRESS       = 3;
+    private static final int SWITCH_TO_LONGPRESS        = 4;
+    private static final int REQUEST_FORM_DATA          = 6;
+    private static final int SWITCH_TO_CLICK            = 7;
+    private static final int RESUME_WEBCORE_UPDATE      = 8;
 
     //! arg1=x, arg2=y
-    static final int SCROLL_TO_MSG_ID               = 10;
-    static final int SCROLL_BY_MSG_ID               = 11;
+    static final int SCROLL_TO_MSG_ID                   = 10;
+    static final int SCROLL_BY_MSG_ID                   = 11;
     //! arg1=x, arg2=y
-    static final int SPAWN_SCROLL_TO_MSG_ID         = 12;
+    static final int SPAWN_SCROLL_TO_MSG_ID             = 12;
     //! arg1=x, arg2=y
-    static final int SYNC_SCROLL_TO_MSG_ID          = 13;
-    static final int NEW_PICTURE_MSG_ID             = 14;
-    static final int UPDATE_TEXT_ENTRY_MSG_ID       = 15;
-    static final int WEBCORE_INITIALIZED_MSG_ID     = 16;
-    static final int UPDATE_TEXTFIELD_TEXT_MSG_ID   = 17;
-    static final int DID_FIRST_LAYOUT_MSG_ID        = 18;
-    static final int RECOMPUTE_FOCUS_MSG_ID         = 19;
-    static final int NOTIFY_FOCUS_SET_MSG_ID        = 20;
-    static final int MARK_NODE_INVALID_ID           = 21;
-    static final int UPDATE_CLIPBOARD               = 22;
-    static final int LONG_PRESS_ENTER               = 23;
-    static final int PREVENT_TOUCH_ID               = 24;
-    static final int WEBCORE_NEED_TOUCH_EVENTS      = 25;
+    static final int SYNC_SCROLL_TO_MSG_ID              = 13;
+    static final int NEW_PICTURE_MSG_ID                 = 14;
+    static final int UPDATE_TEXT_ENTRY_MSG_ID           = 15;
+    static final int WEBCORE_INITIALIZED_MSG_ID         = 16;
+    static final int UPDATE_TEXTFIELD_TEXT_MSG_ID       = 17;
+    static final int DID_FIRST_LAYOUT_MSG_ID            = 18;
+    static final int MOVE_OUT_OF_PLUGIN                 = 19;
+    static final int CLEAR_TEXT_ENTRY                   = 20;
+
+    static final int UPDATE_CLIPBOARD                   = 22;
+    static final int LONG_PRESS_CENTER                  = 23;
+    static final int PREVENT_TOUCH_ID                   = 24;
+    static final int WEBCORE_NEED_TOUCH_EVENTS          = 25;
     // obj=Rect in doc coordinates
-    static final int INVAL_RECT_MSG_ID              = 26;
-    
+    static final int INVAL_RECT_MSG_ID                  = 26;
+    static final int REQUEST_KEYBOARD                   = 27;
+
     static final String[] HandlerDebugString = {
-        "REMEMBER_PASSWORD", // = 1;
-        "NEVER_REMEMBER_PASSWORD", // = 2;
-        "SWITCH_TO_SHORTPRESS", // = 3;
-        "SWITCH_TO_LONGPRESS", // = 4;
+        "REMEMBER_PASSWORD", //              = 1;
+        "NEVER_REMEMBER_PASSWORD", //        = 2;
+        "SWITCH_TO_SHORTPRESS", //           = 3;
+        "SWITCH_TO_LONGPRESS", //            = 4;
         "5",
-        "UPDATE_TEXT_ENTRY_ADAPTER", // = 6;
-        "SWITCH_TO_ENTER", // = 7;
-        "RESUME_WEBCORE_UPDATE", // = 8;
+        "REQUEST_FORM_DATA", //              = 6;
+        "SWITCH_TO_CLICK", //                = 7;
+        "RESUME_WEBCORE_UPDATE", //          = 8;
         "9",
         "SCROLL_TO_MSG_ID", //               = 10;
         "SCROLL_BY_MSG_ID", //               = 11;
@@ -382,14 +488,15 @@
         "WEBCORE_INITIALIZED_MSG_ID", //     = 16;
         "UPDATE_TEXTFIELD_TEXT_MSG_ID", //   = 17;
         "DID_FIRST_LAYOUT_MSG_ID", //        = 18;
-        "RECOMPUTE_FOCUS_MSG_ID", //         = 19;
-        "NOTIFY_FOCUS_SET_MSG_ID", //        = 20;
-        "MARK_NODE_INVALID_ID", //           = 21;
+        "MOVE_OUT_OF_PLUGIN", //             = 19;
+        "CLEAR_TEXT_ENTRY", //               = 20;
+        "21", //                             = 21;
         "UPDATE_CLIPBOARD", //               = 22;
-        "LONG_PRESS_ENTER", //               = 23;
+        "LONG_PRESS_CENTER", //              = 23;
         "PREVENT_TOUCH_ID", //               = 24;
         "WEBCORE_NEED_TOUCH_EVENTS", //      = 25;
-        "INVAL_RECT_MSG_ID" //               = 26;
+        "INVAL_RECT_MSG_ID", //              = 26;
+        "REQUEST_KEYBOARD" //                = 27;
     };
 
     // width which view is considered to be fully zoomed out
@@ -432,7 +539,7 @@
     private static final int SNAP_X_LOCK = 4;
     private static final int SNAP_Y_LOCK = 5;
     private boolean mSnapPositive;
-    
+
     // Used to match key downs and key ups
     private boolean mGotKeyDown;
 
@@ -455,7 +562,7 @@
      * URI scheme for map address
      */
     public static final String SCHEME_GEO = "geo:0,0?q=";
-    
+
     private int mBackgroundColor = Color.WHITE;
 
     // Used to notify listeners of a new picture.
@@ -472,7 +579,8 @@
         public void onNewPicture(WebView view, Picture picture);
     }
 
-    public class HitTestResult {
+    // FIXME: Want to make this public, but need to change the API file.
+    public /*static*/ class HitTestResult {
         /**
          * Default HitTestResult, where the target is unknown
          */
@@ -542,9 +650,7 @@
     private ExtendedZoomControls mZoomControls;
     private Runnable mZoomControlRunnable;
 
-    private ZoomButtonsController mZoomButtonsController; 
-    private ImageView mZoomOverviewButton;
-    private ImageView mZoomFitPageButton;
+    private ZoomButtonsController mZoomButtonsController;
 
     // These keep track of the center point of the zoom.  They are used to
     // determine the point around which we should zoom.
@@ -567,11 +673,11 @@
             } else {
                 zoomOut();
             }
-            
+
             updateZoomButtonsEnabled();
         }
     };
-    
+
     /**
      * Construct a new WebView with a Context object.
      * @param context A Context object used to access application assets.
@@ -602,48 +708,20 @@
         mCallbackProxy = new CallbackProxy(context, this);
         mWebViewCore = new WebViewCore(context, this, mCallbackProxy);
         mDatabase = WebViewDatabase.getInstance(context);
-        mFocusData = new WebViewCore.FocusData();
-        mFocusData.mFrame = 0;
-        mFocusData.mNode = 0;
-        mFocusData.mX = 0;
-        mFocusData.mY = 0;
         mScroller = new Scroller(context);
 
-        initZoomController(context);
-    }
-
-    private void initZoomController(Context context) {
-        // Create the buttons controller
         mZoomButtonsController = new ZoomButtonsController(this);
         mZoomButtonsController.setOnZoomListener(mZoomListener);
-
-        // Create the accessory buttons
-        LayoutInflater inflater =
-                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        ViewGroup container = mZoomButtonsController.getContainer();
-        inflater.inflate(com.android.internal.R.layout.zoom_browser_accessory_buttons, container);
-        mZoomOverviewButton =
-                (ImageView) container.findViewById(com.android.internal.R.id.zoom_page_overview);
-        mZoomOverviewButton.setOnClickListener(
-            new View.OnClickListener() {
-                public void onClick(View v) {
-                    mZoomButtonsController.setVisible(false);
-                    zoomScrollOut();
-                    if (mLogEvent) {
-                        Checkin.updateStats(mContext.getContentResolver(),
-                                Checkin.Stats.Tag.BROWSER_ZOOM_OVERVIEW, 1, 0.0);
-                    }
-                }
-            });
-        mZoomFitPageButton =
-                (ImageView) container.findViewById(com.android.internal.R.id.zoom_fit_page);
-        mZoomFitPageButton.setOnClickListener(
-            new View.OnClickListener() {
-                public void onClick(View v) {
-                    zoomWithPreview(mDefaultScale);
-                    updateZoomButtonsEnabled();
-                }
-            });
+        // ZoomButtonsController positions the buttons at the bottom, but in
+        // the middle.  Change their layout parameters so they appear on the
+        // right.
+        View controls = mZoomButtonsController.getZoomControls();
+        ViewGroup.LayoutParams params = controls.getLayoutParams();
+        if (params instanceof FrameLayout.LayoutParams) {
+            FrameLayout.LayoutParams frameParams = (FrameLayout.LayoutParams)
+                    params;
+            frameParams.gravity = Gravity.RIGHT;
+        }
     }
 
     private void updateZoomButtonsEnabled() {
@@ -653,20 +731,15 @@
             // Hide the zoom in and out buttons, as well as the fit to page
             // button, if the page cannot zoom
             mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
-            mZoomFitPageButton.setVisibility(View.GONE);
         } else {
             // Bring back the hidden zoom controls.
             mZoomButtonsController.getZoomControls()
                     .setVisibility(View.VISIBLE);
-            mZoomFitPageButton.setVisibility(View.VISIBLE);
             // Set each one individually, as a page may be able to zoom in
             // or out.
             mZoomButtonsController.setZoomInEnabled(canZoomIn);
             mZoomButtonsController.setZoomOutEnabled(canZoomOut);
-            mZoomFitPageButton.setEnabled(mActualScale != mDefaultScale);
         }
-        mZoomOverviewButton.setVisibility(canZoomScrollOut() ? View.VISIBLE:
-                View.GONE);
     }
 
     private void init() {
@@ -899,7 +972,7 @@
         clearTextEntry();
         if (mWebViewCore != null) {
             // Set the handlers to null before destroying WebViewCore so no
-            // more messages will be posted. 
+            // more messages will be posted.
             mCallbackProxy.setWebViewClient(null);
             mCallbackProxy.setWebChromeClient(null);
             // Tell WebViewCore to destroy itself
@@ -930,12 +1003,23 @@
 
     /**
      * If platform notifications are enabled, this should be called
-     * from onPause() or onStop().
+     * from the Activity's onPause() or onStop().
      */
     public static void disablePlatformNotifications() {
         Network.disablePlatformNotifications();
     }
-    
+
+    /**
+     * Sets JavaScript engine flags.
+     *
+     * @param flags JS engine flags in a String
+     *
+     * @hide pending API solidification
+     */
+    public void setJsFlags(String flags) {
+        mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
+    }
+
     /**
      * Inform WebView of the network state. This is used to set
      * the javascript property window.navigator.isOnline and
@@ -948,7 +1032,7 @@
     }
 
     /**
-     * Save the state of this WebView used in 
+     * Save the state of this WebView used in
      * {@link android.app.Activity#onSaveInstanceState}. Please note that this
      * method no longer stores the display data for this WebView. The previous
      * behavior could potentially leak files if {@link #restoreState} was never
@@ -1079,10 +1163,10 @@
 
     /**
      * Restore the state of this WebView from the given map used in
-     * {@link android.app.Activity#onRestoreInstanceState}. This method should 
-     * be called to restore the state of the WebView before using the object. If 
-     * it is called after the WebView has had a chance to build state (load 
-     * pages, create a back/forward list, etc.) there may be undesirable 
+     * {@link android.app.Activity#onRestoreInstanceState}. This method should
+     * be called to restore the state of the WebView before using the object. If
+     * it is called after the WebView has had a chance to build state (load
+     * pages, create a back/forward list, etc.) there may be undesirable
      * side-effects. Please note that this method no longer restores the
      * display data for this WebView. See {@link #savePicture} and {@link
      * #restorePicture} for saving and restoring the display data.
@@ -1152,18 +1236,18 @@
      * Load the url with postData using "POST" method into the WebView. If url
      * is not a network url, it will be loaded with {link
      * {@link #loadUrl(String)} instead.
-     * 
+     *
      * @param url The url of the resource to load.
      * @param postData The data will be passed to "POST" request.
-     * 
+     *
      * @hide pending API solidification
      */
     public void postUrl(String url, byte[] postData) {
         if (URLUtil.isNetworkUrl(url)) {
             switchOutDrawHistory();
-            HashMap arg = new HashMap();
-            arg.put("url", url);
-            arg.put("data", postData);
+            WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
+            arg.mUrl = url;
+            arg.mPostData = postData;
             mWebViewCore.sendMessage(EventHub.POST_URL, arg);
             clearTextEntry();
         } else {
@@ -1197,7 +1281,7 @@
      * able to access asset files. If the baseUrl is anything other than
      * http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
      * sub resources.
-     * 
+     *
      * @param baseUrl Url to resolve relative paths with, if null defaults to
      *            "about:blank"
      * @param data A String of data in the given encoding.
@@ -1208,18 +1292,18 @@
      */
     public void loadDataWithBaseURL(String baseUrl, String data,
             String mimeType, String encoding, String failUrl) {
-        
+
         if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
             loadData(data, mimeType, encoding);
             return;
         }
         switchOutDrawHistory();
-        HashMap arg = new HashMap();
-        arg.put("baseUrl", baseUrl);
-        arg.put("data", data);
-        arg.put("mimeType", mimeType);
-        arg.put("encoding", encoding);
-        arg.put("failUrl", failUrl);
+        WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
+        arg.mBaseUrl = baseUrl;
+        arg.mData = data;
+        arg.mMimeType = mimeType;
+        arg.mEncoding = encoding;
+        arg.mFailUrl = failUrl;
         mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
         clearTextEntry();
     }
@@ -1329,7 +1413,7 @@
                     ignoreSnapshot ? 1 : 0);
         }
     }
-    
+
     private boolean extendScroll(int y) {
         int finalY = mScroller.getFinalY();
         int newY = pinLocY(finalY + y);
@@ -1338,7 +1422,7 @@
         mScroller.extendDuration(computeDuration(0, y));
         return true;
     }
-    
+
     /**
      * Scroll the contents of the view up by half the view size
      * @param top true to jump to the top of the page
@@ -1348,7 +1432,7 @@
         if (mNativeClass == 0) {
             return false;
         }
-        nativeClearFocus(-1, -1);
+        nativeClearCursor(); // start next trackball movement from page edge
         if (top) {
             // go to the top of the document
             return pinScrollTo(mScrollX, 0, true, 0);
@@ -1362,10 +1446,10 @@
             y = -h / 2;
         }
         mUserScroll = true;
-        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) 
+        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
                 : extendScroll(y);
     }
-    
+
     /**
      * Scroll the contents of the view down by half the page size
      * @param bottom true to jump to bottom of page
@@ -1375,7 +1459,7 @@
         if (mNativeClass == 0) {
             return false;
         }
-        nativeClearFocus(-1, -1);
+        nativeClearCursor(); // start next trackball movement from page edge
         if (bottom) {
             return pinScrollTo(mScrollX, mContentHeight, true, 0);
         }
@@ -1388,7 +1472,7 @@
             y = h / 2;
         }
         mUserScroll = true;
-        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) 
+        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
                 : extendScroll(y);
     }
 
@@ -1401,7 +1485,7 @@
         mContentHeight = 0;
         mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
     }
-    
+
     /**
      * Return a new picture that captures the current display of the webview.
      * This is a copy of the display, and will be unaffected if the webview
@@ -1412,7 +1496,7 @@
      *         bounds of the view.
      */
     public Picture capturePicture() {
-        if (null == mWebViewCore) return null; // check for out of memory tab 
+        if (null == mWebViewCore) return null; // check for out of memory tab
         return mWebViewCore.copyContentPicture();
     }
 
@@ -1420,17 +1504,17 @@
      *  Return true if the browser is displaying a TextView for text input.
      */
     private boolean inEditingMode() {
-        return mTextEntry != null && mTextEntry.getParent() != null
-                && mTextEntry.hasFocus();
+        return mWebTextView != null && mWebTextView.getParent() != null
+                && mWebTextView.hasFocus();
     }
 
     private void clearTextEntry() {
         if (inEditingMode()) {
-            mTextEntry.remove();
+            mWebTextView.remove();
         }
     }
 
-    /** 
+    /**
      * Return the current scale of the WebView
      * @return The current scale.
      */
@@ -1471,7 +1555,7 @@
     }
 
     /**
-     * Return a HitTestResult based on the current focus node. If a HTML::a tag
+     * Return a HitTestResult based on the current cursor node. If a HTML::a tag
      * is found and the anchor has a non-javascript url, the HitTestResult type
      * is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
      * anchor does not have a url or if it is a javascript url, the type will
@@ -1494,26 +1578,26 @@
         }
 
         HitTestResult result = new HitTestResult();
-
-        if (nativeUpdateFocusNode()) {
-            FocusNode node = mFocusNode;
-            if (node.mIsTextField || node.mIsTextArea) {
+        if (nativeHasCursorNode()) {
+            if (nativeCursorIsTextInput()) {
                 result.setType(HitTestResult.EDIT_TEXT_TYPE);
-            } else if (node.mText != null) {
-                String text = node.mText;
-                if (text.startsWith(SCHEME_TEL)) {
-                    result.setType(HitTestResult.PHONE_TYPE);
-                    result.setExtra(text.substring(SCHEME_TEL.length()));
-                } else if (text.startsWith(SCHEME_MAILTO)) {
-                    result.setType(HitTestResult.EMAIL_TYPE);
-                    result.setExtra(text.substring(SCHEME_MAILTO.length()));
-                } else if (text.startsWith(SCHEME_GEO)) {
-                    result.setType(HitTestResult.GEO_TYPE);
-                    result.setExtra(URLDecoder.decode(text
-                            .substring(SCHEME_GEO.length())));
-                } else if (node.mIsAnchor) {
-                    result.setType(HitTestResult.SRC_ANCHOR_TYPE);
-                    result.setExtra(text);
+            } else {
+                String text = nativeCursorText();
+                if (text != null) {
+                    if (text.startsWith(SCHEME_TEL)) {
+                        result.setType(HitTestResult.PHONE_TYPE);
+                        result.setExtra(text.substring(SCHEME_TEL.length()));
+                    } else if (text.startsWith(SCHEME_MAILTO)) {
+                        result.setType(HitTestResult.EMAIL_TYPE);
+                        result.setExtra(text.substring(SCHEME_MAILTO.length()));
+                    } else if (text.startsWith(SCHEME_GEO)) {
+                        result.setType(HitTestResult.GEO_TYPE);
+                        result.setExtra(URLDecoder.decode(text
+                                .substring(SCHEME_GEO.length())));
+                    } else if (nativeCursorIsAnchor()) {
+                        result.setType(HitTestResult.SRC_ANCHOR_TYPE);
+                        result.setExtra(text);
+                    }
                 }
             }
         }
@@ -1525,8 +1609,8 @@
             int contentY = viewToContent((int) mLastTouchY + mScrollY);
             String text = nativeImageURI(contentX, contentY);
             if (text != null) {
-                result.setType(type == HitTestResult.UNKNOWN_TYPE ? 
-                        HitTestResult.IMAGE_TYPE : 
+                result.setType(type == HitTestResult.UNKNOWN_TYPE ?
+                        HitTestResult.IMAGE_TYPE :
                         HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
                 result.setExtra(text);
             }
@@ -1538,31 +1622,29 @@
      * Request the href of an anchor element due to getFocusNodePath returning
      * "href." If hrefMsg is null, this method returns immediately and does not
      * dispatch hrefMsg to its target.
-     * 
+     *
      * @param hrefMsg This message will be dispatched with the result of the
      *            request as the data member with "url" as key. The result can
      *            be null.
      */
+    // FIXME: API change required to change the name of this function.  We now
+    // look at the cursor node, and not the focus node.  Also, what is
+    // getFocusNodePath?
     public void requestFocusNodeHref(Message hrefMsg) {
         if (hrefMsg == null || mNativeClass == 0) {
             return;
         }
-        if (nativeUpdateFocusNode()) {
-            FocusNode node = mFocusNode;
-            if (node.mIsAnchor) {
-                // NOTE: We may already have the url of the anchor stored in
-                // node.mText but it may be out of date or the caller may want
-                // to know about javascript urls.
-                mWebViewCore.sendMessage(EventHub.REQUEST_FOCUS_HREF,
-                        node.mFramePointer, node.mNodePointer, hrefMsg);
-            }
+        if (nativeCursorIsAnchor()) {
+            mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
+                    nativeCursorFramePointer(), nativeCursorNodePointer(),
+                    hrefMsg);
         }
     }
-    
+
     /**
      * Request the url of the image last touched by the user. msg will be sent
      * to its target with a String representing the url as its object.
-     * 
+     *
      * @param msg This message will be dispatched with the result of the request
      *            as the data member with "url" as key. The result can be null.
      */
@@ -1637,7 +1719,7 @@
         if ((w | h) == 0) {
             return;
         }
-        
+
         // don't abort a scroll animation if we didn't change anything
         if (mContentWidth != w || mContentHeight != h) {
             // record new dimensions
@@ -1697,7 +1779,7 @@
                 mActualScale = scale;
                 mInvActualScale = 1 / scale;
 
-                // as we don't have animation for scaling, don't do animation 
+                // as we don't have animation for scaling, don't do animation
                 // for scrolling, as it causes weird intermediate state
                 //        pinScrollTo(Math.round(sx), Math.round(sy));
                 mScrollX = pinLocX(Math.round(sx));
@@ -1718,18 +1800,21 @@
     private Rect sendOurVisibleRect() {
         Rect rect = new Rect();
         calcOurContentVisibleRect(rect);
-        if (mFindIsUp) {
-            rect.bottom -= viewToContent(FIND_HEIGHT);
-        }
         // Rect.equals() checks for null input.
         if (!rect.equals(mLastVisibleRectSent)) {
+            Point pos = new Point(rect.left, rect.top);
             mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
-                                     rect.left, rect.top);
+                    nativeMoveGeneration(), 0, pos);
             mLastVisibleRectSent = rect;
         }
         Rect globalRect = new Rect();
         if (getGlobalVisibleRect(globalRect)
                 && !globalRect.equals(mLastGlobalRect)) {
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "sendOurVisibleRect=(" + globalRect.left + ","
+                        + globalRect.top + ",r=" + globalRect.right + ",b="
+                        + globalRect.bottom);
+            }
             // TODO: the global offset is only used by windowRect()
             // in ChromeClientAndroid ; other clients such as touch
             // and mouse events could return view + screen relative points.
@@ -1744,6 +1829,9 @@
         Point p = new Point();
         getGlobalVisibleRect(r, p);
         r.offset(-p.x, -p.y);
+        if (mFindIsUp) {
+            r.bottom -= FIND_HEIGHT;
+        }
     }
 
     // Sets r to be our visible rectangle in content coordinates
@@ -1821,10 +1909,10 @@
         WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
         return h != null ? h.getUrl() : null;
     }
-    
+
     /**
-     * Get the original url for the current page. This is not always the same 
-     * as the url passed to WebViewClient.onPageStarted because although the 
+     * Get the original url for the current page. This is not always the same
+     * as the url passed to WebViewClient.onPageStarted because although the
      * load for that url has begun, the current page may not have changed.
      * Also, there may have been redirects resulting in a different url to that
      * originally requested.
@@ -1862,7 +1950,7 @@
     public int getProgress() {
         return mCallbackProxy.getProgress();
     }
-    
+
     /**
      * @return the height of the HTML content.
      */
@@ -1871,30 +1959,77 @@
     }
 
     /**
-     * Pause all layout, parsing, and javascript timers. This can be useful if
-     * the WebView is not visible or the application has been paused.
+     * Pause all layout, parsing, and javascript timers for all webviews. This
+     * is a global requests, not restricted to just this webview. This can be
+     * useful if the application has been paused.
      */
     public void pauseTimers() {
         mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
     }
 
     /**
-     * Resume all layout, parsing, and javascript timers. This will resume
-     * dispatching all timers.
+     * Resume all layout, parsing, and javascript timers for all webviews.
+     * This will resume dispatching all timers.
      */
     public void resumeTimers() {
         mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
     }
 
     /**
-     * Clear the resource cache. This will cause resources to be re-downloaded
-     * if accessed again.
-     * <p>
-     * Note: this really needs to be a static method as it clears cache for all
-     * WebView. But we need mWebViewCore to send message to WebCore thread, so
-     * we can't make this static.
+     * Call this to pause any extra processing associated with this view and
+     * its associated DOM/plugins/javascript/etc. For example, if the view is
+     * taken offscreen, this could be called to reduce unnecessary CPU and/or
+     * network traffic. When the view is again "active", call onResume().
+     *
+     * Note that this differs from pauseTimers(), which affects all views/DOMs
+     * @hide
+     */
+    public void onPause() {
+        if (!mIsPaused) {
+            mIsPaused = true;
+            mWebViewCore.sendMessage(EventHub.ON_PAUSE);
+        }
+    }
+
+    /**
+     * Call this to balanace a previous call to onPause()
+     * @hide
+     */
+    public void onResume() {
+        if (mIsPaused) {
+            mIsPaused = false;
+            mWebViewCore.sendMessage(EventHub.ON_RESUME);
+        }
+    }
+
+    /**
+     * Returns true if the view is paused, meaning onPause() was called. Calling
+     * onResume() sets the paused state back to false.
+     * @hide
+     */
+    public boolean isPaused() {
+        return mIsPaused;
+    }
+
+    /**
+     * Call this to inform the view that memory is low so that it can
+     * free any available memory.
+     * @hide
+     */
+    public void freeMemory() {
+        mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
+    }
+
+    /**
+     * Clear the resource cache. Note that the cache is per-application, so
+     * this will clear the cache for all WebViews used.
+     *
+     * @param includeDiskFiles If false, only the RAM cache is cleared.
      */
     public void clearCache(boolean includeDiskFiles) {
+        // Note: this really needs to be a static method as it clears cache for all
+        // WebView. But we need mWebViewCore to send message to WebCore thread, so
+        // we can't make this static.
         mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
                 includeDiskFiles ? 1 : 0, 0);
     }
@@ -1906,7 +2041,7 @@
     public void clearFormData() {
         if (inEditingMode()) {
             AutoCompleteAdapter adapter = null;
-            mTextEntry.setAdapterCustom(adapter);
+            mWebTextView.setAdapterCustom(adapter);
         }
     }
 
@@ -1940,7 +2075,7 @@
 
     /*
      * Highlight and scroll to the next occurance of String in findAll.
-     * Wraps the page infinitely, and scrolls.  Must be called after 
+     * Wraps the page infinitely, and scrolls.  Must be called after
      * calling findAll.
      *
      * @param forward Direction to search.
@@ -1966,11 +2101,8 @@
     // or not we draw the highlights for matches.
     private boolean mFindIsUp;
 
-    private native int nativeFindAll(String findLower, String findUpper);
-    private native void nativeFindNext(boolean forward);
-    
     /**
-     * Return the first substring consisting of the address of a physical 
+     * Return the first substring consisting of the address of a physical
      * location. Currently, only addresses in the United States are detected,
      * and consist of:
      * - a house number
@@ -1983,14 +2115,40 @@
      * All names must be correctly capitalized, and the zip code, if present,
      * must be valid for the state. The street type must be a standard USPS
      * spelling or abbreviation. The state or territory must also be spelled
-     * or abbreviated using USPS standards. The house number may not exceed 
+     * or abbreviated using USPS standards. The house number may not exceed
      * five digits.
      * @param addr The string to search for addresses.
      *
      * @return the address, or if no address is found, return null.
      */
     public static String findAddress(String addr) {
-        return WebViewCore.nativeFindAddress(addr);
+        return findAddress(addr, false);
+    }
+
+    /**
+     * @hide
+     * Return the first substring consisting of the address of a physical
+     * location. Currently, only addresses in the United States are detected,
+     * and consist of:
+     * - a house number
+     * - a street name
+     * - a street type (Road, Circle, etc), either spelled out or abbreviated
+     * - a city name
+     * - a state or territory, either spelled out or two-letter abbr.
+     * - an optional 5 digit or 9 digit zip code.
+     *
+     * Names are optionally capitalized, and the zip code, if present,
+     * must be valid for the state. The street type must be a standard USPS
+     * spelling or abbreviation. The state or territory must also be spelled
+     * or abbreviated using USPS standards. The house number may not exceed
+     * five digits.
+     * @param addr The string to search for addresses.
+     * @param caseInsensitive addr Set to true to make search ignore case.
+     *
+     * @return the address, or if no address is found, return null.
+     */
+    public static String findAddress(String addr, boolean caseInsensitive) {
+        return WebViewCore.nativeFindAddress(addr, caseInsensitive);
     }
 
     /*
@@ -2201,6 +2359,16 @@
     }
 
     /**
+     * Gets the chrome handler.
+     * @return the current WebChromeClient instance.
+     *
+     * @hide API council approval.
+     */
+    public WebChromeClient getWebChromeClient() {
+        return mCallbackProxy.getWebChromeClient();
+    }
+
+    /**
      * Set the Picture listener. This is an interface used to receive
      * notifications of a new Picture.
      * @param listener An implementation of WebView.PictureListener.
@@ -2245,10 +2413,9 @@
      * @param interfaceName The name to used to expose the class in Javascript
      */
     public void addJavascriptInterface(Object obj, String interfaceName) {
-        // Use Hashmap rather than Bundle as Bundles can't cope with Objects
-        HashMap arg = new HashMap();
-        arg.put("object", obj);
-        arg.put("interfaceName", interfaceName);
+        WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
+        arg.mObject = obj;
+        arg.mInterfaceName = interfaceName;
         mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
     }
 
@@ -2274,16 +2441,10 @@
     }
 
    /**
-    * Signal the WebCore thread to refresh its list of plugins. Use
-    * this if the directory contents of one of the plugin directories
-    * has been modified and needs its changes reflecting. May cause
-    * plugin load and/or unload.
-    * @param reloadOpenPages Set to true to reload all open pages.
+     * TODO: need to add @Deprecated
     */
     public void refreshPlugins(boolean reloadOpenPages) {
-        if (mWebViewCore != null) {
-            mWebViewCore.sendMessage(EventHub.REFRESH_PLUGINS, reloadOpenPages);
-        }
+        PluginManager.getInstance(mContext).refreshPlugins(reloadOpenPages);
     }
 
     //-------------------------------------------------------------------------
@@ -2294,7 +2455,7 @@
     protected void finalize() throws Throwable {
         destroy();
     }
-    
+
     @Override
     protected void onDraw(Canvas canvas) {
         // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
@@ -2303,7 +2464,7 @@
         }
         if (mWebViewCore.mEndScaleZoom) {
             mWebViewCore.mEndScaleZoom = false;
-            if (mTouchMode >= FIRST_SCROLL_ZOOM 
+            if (mTouchMode >= FIRST_SCROLL_ZOOM
                     && mTouchMode <= LAST_SCROLL_ZOOM) {
                 setHorizontalScrollBarEnabled(true);
                 setVerticalScrollBarEnabled(true);
@@ -2314,22 +2475,21 @@
         if (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM) {
             scrollZoomDraw(canvas);
         } else {
-            nativeRecomputeFocus();
             // Update the buttons in the picture, so when we draw the picture
             // to the screen, they are in the correct state.
             // Tell the native side if user is a) touching the screen,
             // b) pressing the trackball down, or c) pressing the enter key
-            // If the focus is a button, we need to draw it in the pressed
+            // If the cursor is on a button, we need to draw it in the pressed
             // state.
             // If mNativeClass is 0, we should not reach here, so we do not
             // need to check it again.
             nativeRecordButtons(hasFocus() && hasWindowFocus(),
                     mTouchMode == TOUCH_SHORTPRESS_START_MODE
-                    || mTrackballDown || mGotEnterDown, false);
-            drawCoreAndFocusRing(canvas, mBackgroundColor, mDrawFocusRing);
+                    || mTrackballDown || mGotCenterDown, false);
+            drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
         }
         canvas.restoreToCount(sc);
-        
+
         if (AUTO_REDRAW_HACK && mAutoRedraw) {
             invalidate();
         }
@@ -2345,15 +2505,23 @@
 
     @Override
     public boolean performLongClick() {
+        if (mNativeClass != 0 && nativeCursorIsTextInput()) {
+            // Send the click so that the textfield is in focus
+            // FIXME: When we start respecting changes to the native textfield's
+            // selection, need to make sure that this does not change it.
+            mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
+                    nativeCursorNodePointer());
+            rebuildWebTextView();
+        }
         if (inEditingMode()) {
-            return mTextEntry.performLongClick();
+            return mWebTextView.performLongClick();
         } else {
             return super.performLongClick();
         }
     }
 
-    private void drawCoreAndFocusRing(Canvas canvas, int color,
-        boolean drawFocus) {
+    private void drawCoreAndCursorRing(Canvas canvas, int color,
+        boolean drawCursorRing) {
         if (mDrawHistory) {
             canvas.scale(mActualScale, mActualScale);
             canvas.drawPicture(mHistoryPicture);
@@ -2361,14 +2529,14 @@
         }
 
         boolean animateZoom = mZoomScale != 0;
-        boolean animateScroll = !mScroller.isFinished() 
+        boolean animateScroll = !mScroller.isFinished()
                 || mVelocityTracker != null;
         if (animateZoom) {
             float zoomScale;
             int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
             if (interval < ZOOM_ANIMATION_LENGTH) {
                 float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
-                zoomScale = 1.0f / (mInvInitialZoomScale 
+                zoomScale = 1.0f / (mInvInitialZoomScale
                         + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
                 invalidate();
             } else {
@@ -2408,10 +2576,10 @@
             if (mTouchSelection) {
                 nativeDrawSelectionRegion(canvas);
             } else {
-                nativeDrawSelection(canvas, mSelectX, mSelectY, 
+                nativeDrawSelection(canvas, mSelectX, mSelectY,
                         mExtendSelection);
             }
-        } else if (drawFocus) {
+        } else if (drawCursorRing) {
             if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
                 mTouchMode = TOUCH_SHORTPRESS_MODE;
                 HitTestResult hitTest = getHitTestResult();
@@ -2422,7 +2590,7 @@
                             LONG_PRESS_TIMEOUT);
                 }
             }
-            nativeDrawFocusRing(canvas);
+            nativeDrawCursorRing(canvas);
         }
         // When the FindDialog is up, only draw the matches if we are not in
         // the process of scrolling them into view.
@@ -2431,14 +2599,12 @@
         }
     }
 
-    private native void nativeDrawMatches(Canvas canvas);
-    
     private float scrollZoomGridScale(float invScale) {
-        float griddedInvScale = (int) (invScale * SCROLL_ZOOM_GRID) 
+        float griddedInvScale = (int) (invScale * SCROLL_ZOOM_GRID)
             / (float) SCROLL_ZOOM_GRID;
         return 1.0f / griddedInvScale;
     }
-    
+
     private float scrollZoomX(float scale) {
         int width = getViewWidth();
         float maxScrollZoomX = mContentWidth * scale - width;
@@ -2454,7 +2620,7 @@
         return -(maxScrollZoomY > 0 ? mZoomScrollY * maxScrollZoomY / maxY
                 : maxScrollZoomY / 2);
     }
-    
+
     private void drawMagnifyFrame(Canvas canvas, Rect frame, Paint paint) {
         final float ADORNMENT_LEN = 16.0f;
         float width = frame.width();
@@ -2475,13 +2641,13 @@
         path.offset(frame.left, frame.top);
         canvas.drawPath(path, paint);
     }
-    
-    // Returns frame surrounding magified portion of screen while 
+
+    // Returns frame surrounding magified portion of screen while
     // scroll-zoom is enabled. The frame is also used to center the
     // zoom-in zoom-out points at the start and end of the animation.
     private Rect scrollZoomFrame(int width, int height, float halfScale) {
         Rect scrollFrame = new Rect();
-        scrollFrame.set(mZoomScrollX, mZoomScrollY, 
+        scrollFrame.set(mZoomScrollX, mZoomScrollY,
                 mZoomScrollX + width, mZoomScrollY + height);
         if (mContentWidth * mZoomScrollLimit < width) {
             float scale = zoomFrameScaleX(width, halfScale, 1.0f);
@@ -2497,37 +2663,37 @@
         }
         return scrollFrame;
     }
-    
+
     private float zoomFrameScaleX(int width, float halfScale, float noScale) {
         // mContentWidth > width > mContentWidth * mZoomScrollLimit
         if (mContentWidth <= width) {
             return halfScale;
         }
-        float part = (width - mContentWidth * mZoomScrollLimit)  
+        float part = (width - mContentWidth * mZoomScrollLimit)
                 / (width * (1 - mZoomScrollLimit));
         return halfScale * part + noScale * (1.0f - part);
     }
-    
+
     private float zoomFrameScaleY(int height, float halfScale, float noScale) {
         if (mContentHeight <= height) {
             return halfScale;
         }
-        float part = (height - mContentHeight * mZoomScrollLimit)  
+        float part = (height - mContentHeight * mZoomScrollLimit)
                 / (height * (1 - mZoomScrollLimit));
         return halfScale * part + noScale * (1.0f - part);
     }
-    
+
     private float scrollZoomMagScale(float invScale) {
         return (invScale * 2 + mInvActualScale) / 3;
     }
-    
+
     private void scrollZoomDraw(Canvas canvas) {
-        float invScale = mZoomScrollInvLimit; 
+        float invScale = mZoomScrollInvLimit;
         int elapsed = 0;
         if (mTouchMode != SCROLL_ZOOM_OUT) {
-            elapsed = (int) Math.min(System.currentTimeMillis() 
+            elapsed = (int) Math.min(System.currentTimeMillis()
                 - mZoomScrollStart, SCROLL_ZOOM_DURATION);
-            float transitionScale = (mZoomScrollInvLimit - mInvActualScale) 
+            float transitionScale = (mZoomScrollInvLimit - mInvActualScale)
                     * elapsed / SCROLL_ZOOM_DURATION;
             if (mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
                 invScale = mInvActualScale + transitionScale;
@@ -2545,9 +2711,9 @@
             if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN) {
                 setHorizontalScrollBarEnabled(true);
                 setVerticalScrollBarEnabled(true);
-                updateTextEntry();
-                scrollTo((int) (scrollFrame.centerX() * mActualScale) 
-                        - (width >> 1), (int) (scrollFrame.centerY() 
+                rebuildWebTextView();
+                scrollTo((int) (scrollFrame.centerX() * mActualScale)
+                        - (width >> 1), (int) (scrollFrame.centerY()
                         * mActualScale) - (height >> 1));
                 mTouchMode = TOUCH_DONE_MODE;
             } else {
@@ -2556,10 +2722,10 @@
         }
         float newX = scrollZoomX(scale);
         float newY = scrollZoomY(scale);
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "scrollZoomDraw scale=" + scale + " + (" + newX
                     + ", " + newY + ") mZoomScroll=(" + mZoomScrollX + ", "
-                    + mZoomScrollY + ")" + " invScale=" + invScale + " scale=" 
+                    + mZoomScrollY + ")" + " invScale=" + invScale + " scale="
                     + scale);
         }
         canvas.translate(newX, newY);
@@ -2603,8 +2769,8 @@
         }
         canvas.scale(halfScale, halfScale, mZoomScrollX + width * halfX
                 , mZoomScrollY + height * halfY);
-        if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "scrollZoomDraw halfScale=" + halfScale + " w/h=(" 
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "scrollZoomDraw halfScale=" + halfScale + " w/h=("
                     + width + ", " + height + ") half=(" + halfX + ", "
                     + halfY + ")");
         }
@@ -2632,14 +2798,17 @@
                 , Math.max(0, (int) ((x - left) / scale)));
         mZoomScrollY = Math.min(mContentHeight - height
                 , Math.max(0, (int) ((y - top) / scale)));
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "zoomScrollTap scale=" + scale + " + (" + left
                     + ", " + top + ") mZoomScroll=(" + mZoomScrollX + ", "
                     + mZoomScrollY + ")" + " x=" + x + " y=" + y);
         }
     }
 
-    private boolean canZoomScrollOut() {
+    /**
+     * @hide
+     */
+    public boolean canZoomScrollOut() {
         if (mContentWidth == 0 || mContentHeight == 0) {
             return false;
         }
@@ -2649,7 +2818,7 @@
         float y = (float) height / (float) mContentHeight;
         mZoomScrollLimit = Math.max(DEFAULT_MIN_ZOOM_SCALE, Math.min(x, y));
         mZoomScrollInvLimit = 1.0f / mZoomScrollLimit;
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "canZoomScrollOut"
                     + " mInvActualScale=" + mInvActualScale
                     + " mZoomScrollLimit=" + mZoomScrollLimit
@@ -2664,7 +2833,7 @@
         return mContentWidth >= width * limit
                 || mContentHeight >= height * limit;
     }
-        
+
     private void startZoomScrollOut() {
         setHorizontalScrollBarEnabled(false);
         setVerticalScrollBarEnabled(false);
@@ -2690,19 +2859,22 @@
         mZoomScrollStart = System.currentTimeMillis();
         Rect zoomFrame = scrollZoomFrame(width, height
                 , scrollZoomMagScale(mZoomScrollInvLimit));
-        mZoomScrollX = Math.max(0, (int) ((mScrollX + halfW) * mInvActualScale) 
+        mZoomScrollX = Math.max(0, (int) ((mScrollX + halfW) * mInvActualScale)
                 - (zoomFrame.width() >> 1));
-        mZoomScrollY = Math.max(0, (int) ((mScrollY + halfH) * mInvActualScale) 
+        mZoomScrollY = Math.max(0, (int) ((mScrollY + halfH) * mInvActualScale)
                 - (zoomFrame.height() >> 1));
         scrollTo(0, 0); // triggers inval, starts animation
         clearTextEntry();
-        if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "startZoomScrollOut mZoomScroll=(" 
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "startZoomScrollOut mZoomScroll=("
                     + mZoomScrollX + ", " + mZoomScrollY +")");
         }
     }
-    
-    private void zoomScrollOut() {
+
+    /**
+     * @hide
+     */
+    public void zoomScrollOut() {
         if (canZoomScrollOut() == false) {
             mTouchMode = TOUCH_DONE_MODE;
             return;
@@ -2713,7 +2885,7 @@
     }
 
     private void moveZoomScrollWindow(float x, float y) {
-        if (Math.abs(x - mLastZoomScrollRawX) < 1.5f 
+        if (Math.abs(x - mLastZoomScrollRawX) < 1.5f
                 && Math.abs(y - mLastZoomScrollRawY) < 1.5f) {
             return;
         }
@@ -2725,12 +2897,12 @@
         int height = getViewHeight();
         int maxZoomX = mContentWidth - width;
         if (maxZoomX > 0) {
-            int maxScreenX = width - (int) Math.ceil(width 
+            int maxScreenX = width - (int) Math.ceil(width
                     * mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
-            if (LOGV_ENABLED) {
-                Log.v(LOGTAG, "moveZoomScrollWindow-X" 
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "moveZoomScrollWindow-X"
                         + " maxScreenX=" + maxScreenX + " width=" + width
-                        + " mZoomScrollLimit=" + mZoomScrollLimit + " x=" + x); 
+                        + " mZoomScrollLimit=" + mZoomScrollLimit + " x=" + x);
             }
             x += maxScreenX * mLastScrollX / maxZoomX - mLastTouchX;
             x *= Math.max(maxZoomX / maxScreenX, mZoomScrollInvLimit);
@@ -2738,12 +2910,12 @@
         }
         int maxZoomY = mContentHeight - height;
         if (maxZoomY > 0) {
-            int maxScreenY = height - (int) Math.ceil(height 
+            int maxScreenY = height - (int) Math.ceil(height
                     * mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
-            if (LOGV_ENABLED) {
-                Log.v(LOGTAG, "moveZoomScrollWindow-Y" 
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "moveZoomScrollWindow-Y"
                         + " maxScreenY=" + maxScreenY + " height=" + height
-                        + " mZoomScrollLimit=" + mZoomScrollLimit + " y=" + y); 
+                        + " mZoomScrollLimit=" + mZoomScrollLimit + " y=" + y);
             }
             y += maxScreenY * mLastScrollY / maxZoomY - mLastTouchY;
             y *= Math.max(maxZoomY / maxScreenY, mZoomScrollInvLimit);
@@ -2752,12 +2924,12 @@
         if (oldX != mZoomScrollX || oldY != mZoomScrollY) {
             invalidate();
         }
-        if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "moveZoomScrollWindow" 
-                    + " scrollTo=(" + mZoomScrollX + ", " + mZoomScrollY + ")" 
-                    + " mLastTouch=(" + mLastTouchX + ", " + mLastTouchY + ")" 
-                    + " maxZoom=(" + maxZoomX + ", " + maxZoomY + ")" 
-                    + " last=("+mLastScrollX+", "+mLastScrollY+")" 
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "moveZoomScrollWindow"
+                    + " scrollTo=(" + mZoomScrollX + ", " + mZoomScrollY + ")"
+                    + " mLastTouch=(" + mLastTouchX + ", " + mLastTouchY + ")"
+                    + " maxZoom=(" + maxZoomX + ", " + maxZoomY + ")"
+                    + " last=("+mLastScrollX+", "+mLastScrollY+")"
                     + " x=" + x + " y=" + y);
         }
     }
@@ -2802,7 +2974,7 @@
     // Should only be called in UI thread
     void switchOutDrawHistory() {
         if (null == mWebViewCore) return; // CallbackProxy may trigger this
-        if (mDrawHistory) {
+        if (mDrawHistory && mWebViewCore.pictureReady()) {
             mDrawHistory = false;
             invalidate();
             int oldScrollX = mScrollX;
@@ -2818,72 +2990,26 @@
         }
     }
 
-    /**
-     *  Class representing the node which is focused.
-     */
-    private class FocusNode {
-        public FocusNode() {
-            mBounds = new Rect();
-        }
-        // Only to be called by JNI
-        private void setAll(boolean isTextField, boolean isTextArea, boolean 
-                isPassword, boolean isAnchor, boolean isRtlText, int maxLength, 
-                int textSize, int boundsX, int boundsY, int boundsRight, int 
-                boundsBottom, int nodePointer, int framePointer, String text, 
-                String name, int rootTextGeneration) {
-            mIsTextField        = isTextField;
-            mIsTextArea         = isTextArea;
-            mIsPassword         = isPassword;
-            mIsAnchor           = isAnchor;
-            mIsRtlText          = isRtlText;
-
-            mMaxLength          = maxLength;
-            mTextSize           = textSize;
-            
-            mBounds.set(boundsX, boundsY, boundsRight, boundsBottom);
-            
-            
-            mNodePointer        = nodePointer;
-            mFramePointer       = framePointer;
-            mText               = text;
-            mName               = name;
-            mRootTextGeneration = rootTextGeneration;
-        }
-        public boolean  mIsTextField;
-        public boolean  mIsTextArea;
-        public boolean  mIsPassword;
-        public boolean  mIsAnchor;
-        public boolean  mIsRtlText;
-
-        public int      mSelectionStart;
-        public int      mSelectionEnd;
-        public int      mMaxLength;
-        public int      mTextSize;
-        
-        public Rect     mBounds;
-        
-        public int      mNodePointer;
-        public int      mFramePointer;
-        public String   mText;
-        public String   mName;
-        public int      mRootTextGeneration;
+    WebViewCore.CursorData cursorData() {
+        WebViewCore.CursorData result = new WebViewCore.CursorData();
+        result.mMoveGeneration = nativeMoveGeneration();
+        result.mFrame = nativeCursorFramePointer();
+        Point position = nativeCursorPosition();
+        result.mX = position.x;
+        result.mY = position.y;
+        return result;
     }
-    
-    // Warning: ONLY use mFocusNode AFTER calling nativeUpdateFocusNode(),
-    // and ONLY if it returns true;
-    private FocusNode mFocusNode = new FocusNode();
-    
+
     /**
      *  Delete text from start to end in the focused textfield. If there is no
-     *  focus, or if start == end, silently fail.  If start and end are out of 
+     *  focus, or if start == end, silently fail.  If start and end are out of
      *  order, swap them.
      *  @param  start   Beginning of selection to delete.
      *  @param  end     End of selection to delete.
      */
     /* package */ void deleteSelection(int start, int end) {
         mTextGeneration++;
-        mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, start, end,
-                new WebViewCore.FocusData(mFocusData));
+        mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, start, end);
     }
 
     /**
@@ -2893,119 +3019,133 @@
      *  @param  end     End of selection.
      */
     /* package */ void setSelection(int start, int end) {
-        mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end,
-                new WebViewCore.FocusData(mFocusData));
+        mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
     }
 
     // Called by JNI when a touch event puts a textfield into focus.
-    private void displaySoftKeyboard() {
+    private void displaySoftKeyboard(boolean isTextView) {
         InputMethodManager imm = (InputMethodManager)
                 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-        imm.showSoftInput(mTextEntry, 0);
-        mTextEntry.enableScrollOnScreen(true);
-        // Now we need to fake a touch event to place the cursor where the
-        // user touched.
-        AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams)
-                mTextEntry.getLayoutParams();
-        if (lp != null) {
-            // Take the last touch and adjust for the location of the
-            // TextDialog.
-            float x = mLastTouchX + (float) (mScrollX - lp.x);
-            float y = mLastTouchY + (float) (mScrollY - lp.y);
-            mTextEntry.fakeTouchEvent(x, y);
+
+        if (isTextView) {
+            imm.showSoftInput(mWebTextView, 0);
+            // Now we need to fake a touch event to place the cursor where the
+            // user touched.
+            AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams)
+                    mWebTextView.getLayoutParams();
+            if (lp != null) {
+                // Take the last touch and adjust for the location of the
+                // WebTextView.
+                float x = mLastTouchX + (float) (mScrollX - lp.x);
+                float y = mLastTouchY + (float) (mScrollY - lp.y);
+                mWebTextView.fakeTouchEvent(x, y);
+            }
+        }
+        else { // used by plugins
+            imm.showSoftInput(this, 0);
         }
     }
 
-    private void updateTextEntry() {
-        if (mTextEntry == null) {
-            mTextEntry = new TextDialog(mContext, WebView.this);
-            // Initialize our generation number.
-            mTextGeneration = 0;
-        }
-        // If we do not have focus, do nothing until we gain focus.
-        if (!hasFocus() && !mTextEntry.hasFocus()
-                || (mTouchMode >= FIRST_SCROLL_ZOOM 
+    // Called by WebKit to instruct the UI to hide the keyboard
+    private void hideSoftKeyboard() {
+        InputMethodManager imm = (InputMethodManager)
+                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+        imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
+    }
+
+    /*
+     * This method checks the current focus and cursor and potentially rebuilds
+     * mWebTextView to have the appropriate properties, such as password,
+     * multiline, and what text it contains.  It also removes it if necessary.
+     */
+    private void rebuildWebTextView() {
+        // If the WebView does not have focus, do nothing until it gains focus.
+        if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())
+                || (mTouchMode >= FIRST_SCROLL_ZOOM
                 && mTouchMode <= LAST_SCROLL_ZOOM)) {
-            mNeedsUpdateTextEntry = true;
             return;
         }
         boolean alreadyThere = inEditingMode();
-        if (0 == mNativeClass || !nativeUpdateFocusNode()) {
+        // inEditingMode can only return true if mWebTextView is non-null,
+        // so we can safely call remove() if (alreadyThere)
+        if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
             if (alreadyThere) {
-                mTextEntry.remove();
+                mWebTextView.remove();
             }
             return;
         }
-        FocusNode node = mFocusNode;
-        if (!node.mIsTextField && !node.mIsTextArea) {
-            if (alreadyThere) {
-                mTextEntry.remove();
-            }
-            return;
+        // At this point, we know we have found an input field, so go ahead
+        // and create the WebTextView if necessary.
+        if (mWebTextView == null) {
+            mWebTextView = new WebTextView(mContext, WebView.this);
+            // Initialize our generation number.
+            mTextGeneration = 0;
         }
-        mTextEntry.setTextSize(contentToView(node.mTextSize));
-        Rect visibleRect = sendOurVisibleRect();
+        mWebTextView.setTextSize(contentToView(nativeFocusCandidateTextSize()));
+        Rect visibleRect = new Rect();
+        calcOurContentVisibleRect(visibleRect);
         // Note that sendOurVisibleRect calls viewToContent, so the coordinates
         // should be in content coordinates.
-        if (!Rect.intersects(node.mBounds, visibleRect)) {
-            // Node is not on screen, so do not bother.
-            return;
+        Rect bounds = nativeFocusCandidateNodeBounds();
+        if (!Rect.intersects(bounds, visibleRect)) {
+            mWebTextView.bringIntoView();
         }
-        int x = node.mBounds.left;
-        int y = node.mBounds.top;
-        int width = node.mBounds.width();
-        int height = node.mBounds.height();
-        if (alreadyThere && mTextEntry.isSameTextField(node.mNodePointer)) {
+        String text = nativeFocusCandidateText();
+        int nodePointer = nativeFocusCandidatePointer();
+        if (alreadyThere && mWebTextView.isSameTextField(nodePointer)) {
             // It is possible that we have the same textfield, but it has moved,
             // i.e. In the case of opening/closing the screen.
             // In that case, we need to set the dimensions, but not the other
             // aspects.
             // We also need to restore the selection, which gets wrecked by
             // calling setTextEntryRect.
-            Spannable spannable = (Spannable) mTextEntry.getText();
+            Spannable spannable = (Spannable) mWebTextView.getText();
             int start = Selection.getSelectionStart(spannable);
             int end = Selection.getSelectionEnd(spannable);
-            setTextEntryRect(x, y, width, height);
             // If the text has been changed by webkit, update it.  However, if
             // there has been more UI text input, ignore it.  We will receive
             // another update when that text is recognized.
-            if (node.mText != null && !node.mText.equals(spannable.toString())
-                    && node.mRootTextGeneration == mTextGeneration) {
-                mTextEntry.setTextAndKeepSelection(node.mText);
+            if (text != null && !text.equals(spannable.toString())
+                    && nativeTextGeneration() == mTextGeneration) {
+                mWebTextView.setTextAndKeepSelection(text);
             } else {
                 Selection.setSelection(spannable, start, end);
             }
         } else {
-            String text = node.mText;
-            setTextEntryRect(x, y, width, height);
-            mTextEntry.setGravity(node.mIsRtlText ? Gravity.RIGHT : 
-                    Gravity.NO_GRAVITY);
+            Rect vBox = contentToView(bounds);
+            mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
+                    vBox.height());
+            mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ?
+                    Gravity.RIGHT : Gravity.NO_GRAVITY);
             // this needs to be called before update adapter thread starts to
-            // ensure the mTextEntry has the same node pointer
-            mTextEntry.setNodePointer(node.mNodePointer);
+            // ensure the mWebTextView has the same node pointer
+            mWebTextView.setNodePointer(nodePointer);
             int maxLength = -1;
-            if (node.mIsTextField) {
-                maxLength = node.mMaxLength;
+            boolean isTextField = nativeFocusCandidateIsTextField();
+            if (isTextField) {
+                maxLength = nativeFocusCandidateMaxLength();
+                String name = nativeFocusCandidateName();
                 if (mWebViewCore.getSettings().getSaveFormData()
-                        && node.mName != null) {
-                    HashMap data = new HashMap();
-                    data.put("text", node.mText);
+                        && name != null) {
                     Message update = mPrivateHandler.obtainMessage(
-                            UPDATE_TEXT_ENTRY_ADAPTER, node.mNodePointer, 0,
-                            data);
-                    UpdateTextEntryAdapter updater = new UpdateTextEntryAdapter(
-                            node.mName, getUrl(), update);
+                            REQUEST_FORM_DATA, nodePointer);
+                    RequestFormData updater = new RequestFormData(name,
+                            getUrl(), update);
                     Thread t = new Thread(updater);
                     t.start();
                 }
             }
-            mTextEntry.setMaxLength(maxLength);
+            mWebTextView.setMaxLength(maxLength);
             AutoCompleteAdapter adapter = null;
-            mTextEntry.setAdapterCustom(adapter);
-            mTextEntry.setSingleLine(node.mIsTextField);
-            mTextEntry.setInPassword(node.mIsPassword);
+            mWebTextView.setAdapterCustom(adapter);
+            mWebTextView.setSingleLine(isTextField);
+            mWebTextView.setInPassword(nativeFocusCandidateIsPassword());
             if (null == text) {
-                mTextEntry.setText("", 0, 0);
+                mWebTextView.setText("", 0, 0);
+                if (DebugFlags.WEB_VIEW) {
+                    Log.v(LOGTAG, "rebuildWebTextView null == text");
+                }
             } else {
                 // Change to true to enable the old style behavior, where
                 // entering a textfield/textarea always set the selection to the
@@ -3016,24 +3156,35 @@
                 // textarea.  Testing out a new behavior, where textfields set
                 // selection at the end, and textareas at the beginning.
                 if (false) {
-                    mTextEntry.setText(text, 0, text.length());
-                } else if (node.mIsTextField) {
+                    mWebTextView.setText(text, 0, text.length());
+                } else if (isTextField) {
                     int length = text.length();
-                    mTextEntry.setText(text, length, length);
+                    mWebTextView.setText(text, length, length);
+                    if (DebugFlags.WEB_VIEW) {
+                        Log.v(LOGTAG, "rebuildWebTextView length=" + length);
+                    }
                 } else {
-                    mTextEntry.setText(text, 0, 0);
+                    mWebTextView.setText(text, 0, 0);
+                    if (DebugFlags.WEB_VIEW) {
+                        Log.v(LOGTAG, "rebuildWebTextView !isTextField");
+                    }
                 }
             }
-            mTextEntry.requestFocus();
+            mWebTextView.requestFocus();
         }
     }
 
-    private class UpdateTextEntryAdapter implements Runnable {
+    /*
+     * This class requests an Adapter for the WebTextView which shows past
+     * entries stored in the database.  It is a Runnable so that it can be done
+     * in its own thread, without slowing down the UI.
+     */
+    private class RequestFormData implements Runnable {
         private String mName;
         private String mUrl;
         private Message mUpdateMessage;
 
-        public UpdateTextEntryAdapter(String name, String url, Message msg) {
+        public RequestFormData(String name, String url, Message msg) {
             mName = name;
             mUrl = url;
             mUpdateMessage = msg;
@@ -3044,29 +3195,21 @@
             if (pastEntries.size() > 0) {
                 AutoCompleteAdapter adapter = new
                         AutoCompleteAdapter(mContext, pastEntries);
-                ((HashMap) mUpdateMessage.obj).put("adapter", adapter);
+                mUpdateMessage.obj = adapter;
                 mUpdateMessage.sendToTarget();
             }
         }
     }
 
-    private void setTextEntryRect(int x, int y, int width, int height) {
-        x = contentToView(x);
-        y = contentToView(y);
-        width = contentToView(width);
-        height = contentToView(height);
-        mTextEntry.setRect(x, y, width, height);
-    }
-
-    // This is used to determine long press with the enter key, or
-    // a center key.  Does not affect long press with the trackball/touch.
-    private boolean mGotEnterDown = false;
+    // This is used to determine long press with the center key.  Does not
+    // affect long press with the trackball/touch.
+    private boolean mGotCenterDown = false;
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
-                    + ", " + event);
+                    + ", " + event + ", unicode=" + event.getUnicodeChar());
         }
 
         if (mNativeClass == 0) {
@@ -3092,29 +3235,27 @@
             return false;
         }
 
-        if (mShiftIsPressed == false && nativeFocusNodeWantsKeyEvents() == false
-                && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT 
+        if (mShiftIsPressed == false && nativeCursorWantsKeyEvents() == false
+                && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
             mExtendSelection = false;
             mShiftIsPressed = true;
-            if (nativeUpdateFocusNode()) {
-                FocusNode node = mFocusNode;
-                mSelectX = contentToView(node.mBounds.left);
-                mSelectY = contentToView(node.mBounds.top);
+            if (nativeHasCursorNode()) {
+                Rect rect = nativeCursorNodeBounds();
+                mSelectX = contentToView(rect.left);
+                mSelectY = contentToView(rect.top);
             } else {
                 mSelectX = mScrollX + (int) mLastTouchX;
                 mSelectY = mScrollY + (int) mLastTouchY;
             }
-            int contentX = viewToContent((int) mLastTouchX + mScrollX);
-            int contentY = viewToContent((int) mLastTouchY + mScrollY);
-            nativeClearFocus(contentX, contentY);
+            nativeHideCursor();
        }
 
         if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
                 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
             // always handle the navigation keys in the UI thread
             switchOutDrawHistory();
-            if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
+            if (navHandledKey(keyCode, 1, false, event.getEventTime(), false)) {
                 playSoundEffect(keyCodeToSoundsEffect(keyCode));
                 return true;
             }
@@ -3122,13 +3263,12 @@
             return false;
         }
 
-        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
-                || keyCode == KeyEvent.KEYCODE_ENTER) {
+        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
             switchOutDrawHistory();
             if (event.getRepeatCount() == 0) {
-                mGotEnterDown = true;
+                mGotCenterDown = true;
                 mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                        .obtainMessage(LONG_PRESS_ENTER), LONG_PRESS_TIMEOUT);
+                        .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
                 // Already checked mNativeClass, so we do not need to check it
                 // again.
                 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
@@ -3138,6 +3278,15 @@
             return false;
         }
 
+        if (keyCode != KeyEvent.KEYCODE_SHIFT_LEFT
+                && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
+            // turn off copy select if a shift-key combo is pressed
+            mExtendSelection = mShiftIsPressed = false;
+            if (mTouchMode == TOUCH_SELECT_MODE) {
+                mTouchMode = TOUCH_INIT_MODE;
+            }
+        }
+
         if (getSettings().getNavDump()) {
             switch (keyCode) {
                 case KeyEvent.KEYCODE_4:
@@ -3166,8 +3315,29 @@
             }
         }
 
+        if (nativeCursorIsPlugin()) {
+            nativeUpdatePluginReceivesEvents();
+            invalidate();
+        } else if (nativeCursorIsTextInput()) {
+            // This message will put the node in focus, for the DOM's notion
+            // of focus, and make the focuscontroller active
+            mWebViewCore.sendMessage(EventHub.CLICK);
+            // This will bring up the WebTextView and put it in focus, for
+            // our view system's notion of focus
+            rebuildWebTextView();
+            // Now we need to pass the event to it
+            return mWebTextView.onKeyDown(keyCode, event);
+        } else if (nativeHasFocusNode()) {
+            // In this case, the cursor is not on a text input, but the focus
+            // might be.  Check it, and if so, hand over to the WebTextView.
+            rebuildWebTextView();
+            if (inEditingMode()) {
+                return mWebTextView.onKeyDown(keyCode, event);
+            }
+        }
+
         // TODO: should we pass all the keys to DOM or check the meta tag
-        if (nativeFocusNodeWantsKeyEvents() || true) {
+        if (nativeCursorWantsKeyEvents() || true) {
             // pass the key to DOM
             mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
             // return true as DOM handles the key
@@ -3180,20 +3350,19 @@
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
-                    + ", " + event);
+                    + ", " + event + ", unicode=" + event.getUnicodeChar());
         }
 
         if (mNativeClass == 0) {
             return false;
         }
 
-        // special CALL handling when focus node's href is "tel:XXX"
-        if (keyCode == KeyEvent.KEYCODE_CALL && nativeUpdateFocusNode()) {
-            FocusNode node = mFocusNode;
-            String text = node.mText;
-            if (!node.mIsTextField && !node.mIsTextArea && text != null
+        // special CALL handling when cursor node's href is "tel:XXX"
+        if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
+            String text = nativeCursorText();
+            if (!nativeCursorIsTextInput() && text != null
                     && text.startsWith(SCHEME_TEL)) {
                 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
                 getContext().startActivity(intent);
@@ -3220,7 +3389,7 @@
             return false;
         }
 
-        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT 
+        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
             if (commitCopy()) {
                 return true;
@@ -3234,55 +3403,30 @@
             return false;
         }
 
-        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
-                || keyCode == KeyEvent.KEYCODE_ENTER) {
+        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
             // remove the long press message first
-            mPrivateHandler.removeMessages(LONG_PRESS_ENTER);
-            mGotEnterDown = false;
+            mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
+            mGotCenterDown = false;
 
-            if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode) {
-                if (mShiftIsPressed) {
-                    return false;
-                }
-                if (getSettings().supportZoom()) {
-                    if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
-                        zoomScrollOut();
-                    } else {
-                        if (LOGV_ENABLED) {
-                            Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
-                        }
-                        mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                                .obtainMessage(SWITCH_TO_ENTER), TAP_TIMEOUT);
-                        mTouchMode = TOUCH_DOUBLECLICK_MODE;
-                    }
-                    return true;
-                }
+            if (mShiftIsPressed) {
+                return false;
             }
-
-            Rect visibleRect = sendOurVisibleRect();
-            // Note that sendOurVisibleRect calls viewToContent, so the
-            // coordinates should be in content coordinates.
-            if (nativeUpdateFocusNode()) {
-                if (Rect.intersects(mFocusNode.mBounds, visibleRect)) {
-                    nativeSetFollowedLink(true);
-                    mWebViewCore.sendMessage(EventHub.SET_FINAL_FOCUS,
-                            EventHub.BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP, 0,
-                            new WebViewCore.FocusData(mFocusData));
-                    playSoundEffect(SoundEffectConstants.CLICK);
-                    if (!mCallbackProxy.uiOverrideUrlLoading(mFocusNode.mText)) {
-                        // use CLICK instead of KEY_DOWN/KEY_UP so that we can
-                        // trigger mouse click events
-                        mWebViewCore.sendMessage(EventHub.CLICK);
-                    }
+            if (getSettings().supportZoom()
+                    && mTouchMode == TOUCH_DOUBLECLICK_MODE) {
+                zoomScrollOut();
+            } else {
+                mPrivateHandler.sendMessageDelayed(mPrivateHandler
+                        .obtainMessage(SWITCH_TO_CLICK), TAP_TIMEOUT);
+                if (DebugFlags.WEB_VIEW) {
+                    Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
                 }
-                return true;
+                mTouchMode = TOUCH_DOUBLECLICK_MODE;
             }
-            // Bubble up the key event as WebView doesn't handle it
-            return false;
+            return true;
         }
 
         // TODO: should we pass all the keys to DOM or check the meta tag
-        if (nativeFocusNodeWantsKeyEvents() || true) {
+        if (nativeCursorWantsKeyEvents() || true) {
             // pass the key to DOM
             mWebViewCore.sendMessage(EventHub.KEY_UP, event);
             // return true as DOM handles the key
@@ -3292,16 +3436,14 @@
         // Bubble up the key event as WebView doesn't handle it
         return false;
     }
-    
+
     /**
      * @hide
      */
     public void emulateShiftHeld() {
         mExtendSelection = false;
         mShiftIsPressed = true;
-        int contentX = viewToContent((int) mLastTouchX + mScrollX);
-        int contentY = viewToContent((int) mLastTouchY + mScrollY);
-        nativeClearFocus(contentX, contentY);
+        nativeHideCursor();
     }
 
     private boolean commitCopy() {
@@ -3349,16 +3491,13 @@
         // Clean up the zoom controller
         mZoomButtonsController.setVisible(false);
     }
-    
+
     // Implementation for OnHierarchyChangeListener
     public void onChildViewAdded(View parent, View child) {}
-    
+
     public void onChildViewRemoved(View p, View child) {
         if (child == this) {
-            if (inEditingMode()) {
-                clearTextEntry();
-                mNeedsUpdateTextEntry = true;
-            }
+            clearTextEntry();
         }
     }
 
@@ -3371,26 +3510,25 @@
     public void onGlobalFocusChanged(View oldFocus, View newFocus) {
     }
 
-    // To avoid drawing the focus ring, and remove the TextView when our window
+    // To avoid drawing the cursor ring, and remove the TextView when our window
     // loses focus.
     @Override
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         if (hasWindowFocus) {
             if (hasFocus()) {
                 // If our window regained focus, and we have focus, then begin
-                // drawing the focus ring, and restore the TextView if
-                // necessary.
-                mDrawFocusRing = true;
-                if (mNeedsUpdateTextEntry) {
-                    updateTextEntry();
-                }
+                // drawing the cursor ring
+                mDrawCursorRing = true;
                 if (mNativeClass != 0) {
                     nativeRecordButtons(true, false, true);
+                    if (inEditingMode()) {
+                        mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 1, 0);
+                    }
                 }
             } else {
                 // If our window gained focus, but we do not have it, do not
-                // draw the focus ring.
-                mDrawFocusRing = false;
+                // draw the cursor ring.
+                mDrawCursorRing = false;
                 // We do not call nativeRecordButtons here because we assume
                 // that when we lost focus, or window focus, it got called with
                 // false for the first parameter
@@ -3399,39 +3537,49 @@
             if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
                 /*
                  * The zoom controls come in their own window, so our window
-                 * loses focus. Our policy is to not draw the focus ring if
+                 * loses focus. Our policy is to not draw the cursor ring if
                  * our window is not focused, but this is an exception since
                  * the user can still navigate the web page with the zoom
                  * controls showing.
                  */
-                // If our window has lost focus, stop drawing the focus ring
-                mDrawFocusRing = false;
+                // If our window has lost focus, stop drawing the cursor ring
+                mDrawCursorRing = false;
             }
             mGotKeyDown = false;
             mShiftIsPressed = false;
             if (mNativeClass != 0) {
                 nativeRecordButtons(false, false, true);
             }
+            setFocusControllerInactive();
         }
         invalidate();
         super.onWindowFocusChanged(hasWindowFocus);
     }
 
+    /*
+     * Pass a message to WebCore Thread, telling the WebCore::Page's
+     * FocusController to be  "inactive" so that it will
+     * not draw the blinking cursor.  It gets set to "active" to draw the cursor
+     * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
+     */
+    private void setFocusControllerInactive() {
+        // Do not need to also check whether mWebViewCore is null, because
+        // mNativeClass is only set if mWebViewCore is non null
+        if (mNativeClass == 0) return;
+        mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 0, 0);
+    }
+
     @Override
     protected void onFocusChanged(boolean focused, int direction,
             Rect previouslyFocusedRect) {
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
         }
         if (focused) {
             // When we regain focus, if we have window focus, resume drawing
-            // the focus ring, and add the TextView if necessary.
+            // the cursor ring
             if (hasWindowFocus()) {
-                mDrawFocusRing = true;
-                if (mNeedsUpdateTextEntry) {
-                    updateTextEntry();
-                    mNeedsUpdateTextEntry = false;
-                }
+                mDrawCursorRing = true;
                 if (mNativeClass != 0) {
                     nativeRecordButtons(true, false, true);
                 }
@@ -3442,12 +3590,13 @@
             }
         } else {
             // When we lost focus, unless focus went to the TextView (which is
-            // true if we are in editing mode), stop drawing the focus ring.
+            // true if we are in editing mode), stop drawing the cursor ring.
             if (!inEditingMode()) {
-                mDrawFocusRing = false;
+                mDrawCursorRing = false;
                 if (mNativeClass != 0) {
                     nativeRecordButtons(false, false, true);
                 }
+                setFocusControllerInactive();
             }
             mGotKeyDown = false;
         }
@@ -3478,8 +3627,8 @@
         super.onScrollChanged(l, t, oldl, oldt);
         sendOurVisibleRect();
     }
-    
-    
+
+
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         boolean dispatch = true;
@@ -3523,7 +3672,7 @@
             return false;
         }
 
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
                     + mTouchMode);
         }
@@ -3548,7 +3697,7 @@
         if (mForwardTouchEvents && mTouchMode != SCROLL_ZOOM_OUT
                 && mTouchMode != SCROLL_ZOOM_ANIMATION_IN
                 && mTouchMode != SCROLL_ZOOM_ANIMATION_OUT
-                && (action != MotionEvent.ACTION_MOVE || 
+                && (action != MotionEvent.ACTION_MOVE ||
                         eventTime - mLastSentTouchTime > TOUCH_SENT_INTERVAL)) {
             WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
             ted.mAction = action;
@@ -3579,7 +3728,7 @@
                     mSelectX = mScrollX + (int) x;
                     mSelectY = mScrollY + (int) y;
                     mTouchMode = TOUCH_SELECT_MODE;
-                    if (LOGV_ENABLED) {
+                    if (DebugFlags.WEB_VIEW) {
                         Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
                     }
                     nativeMoveSelection(viewToContent(mSelectX)
@@ -3607,7 +3756,7 @@
                 break;
             }
             case MotionEvent.ACTION_MOVE: {
-                if (mTouchMode == TOUCH_DONE_MODE 
+                if (mTouchMode == TOUCH_DONE_MODE
                         || mTouchMode == SCROLL_ZOOM_ANIMATION_IN
                         || mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
                     // no dragging during scroll zoom animation
@@ -3624,7 +3773,7 @@
                     if (mTouchMode == TOUCH_SELECT_MODE) {
                         mSelectX = mScrollX + (int) x;
                         mSelectY = mScrollY + (int) y;
-                        if (LOGV_ENABLED) {
+                        if (DebugFlags.WEB_VIEW) {
                             Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
                         }
                         nativeMoveSelection(viewToContent(mSelectX)
@@ -3657,12 +3806,7 @@
 
                     mTouchMode = TOUCH_DRAG_MODE;
                     WebViewCore.pauseUpdate(mWebViewCore);
-                    int contentX = viewToContent((int) x + mScrollX);
-                    int contentY = viewToContent((int) y + mScrollY);
-                    if (inEditingMode()) {
-                        mTextEntry.updateCachedTextfield();
-                    }
-                    nativeClearFocus(contentX, contentY);
+                    nativeHideCursor();
                     // remove the zoom anchor if there is any
                     if (mZoomScale != 0) {
                         mWebViewCore
@@ -3672,7 +3816,7 @@
                     if (settings.supportZoom()
                             && settings.getBuiltInZoomControls()
                             && !mZoomButtonsController.isVisible()
-                            && (canZoomScrollOut() || 
+                            && (canZoomScrollOut() ||
                                     mMinZoomScale < mMaxZoomScale)) {
                         mZoomButtonsController.setVisible(true);
                     }
@@ -3698,7 +3842,7 @@
                             }
                             // reverse direction means lock in the snap mode
                             if ((ax > MAX_SLOPE_FOR_DIAG * ay) &&
-                                    ((mSnapPositive && 
+                                    ((mSnapPositive &&
                                     deltaX < -mMinLockSnapReverseDistance)
                                     || (!mSnapPositive &&
                                     deltaX > mMinLockSnapReverseDistance))) {
@@ -3712,9 +3856,9 @@
                             }
                             // reverse direction means lock in the snap mode
                             if ((ay > MAX_SLOPE_FOR_DIAG * ax) &&
-                                    ((mSnapPositive && 
+                                    ((mSnapPositive &&
                                     deltaY < -mMinLockSnapReverseDistance)
-                                    || (!mSnapPositive && 
+                                    || (!mSnapPositive &&
                                     deltaY > mMinLockSnapReverseDistance))) {
                                 mSnapScrollMode = SNAP_Y_LOCK;
                             }
@@ -3779,7 +3923,7 @@
                         // no action during scroll animation
                         break;
                     case SCROLL_ZOOM_OUT:
-                        if (LOGV_ENABLED) {
+                        if (DebugFlags.WEB_VIEW) {
                             Log.v(LOGTAG, "ACTION_UP SCROLL_ZOOM_OUT"
                                     + " eventTime - mLastTouchTime="
                                     + (eventTime - mLastTouchTime));
@@ -3837,18 +3981,13 @@
                 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
                 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
                 mTouchMode = TOUCH_DONE_MODE;
-                int contentX = viewToContent((int) mLastTouchX + mScrollX);
-                int contentY = viewToContent((int) mLastTouchY + mScrollY);
-                if (inEditingMode()) {
-                    mTextEntry.updateCachedTextfield();
-                }
-                nativeClearFocus(contentX, contentY);
+                nativeHideCursor();
                 break;
             }
         }
         return true;
     }
-    
+
     private long mTrackballFirstTime = 0;
     private long mTrackballLastTime = 0;
     private float mTrackballRemainsX = 0.0f;
@@ -3870,14 +4009,14 @@
     private boolean mShiftIsPressed = false;
     private boolean mTrackballDown = false;
     private long mTrackballUpTime = 0;
-    private long mLastFocusTime = 0;
-    private Rect mLastFocusBounds;
+    private long mLastCursorTime = 0;
+    private Rect mLastCursorBounds;
 
     // Set by default; BrowserActivity clears to interpret trackball data
-    // directly for movement. Currently, the framework only passes 
+    // directly for movement. Currently, the framework only passes
     // arrow key events, not trackball events, from one child to the next
     private boolean mMapTrackballToArrowKeys = true;
-    
+
     public void setMapTrackballToArrowKeys(boolean setMap) {
         mMapTrackballToArrowKeys = setMap;
     }
@@ -3895,26 +4034,27 @@
             return true;
         }
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mPrivateHandler.removeMessages(SWITCH_TO_ENTER);
+            mPrivateHandler.removeMessages(SWITCH_TO_CLICK);
             mTrackballDown = true;
-            if (mNativeClass != 0) {
-                nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
+            if (mNativeClass == 0) {
+                return false;
             }
-            if (time - mLastFocusTime <= TRACKBALL_TIMEOUT
-                    && !mLastFocusBounds.equals(nativeGetFocusRingBounds())) {
-                nativeSelectBestAt(mLastFocusBounds);
+            nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
+            if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
+                    && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) {
+                nativeSelectBestAt(mLastCursorBounds);
             }
-            if (LOGV_ENABLED) {
+            if (DebugFlags.WEB_VIEW) {
                 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
-                        + " time=" + time 
-                        + " mLastFocusTime=" + mLastFocusTime);
+                        + " time=" + time
+                        + " mLastCursorTime=" + mLastCursorTime);
             }
             if (isInTouchMode()) requestFocusFromTouch();
             return false; // let common code in onKeyDown at it
-        } 
+        }
         if (ev.getAction() == MotionEvent.ACTION_UP) {
-            // LONG_PRESS_ENTER is set in common onKeyDown
-            mPrivateHandler.removeMessages(LONG_PRESS_ENTER);
+            // LONG_PRESS_CENTER is set in common onKeyDown
+            mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
             mTrackballDown = false;
             mTrackballUpTime = time;
             if (mShiftIsPressed) {
@@ -3924,42 +4064,42 @@
                     mExtendSelection = true;
                 }
             }
-            if (LOGV_ENABLED) {
+            if (DebugFlags.WEB_VIEW) {
                 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
-                        + " time=" + time 
+                        + " time=" + time
                 );
             }
             return false; // let common code in onKeyUp at it
         }
         if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
-            if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent gmail quit");
+            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
             return false;
         }
-        // no move if we're still waiting on SWITCH_TO_ENTER timeout
+        // no move if we're still waiting on SWITCH_TO_CLICK timeout
         if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
-            if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent 2 click quit");
+            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent 2 click quit");
             return true;
         }
         if (mTrackballDown) {
-            if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent down quit");
+            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
             return true; // discard move if trackball is down
         }
         if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
-            if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
+            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
             return true;
         }
         // TODO: alternatively we can do panning as touch does
         switchOutDrawHistory();
         if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
-            if (LOGV_ENABLED) {
-                Log.v(LOGTAG, "onTrackballEvent time=" 
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "onTrackballEvent time="
                         + time + " last=" + mTrackballLastTime);
             }
             mTrackballFirstTime = time;
             mTrackballXMove = mTrackballYMove = 0;
         }
         mTrackballLastTime = time;
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
         }
         mTrackballRemainsX += ev.getX();
@@ -3967,7 +4107,7 @@
         doTrackball(time);
         return true;
     }
-    
+
     void moveSelection(float xRate, float yRate) {
         if (mNativeClass == 0)
             return;
@@ -3981,8 +4121,8 @@
                 , mSelectX));
         mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
                 , mSelectY));
-        if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "moveSelection" 
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "moveSelection"
                     + " mSelectX=" + mSelectX
                     + " mSelectY=" + mSelectY
                     + " mScrollX=" + mScrollX
@@ -3994,10 +4134,10 @@
         nativeMoveSelection(viewToContent(mSelectX)
                 , viewToContent(mSelectY), mExtendSelection);
         int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
-                : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET 
+                : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
                 : 0;
         int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
-                : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET 
+                : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
                 : 0;
         pinScrollBy(scrollX, scrollY, true, 0);
         Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
@@ -4054,7 +4194,7 @@
         if (elapsed == 0) {
             elapsed = TRACKBALL_TIMEOUT;
         }
-        float xRate = mTrackballRemainsX * 1000 / elapsed; 
+        float xRate = mTrackballRemainsX * 1000 / elapsed;
         float yRate = mTrackballRemainsY * 1000 / elapsed;
         if (mShiftIsPressed) {
             moveSelection(xRate, yRate);
@@ -4064,7 +4204,7 @@
         float ax = Math.abs(xRate);
         float ay = Math.abs(yRate);
         float maxA = Math.max(ax, ay);
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
                     + " xRate=" + xRate
                     + " yRate=" + yRate
@@ -4081,9 +4221,9 @@
             int maxWH = Math.max(width, height);
             mZoomScrollX += scaleTrackballX(xRate, maxWH);
             mZoomScrollY += scaleTrackballY(yRate, maxWH);
-            if (LOGV_ENABLED) {
+            if (DebugFlags.WEB_VIEW) {
                 Log.v(LOGTAG, "doTrackball SCROLL_ZOOM_OUT"
-                        + " mZoomScrollX=" + mZoomScrollX 
+                        + " mZoomScrollX=" + mZoomScrollX
                         + " mZoomScrollY=" + mZoomScrollY);
             }
             mZoomScrollX = Math.min(width, Math.max(0, mZoomScrollX));
@@ -4101,18 +4241,18 @@
         int oldScrollX = mScrollX;
         int oldScrollY = mScrollY;
         if (count > 0) {
-            int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ? 
-                    KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN : 
+            int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
+                    KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
                     mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
                     KeyEvent.KEYCODE_DPAD_RIGHT;
             count = Math.min(count, TRACKBALL_MOVE_COUNT);
-            if (LOGV_ENABLED) {
-                Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode 
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
                         + " count=" + count
                         + " mTrackballRemainsX=" + mTrackballRemainsX
                         + " mTrackballRemainsY=" + mTrackballRemainsY);
             }
-            if (navHandledKey(selectKeyCode, count, false, time)) {
+            if (navHandledKey(selectKeyCode, count, false, time, false)) {
                 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
             }
             mTrackballRemainsX = mTrackballRemainsY = 0;
@@ -4120,12 +4260,12 @@
         if (count >= TRACKBALL_SCROLL_COUNT) {
             int xMove = scaleTrackballX(xRate, width);
             int yMove = scaleTrackballY(yRate, height);
-            if (LOGV_ENABLED) {
+            if (DebugFlags.WEB_VIEW) {
                 Log.v(LOGTAG, "doTrackball pinScrollBy"
                         + " count=" + count
                         + " xMove=" + xMove + " yMove=" + yMove
-                        + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX) 
-                        + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY) 
+                        + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
+                        + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
                         );
             }
             if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
@@ -4138,18 +4278,17 @@
                 pinScrollBy(xMove, yMove, true, 0);
             }
             mUserScroll = true;
-        } 
-        mWebViewCore.sendMessage(EventHub.UNBLOCK_FOCUS);        
+        }
     }
 
     public void flingScroll(int vx, int vy) {
         int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
         int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0);
-        
+
         mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
         invalidate();
     }
-    
+
     private void doFling() {
         if (mVelocityTracker == null) {
             return;
@@ -4168,7 +4307,7 @@
                 vx = 0;
             }
         }
-        
+
         if (true /* EMG release: make our fling more like Maps' */) {
             // maps cuts their velocity in half
             vx = vx * 3 / 4;
@@ -4229,7 +4368,7 @@
         }
         if (mZoomControls == null) {
             mZoomControls = createZoomControls();
-            
+
             /*
              * need to be set to VISIBLE first so that getMeasuredHeight() in
              * {@link #onSizeChanged()} can return the measured value for proper
@@ -4238,7 +4377,7 @@
             mZoomControls.setVisibility(View.VISIBLE);
             mZoomControlRunnable = new Runnable() {
                 public void run() {
-                    
+
                     /* Don't dismiss the controls if the user has
                      * focus on them. Wait and check again later.
                      */
@@ -4290,7 +4429,7 @@
     /**
      * Gets the {@link ZoomButtonsController} which can be used to add
      * additional buttons to the zoom controls window.
-     * 
+     *
      * @return The instance of {@link ZoomButtonsController} used by this class,
      *         or null if it is unavailable.
      * @hide
@@ -4328,19 +4467,15 @@
         int contentY = viewToContent((int) mLastTouchY + mScrollY);
         Rect rect = new Rect(contentX - mNavSlop, contentY - mNavSlop,
                 contentX + mNavSlop, contentY + mNavSlop);
-        // If we were already focused on a textfield, update its cache.
-        if (inEditingMode()) {
-            mTextEntry.updateCachedTextfield();
-        }
         nativeSelectBestAt(rect);
     }
 
     /*package*/ void shortPressOnTextField() {
         if (inEditingMode()) {
-            View v = mTextEntry;
+            View v = mWebTextView;
             int x = viewToContent((v.getLeft() + v.getRight()) >> 1);
             int y = viewToContent((v.getTop() + v.getBottom()) >> 1);
-            nativeMotionUp(x, y, mNavSlop, true);
+            nativeMotionUp(x, y, mNavSlop);
         }
     }
 
@@ -4352,14 +4487,13 @@
         // mLastTouchX and mLastTouchY are the point in the current viewport
         int contentX = viewToContent((int) mLastTouchX + mScrollX);
         int contentY = viewToContent((int) mLastTouchY + mScrollY);
-        if (nativeMotionUp(contentX, contentY, mNavSlop, true)) {
+        if (nativeMotionUp(contentX, contentY, mNavSlop)) {
             if (mLogEvent) {
                 Checkin.updateStats(mContext.getContentResolver(),
                         Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0);
             }
         }
-        if (nativeUpdateFocusNode() && !mFocusNode.mIsTextField
-                && !mFocusNode.mIsTextArea) {
+        if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
             playSoundEffect(SoundEffectConstants.CLICK);
         }
     }
@@ -4374,7 +4508,8 @@
     public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
         boolean result = false;
         if (inEditingMode()) {
-            result = mTextEntry.requestFocus(direction, previouslyFocusedRect);
+            result = mWebTextView.requestFocus(direction,
+                    previouslyFocusedRect);
         } else {
             result = super.requestFocus(direction, previouslyFocusedRect);
             if (mWebViewCore.getSettings().getNeedInitialFocus()) {
@@ -4398,8 +4533,8 @@
                     default:
                         return result;
                 }
-                if (mNativeClass != 0 && !nativeUpdateFocusNode()) {
-                    navHandledKey(fakeKeyDirection, 1, true, 0);
+                if (mNativeClass != 0 && !nativeHasCursorNode()) {
+                    navHandledKey(fakeKeyDirection, 1, true, 0, true);
                 }
             }
         }
@@ -4467,14 +4602,19 @@
 
         int scrollYDelta = 0;
 
-        if (rect.bottom > screenBottom && rect.top > screenTop) {
-            if (rect.height() > height) {
-                scrollYDelta += (rect.top - screenTop);
+        if (rect.bottom > screenBottom) {
+            int oneThirdOfScreenHeight = height / 3;
+            if (rect.height() > 2 * oneThirdOfScreenHeight) {
+                // If the rectangle is too tall to fit in the bottom two thirds
+                // of the screen, place it at the top.
+                scrollYDelta = rect.top - screenTop;
             } else {
-                scrollYDelta += (rect.bottom - screenBottom);
+                // If the rectangle will still fit on screen, we want its
+                // top to be in the top third of the screen.
+                scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
             }
         } else if (rect.top < screenTop) {
-            scrollYDelta -= (screenTop - rect.top);
+            scrollYDelta = rect.top - screenTop;
         }
 
         int width = getWidth() - getVerticalScrollbarWidth();
@@ -4499,33 +4639,34 @@
 
         return false;
     }
-    
+
     /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
             String replace, int newStart, int newEnd) {
-        HashMap arg = new HashMap();
-        arg.put("focusData", new WebViewCore.FocusData(mFocusData));
-        arg.put("replace", replace);
-        arg.put("start", new Integer(newStart));
-        arg.put("end", new Integer(newEnd));
+        WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
+        arg.mReplace = replace;
+        arg.mNewStart = newStart;
+        arg.mNewEnd = newEnd;
         mTextGeneration++;
         mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
     }
 
     /* package */ void passToJavaScript(String currentText, KeyEvent event) {
-        HashMap arg = new HashMap();
-        arg.put("focusData", new WebViewCore.FocusData(mFocusData));
-        arg.put("event", event);
-        arg.put("currentText", currentText);
+        if (nativeCursorWantsKeyEvents() && !nativeCursorMatchesFocus()) {
+            mWebViewCore.sendMessage(EventHub.CLICK);
+        }
+        WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
+        arg.mEvent = event;
+        arg.mCurrentText = currentText;
         // Increase our text generation number, and pass it to webcore thread
         mTextGeneration++;
         mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
         // WebKit's document state is not saved until about to leave the page.
-        // To make sure the host application, like Browser, has the up to date 
-        // document state when it goes to background, we force to save the 
+        // To make sure the host application, like Browser, has the up to date
+        // document state when it goes to background, we force to save the
         // document state.
         mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
         mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
-                new WebViewCore.FocusData(mFocusData), 1000);
+                cursorData(), 1000);
     }
 
     /* package */ WebViewCore getWebViewCore() {
@@ -4543,9 +4684,9 @@
     class PrivateHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
-            if (LOGV_ENABLED) {
-                Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what 
-                        > INVAL_RECT_MSG_ID ? Integer.toString(msg.what) 
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
+                        > INVAL_RECT_MSG_ID ? Integer.toString(msg.what)
                         : HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
             }
             switch (msg.what) {
@@ -4574,23 +4715,42 @@
                     if (!mPreventDrag) {
                         mTouchMode = TOUCH_DONE_MODE;
                         performLongClick();
-                        updateTextEntry();
+                        rebuildWebTextView();
                     }
                     break;
                 }
-                case SWITCH_TO_ENTER:
-                    if (LOGV_ENABLED) Log.v(LOGTAG, "SWITCH_TO_ENTER");
+                case SWITCH_TO_CLICK:
+                    // The user clicked with the trackball, and did not click a
+                    // second time, so perform the action of a trackball single
+                    // click
                     mTouchMode = TOUCH_DONE_MODE;
-                    onKeyUp(KeyEvent.KEYCODE_ENTER
-                            , new KeyEvent(KeyEvent.ACTION_UP
-                            , KeyEvent.KEYCODE_ENTER));
+                    Rect visibleRect = sendOurVisibleRect();
+                    // Note that sendOurVisibleRect calls viewToContent, so the
+                    // coordinates should be in content coordinates.
+                    if (!nativeCursorIntersects(visibleRect)) {
+                        break;
+                    }
+                    nativeSetFollowedLink(true);
+                    nativeUpdatePluginReceivesEvents();
+                    WebViewCore.CursorData data = cursorData();
+                    mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
+                    playSoundEffect(SoundEffectConstants.CLICK);
+                    boolean isTextInput = nativeCursorIsTextInput();
+                    if (isTextInput || !mCallbackProxy.uiOverrideUrlLoading(
+                                nativeCursorText())) {
+                        mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
+                                nativeCursorNodePointer());
+                    }
+                    if (isTextInput) {
+                        rebuildWebTextView();
+                    }
                     break;
                 case SCROLL_BY_MSG_ID:
                     setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);
                     break;
                 case SYNC_SCROLL_TO_MSG_ID:
                     if (mUserScroll) {
-                        // if user has scrolled explicitly, don't sync the 
+                        // if user has scrolled explicitly, don't sync the
                         // scroll position any more
                         mUserScroll = false;
                         break;
@@ -4599,7 +4759,7 @@
                 case SCROLL_TO_MSG_ID:
                     if (setContentScrollTo(msg.arg1, msg.arg2)) {
                         // if we can't scroll to the exact position due to pin,
-                        // send a message to WebCore to re-scroll when we get a 
+                        // send a message to WebCore to re-scroll when we get a
                         // new picture
                         mUserScroll = false;
                         mWebViewCore.sendMessage(EventHub.SYNC_SCROLL,
@@ -4611,7 +4771,7 @@
                     break;
                 case NEW_PICTURE_MSG_ID:
                     // called for new content
-                    final WebViewCore.DrawData draw = 
+                    final WebViewCore.DrawData draw =
                             (WebViewCore.DrawData) msg.obj;
                     final Point viewSize = draw.mViewPoint;
                     if (mZoomScale > 0) {
@@ -4634,9 +4794,9 @@
                     // received in the fixed dimension.
                     final boolean updateLayout = viewSize.x == mLastWidthSent
                             && viewSize.y == mLastHeightSent;
-                    recordNewContentSize(draw.mWidthHeight.x, 
+                    recordNewContentSize(draw.mWidthHeight.x,
                             draw.mWidthHeight.y, updateLayout);
-                    if (LOGV_ENABLED) {
+                    if (DebugFlags.WEB_VIEW) {
                         Rect b = draw.mInvalRegion.getBounds();
                         Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
                                 b.left+","+b.top+","+b.right+","+b.bottom+"}");
@@ -4652,22 +4812,23 @@
                     break;
                 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
                     // Make sure that the textfield is currently focused
-                    // and representing the same node as the pointer.  
-                    if (inEditingMode() && 
-                            mTextEntry.isSameTextField(msg.arg1)) {
+                    // and representing the same node as the pointer.
+                    if (inEditingMode() &&
+                            mWebTextView.isSameTextField(msg.arg1)) {
                         if (msg.getData().getBoolean("password")) {
-                            Spannable text = (Spannable) mTextEntry.getText();
+                            Spannable text = (Spannable) mWebTextView.getText();
                             int start = Selection.getSelectionStart(text);
                             int end = Selection.getSelectionEnd(text);
-                            mTextEntry.setInPassword(true);
+                            mWebTextView.setInPassword(true);
                             // Restore the selection, which may have been
                             // ruined by setInPassword.
-                            Spannable pword = (Spannable) mTextEntry.getText();
+                            Spannable pword =
+                                    (Spannable) mWebTextView.getText();
                             Selection.setSelection(pword, start, end);
                         // If the text entry has created more events, ignore
                         // this one.
                         } else if (msg.arg2 == mTextGeneration) {
-                            mTextEntry.setTextAndKeepSelection(
+                            mWebTextView.setTextAndKeepSelection(
                                     (String) msg.obj);
                         }
                     }
@@ -4676,14 +4837,8 @@
                     if (mNativeClass == 0) {
                         break;
                     }
-// Do not reset the focus or clear the text; the user may have already
-// navigated or entered text at this point. The focus should have gotten 
-// reset, if need be, when the focus cache was built. Similarly, the text
-// view should already be torn down and rebuilt if needed.
-//                    nativeResetFocus();
-//                    clearTextEntry();
-                    HashMap scaleLimit = (HashMap) msg.obj;
-                    int minScale = (Integer) scaleLimit.get("minScale");
+                    ScaleLimitData scaleLimit = (ScaleLimitData) msg.obj;
+                    int minScale = scaleLimit.mMinScale;
                     if (minScale == 0) {
                         mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
                         mMinZoomScaleFixed = false;
@@ -4691,7 +4846,7 @@
                         mMinZoomScale = (float) (minScale / 100.0);
                         mMinZoomScaleFixed = true;
                     }
-                    int maxScale = (Integer) scaleLimit.get("maxScale");
+                    int maxScale = scaleLimit.mMaxScale;
                     if (maxScale == 0) {
                         mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
                     } else {
@@ -4712,6 +4867,7 @@
                     if (mInitialScale > 0) {
                         scale = mInitialScale / 100.0f;
                     } else  {
+                        if (initialScale < 0) break;
                         if (mWebViewCore.getSettings().getUseWideViewPort()) {
                             // force viewSizeChanged by setting mLastWidthSent
                             // to 0
@@ -4729,30 +4885,21 @@
                     }
                     setNewZoomScale(scale, false);
                     break;
-                case MARK_NODE_INVALID_ID:
-                    nativeMarkNodeInvalid(msg.arg1);
-                    break;
-                case NOTIFY_FOCUS_SET_MSG_ID:
-                    if (mNativeClass != 0) {
-                        nativeNotifyFocusSet(inEditingMode());
+                case MOVE_OUT_OF_PLUGIN:
+                    if (nativePluginEatsNavKey()) {
+                        navHandledKey(msg.arg1, 1, false, 0, true);
                     }
                     break;
                 case UPDATE_TEXT_ENTRY_MSG_ID:
-                    // this is sent after finishing resize in WebViewCore. Make 
+                    // this is sent after finishing resize in WebViewCore. Make
                     // sure the text edit box is still on the  screen.
-                    boolean alreadyThere = inEditingMode();
-                    if (alreadyThere && nativeUpdateFocusNode()) {
-                        FocusNode node = mFocusNode;
-                        if (node.mIsTextField || node.mIsTextArea) {
-                            mTextEntry.bringIntoView();
-                        }
+                    if (inEditingMode() && nativeCursorIsTextInput()) {
+                        mWebTextView.bringIntoView();
                     }
-                    updateTextEntry();
+                    rebuildWebTextView();
                     break;
-                case RECOMPUTE_FOCUS_MSG_ID:
-                    if (mNativeClass != 0) {
-                        nativeRecomputeFocus();
-                    }
+                case CLEAR_TEXT_ENTRY:
+                    clearTextEntry();
                     break;
                 case INVAL_RECT_MSG_ID: {
                     Rect r = (Rect)msg.obj;
@@ -4765,17 +4912,15 @@
                     }
                     break;
                 }
-                case UPDATE_TEXT_ENTRY_ADAPTER:
-                    HashMap data = (HashMap) msg.obj;
-                    if (mTextEntry.isSameTextField(msg.arg1)) {
-                        AutoCompleteAdapter adapter =
-                                (AutoCompleteAdapter) data.get("adapter");
-                        mTextEntry.setAdapterCustom(adapter);
+                case REQUEST_FORM_DATA:
+                    AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
+                    if (mWebTextView.isSameTextField(msg.arg1)) {
+                        mWebTextView.setAdapterCustom(adapter);
                     }
                     break;
                 case UPDATE_CLIPBOARD:
                     String str = (String) msg.obj;
-                    if (LOGV_ENABLED) {
+                    if (DebugFlags.WEB_VIEW) {
                         Log.v(LOGTAG, "UPDATE_CLIPBOARD " + str);
                     }
                     try {
@@ -4790,12 +4935,12 @@
                     WebViewCore.resumeUpdate(mWebViewCore);
                     break;
 
-                case LONG_PRESS_ENTER:
+                case LONG_PRESS_CENTER:
                     // as this is shared by keydown and trackballdown, reset all
                     // the states
-                    mGotEnterDown = false;
+                    mGotCenterDown = false;
                     mTrackballDown = false;
-                    // LONG_PRESS_ENTER is sent as a delayed message. If we
+                    // LONG_PRESS_CENTER is sent as a delayed message. If we
                     // switch to windows overview, the WebView will be
                     // temporarily removed from the view system. In that case,
                     // do nothing.
@@ -4817,6 +4962,14 @@
                     }
                     break;
 
+                case REQUEST_KEYBOARD:
+                    if (msg.arg1 == 0) {
+                        hideSoftKeyboard();
+                    } else {
+                        displaySoftKeyboard(false);
+                    }
+                    break;
+
                 default:
                     super.handleMessage(msg);
                     break;
@@ -4826,16 +4979,12 @@
 
     // Class used to use a dropdown for a <select> element
     private class InvokeListBox implements Runnable {
-        // Strings for the labels in the listbox.
-        private String[]    mArray;
-        // Array representing whether each item is enabled.
-        private boolean[]   mEnableArray;
         // Whether the listbox allows multiple selection.
         private boolean     mMultiple;
         // Passed in to a list with multiple selection to tell
         // which items are selected.
         private int[]       mSelectedArray;
-        // Passed in to a list with single selection to tell 
+        // Passed in to a list with single selection to tell
         // where the initial selection is.
         private int         mSelection;
 
@@ -4854,14 +5003,14 @@
         }
 
         /**
-         *  Subclass ArrayAdapter so we can disable OptionGroupLabels, 
+         *  Subclass ArrayAdapter so we can disable OptionGroupLabels,
          *  and allow filtering.
          */
         private class MyArrayListAdapter extends ArrayAdapter<Container> {
             public MyArrayListAdapter(Context context, Container[] objects, boolean multiple) {
-                super(context, 
+                super(context,
                             multiple ? com.android.internal.R.layout.select_dialog_multichoice :
-                            com.android.internal.R.layout.select_dialog_singlechoice, 
+                            com.android.internal.R.layout.select_dialog_singlechoice,
                             objects);
             }
 
@@ -4919,7 +5068,7 @@
             }
         }
 
-        private InvokeListBox(String[] array, boolean[] enabled, int 
+        private InvokeListBox(String[] array, boolean[] enabled, int
                 selection) {
             mSelection = selection;
             mMultiple = false;
@@ -4982,31 +5131,36 @@
         public void run() {
             final ListView listView = (ListView) LayoutInflater.from(mContext)
                     .inflate(com.android.internal.R.layout.select_dialog, null);
-            final MyArrayListAdapter adapter = new 
+            final MyArrayListAdapter adapter = new
                     MyArrayListAdapter(mContext, mContainers, mMultiple);
             AlertDialog.Builder b = new AlertDialog.Builder(mContext)
                     .setView(listView).setCancelable(true)
                     .setInverseBackgroundForced(true);
-                    
+
             if (mMultiple) {
                 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                     public void onClick(DialogInterface dialog, int which) {
                         mWebViewCore.sendMessage(
-                                EventHub.LISTBOX_CHOICES, 
+                                EventHub.LISTBOX_CHOICES,
                                 adapter.getCount(), 0,
                                 listView.getCheckedItemPositions());
                     }});
-                b.setNegativeButton(android.R.string.cancel, null);
+                b.setNegativeButton(android.R.string.cancel,
+                        new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int which) {
+                        mWebViewCore.sendMessage(
+                                EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
+                }});
             }
             final AlertDialog dialog = b.create();
             listView.setAdapter(adapter);
             listView.setFocusableInTouchMode(true);
             // There is a bug (1250103) where the checks in a ListView with
             // multiple items selected are associated with the positions, not
-            // the ids, so the items do not properly retain their checks when 
+            // the ids, so the items do not properly retain their checks when
             // filtered.  Do not allow filtering on multiple lists until
             // that bug is fixed.
-            
+
             listView.setTextFilterEnabled(!mMultiple);
             if (mMultiple) {
                 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
@@ -5069,48 +5223,40 @@
     }
 
     // called by JNI
-    private void sendFinalFocus(int frame, int node, int x, int y) {
-        WebViewCore.FocusData focusData = new WebViewCore.FocusData();
-        focusData.mFrame = frame;
-        focusData.mNode = node;
-        focusData.mX = x;
-        focusData.mY = y;
-        mWebViewCore.sendMessage(EventHub.SET_FINAL_FOCUS, 
-                EventHub.NO_FOCUS_CHANGE_BLOCK, 0, focusData);
+    private void sendMoveMouse(int frame, int node, int x, int y) {
+        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
+                new WebViewCore.CursorData(frame, node, x, y));
+    }
+
+    /*
+     * Send a mouse move event to the webcore thread.
+     *
+     * @param removeFocus Pass true if the "mouse" cursor is now over a node
+     *                    which wants key events, but it is not the focus. This
+     *                    will make the visual appear as though nothing is in
+     *                    focus.  Remove the WebTextView, if present, and stop
+     *                    drawing the blinking caret.
+     * called by JNI
+     */
+    private void sendMoveMouseIfLatest(boolean removeFocus) {
+        if (removeFocus) {
+            clearTextEntry();
+            setFocusControllerInactive();
+        }
+        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
+                cursorData());
     }
 
     // called by JNI
-    private void setFocusData(int moveGeneration, int buildGeneration,
-            int frame, int node, int x, int y, boolean ignoreNullFocus) {
-        mFocusData.mMoveGeneration = moveGeneration;
-        mFocusData.mBuildGeneration = buildGeneration;
-        mFocusData.mFrame = frame;
-        mFocusData.mNode = node;
-        mFocusData.mX = x;
-        mFocusData.mY = y;
-        mFocusData.mIgnoreNullFocus = ignoreNullFocus;
-    }
-    
-    // called by JNI
-    private void sendKitFocus() {
-        WebViewCore.FocusData focusData = new WebViewCore.FocusData(mFocusData);
-        mWebViewCore.sendMessage(EventHub.SET_KIT_FOCUS, focusData);
-    }
-
-    // called by JNI
-    private void sendMotionUp(int touchGeneration, int buildGeneration,
-            int frame, int node, int x, int y, int size, boolean isClick,
-            boolean retry) {
+    private void sendMotionUp(int touchGeneration,
+            int frame, int node, int x, int y, int size) {
         WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
         touchUpData.mMoveGeneration = touchGeneration;
-        touchUpData.mBuildGeneration = buildGeneration;
         touchUpData.mSize = size;
-        touchUpData.mIsClick = isClick;
-        touchUpData.mRetry = retry;
-        mFocusData.mFrame = touchUpData.mFrame = frame;
-        mFocusData.mNode = touchUpData.mNode = node;
-        mFocusData.mX = touchUpData.mX = x;
-        mFocusData.mY = touchUpData.mY = y;
+        touchUpData.mFrame = frame;
+        touchUpData.mNode = node;
+        touchUpData.mX = x;
+        touchUpData.mY = y;
         mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
     }
 
@@ -5149,56 +5295,72 @@
     private void viewInvalidate() {
         invalidate();
     }
-    
+
     // return true if the key was handled
-    private boolean navHandledKey(int keyCode, int count, boolean noScroll
-            , long time) {
+    private boolean navHandledKey(int keyCode, int count, boolean noScroll,
+            long time, boolean ignorePlugin) {
         if (mNativeClass == 0) {
             return false;
         }
-        mLastFocusTime = time;
-        mLastFocusBounds = nativeGetFocusRingBounds();
-        boolean keyHandled = nativeMoveFocus(keyCode, count, noScroll) == false;
-        if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "navHandledKey mLastFocusBounds=" + mLastFocusBounds
-                    + " mLastFocusTime=" + mLastFocusTime
+        if (ignorePlugin == false && nativePluginEatsNavKey()) {
+            KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN
+                , keyCode, count, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
+                | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
+                | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
+                , 0, 0, 0);
+            mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
+            mWebViewCore.sendMessage(EventHub.KEY_UP, event);
+            return true;
+        }
+        mLastCursorTime = time;
+        mLastCursorBounds = nativeGetCursorRingBounds();
+        boolean keyHandled
+                = nativeMoveCursor(keyCode, count, noScroll) == false;
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
+                    + " mLastCursorTime=" + mLastCursorTime
                     + " handled=" + keyHandled);
         }
         if (keyHandled == false || mHeightCanMeasure == false) {
             return keyHandled;
         }
-        Rect contentFocus = nativeGetFocusRingBounds();
-        if (contentFocus.isEmpty()) return keyHandled;
-        Rect viewFocus = contentToView(contentFocus);
+        Rect contentCursorRingBounds = nativeGetCursorRingBounds();
+        if (contentCursorRingBounds.isEmpty()) return keyHandled;
+        Rect viewCursorRingBounds = contentToView(contentCursorRingBounds);
         Rect visRect = new Rect();
         calcOurVisibleRect(visRect);
         Rect outset = new Rect(visRect);
         int maxXScroll = visRect.width() / 2;
         int maxYScroll = visRect.height() / 2;
         outset.inset(-maxXScroll, -maxYScroll);
-        if (Rect.intersects(outset, viewFocus) == false) {
+        if (Rect.intersects(outset, viewCursorRingBounds) == false) {
             return keyHandled;
         }
         // FIXME: Necessary because ScrollView/ListView do not scroll left/right
-        int maxH = Math.min(viewFocus.right - visRect.right, maxXScroll);
+        int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
+                maxXScroll);
         if (maxH > 0) {
             pinScrollBy(maxH, 0, true, 0);
         } else {
-            maxH = Math.max(viewFocus.left - visRect.left, -maxXScroll);
+            maxH = Math.max(viewCursorRingBounds.left - visRect.left,
+                    -maxXScroll);
             if (maxH < 0) {
                 pinScrollBy(maxH, 0, true, 0);
             }
         }
-        if (mLastFocusBounds.isEmpty()) return keyHandled;
-        if (mLastFocusBounds.equals(contentFocus)) return keyHandled;
-        if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "navHandledKey contentFocus=" + contentFocus);
+        if (mLastCursorBounds.isEmpty()) return keyHandled;
+        if (mLastCursorBounds.equals(contentCursorRingBounds)) {
+            return keyHandled;
         }
-        requestRectangleOnScreen(viewFocus);
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
+                    + contentCursorRingBounds);
+        }
+        requestRectangleOnScreen(viewCursorRingBounds);
         mUserScroll = true;
         return keyHandled;
     }
-    
+
     /**
      * Set the background color. It's white by default. Pass
      * zero to make the view transparent.
@@ -5213,7 +5375,7 @@
         nativeDebugDump();
         mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
     }
-    
+
     /**
      *  Update our cache with updatedText.
      *  @param updatedText  The new text to put in our cache.
@@ -5223,52 +5385,72 @@
         // we recognize that it is up to date.
         nativeUpdateCachedTextfield(updatedText, mTextGeneration);
     }
-    
-    // Never call this version except by updateCachedTextfield(String) -
-    // we always want to pass in our generation number.
-    private native void     nativeUpdateCachedTextfield(String updatedText, 
-            int generation);
-    private native void     nativeClearFocus(int x, int y);
+
+    private native void     nativeClearCursor();
     private native void     nativeCreate(int ptr);
+    private native int      nativeCursorFramePointer();
+    private native Rect     nativeCursorNodeBounds();
+    /* package */ native int nativeCursorNodePointer();
+    /* package */ native boolean nativeCursorMatchesFocus();
+    private native boolean  nativeCursorIntersects(Rect visibleRect);
+    private native boolean  nativeCursorIsAnchor();
+    private native boolean  nativeCursorIsPlugin();
+    private native boolean  nativeCursorIsTextInput();
+    private native Point    nativeCursorPosition();
+    private native String   nativeCursorText();
+    /**
+     * Returns true if the native cursor node says it wants to handle key events
+     * (ala plugins). This can only be called if mNativeClass is non-zero!
+     */
+    private native boolean  nativeCursorWantsKeyEvents();
     private native void     nativeDebugDump();
     private native void     nativeDestroy();
-    private native void     nativeDrawFocusRing(Canvas content);
+    private native void     nativeDrawCursorRing(Canvas content);
+    private native void     nativeDrawMatches(Canvas canvas);
     private native void     nativeDrawSelection(Canvas content
             , int x, int y, boolean extendSelection);
     private native void     nativeDrawSelectionRegion(Canvas content);
-    private native boolean  nativeUpdateFocusNode();
-    private native Rect     nativeGetFocusRingBounds();
-    private native Rect     nativeGetNavBounds();
+    private native void     nativeDumpDisplayTree(String urlOrNull);
+    private native int      nativeFindAll(String findLower, String findUpper);
+    private native void     nativeFindNext(boolean forward);
+    private native boolean  nativeFocusCandidateIsPassword();
+    private native boolean  nativeFocusCandidateIsRtlText();
+    private native boolean  nativeFocusCandidateIsTextField();
+    private native boolean  nativeFocusCandidateIsTextInput();
+    private native int      nativeFocusCandidateMaxLength();
+    /* package */ native String   nativeFocusCandidateName();
+    private native Rect     nativeFocusCandidateNodeBounds();
+    /* package */ native int nativeFocusCandidatePointer();
+    private native String   nativeFocusCandidateText();
+    private native int      nativeFocusCandidateTextSize();
+    private native Rect     nativeGetCursorRingBounds();
+    private native Region   nativeGetSelection();
+    private native boolean  nativeHasCursorNode();
+    private native boolean  nativeHasFocusNode();
+    private native void     nativeHideCursor();
+    private native String   nativeImageURI(int x, int y);
     private native void     nativeInstrumentReport();
-    private native void     nativeMarkNodeInvalid(int node);
     // return true if the page has been scrolled
-    private native boolean  nativeMotionUp(int x, int y, int slop, boolean isClick);
+    private native boolean  nativeMotionUp(int x, int y, int slop);
     // returns false if it handled the key
-    private native boolean  nativeMoveFocus(int keyCode, int count, 
+    private native boolean  nativeMoveCursor(int keyCode, int count,
             boolean noScroll);
-    private native void     nativeNotifyFocusSet(boolean inEditingMode);
-    private native void     nativeRecomputeFocus();
+    private native int      nativeMoveGeneration();
+    private native void     nativeMoveSelection(int x, int y,
+            boolean extendSelection);
+    private native boolean  nativePluginEatsNavKey();
     // Like many other of our native methods, you must make sure that
     // mNativeClass is not null before calling this method.
     private native void     nativeRecordButtons(boolean focused,
             boolean pressed, boolean invalidate);
-    private native void     nativeResetFocus();
-    private native void     nativeResetNavClipBounds();
     private native void     nativeSelectBestAt(Rect rect);
     private native void     nativeSetFindIsDown();
     private native void     nativeSetFollowedLink(boolean followed);
     private native void     nativeSetHeightCanMeasure(boolean measure);
-    private native void     nativeSetNavBounds(Rect rect);
-    private native void     nativeSetNavClipBounds(Rect rect);
-    private native String   nativeImageURI(int x, int y);
-    /**
-     * Returns true if the native focus nodes says it wants to handle key events
-     * (ala plugins). This can only be called if mNativeClass is non-zero!
-     */
-    private native boolean  nativeFocusNodeWantsKeyEvents();
-    private native void     nativeMoveSelection(int x, int y
-            , boolean extendSelection);
-    private native Region   nativeGetSelection();
-
-    private native void nativeDumpDisplayTree(String urlOrNull);
+    private native int      nativeTextGeneration();
+    // Never call this version except by updateCachedTextfield(String) -
+    // we always want to pass in our generation number.
+    private native void     nativeUpdateCachedTextfield(String updatedText,
+            int generation);
+    private native void     nativeUpdatePluginReceivesEvents();
 }
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index a5fa41e..d8d9808 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -34,15 +34,12 @@
 import android.view.KeyEvent;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 
 import junit.framework.Assert;
 
 final class WebViewCore {
 
     private static final String LOGTAG = "webcore";
-    static final boolean DEBUG = false;
-    static final boolean LOGV_ENABLED = DEBUG;
 
     static {
         // Load libwebcore during static initialization. This happens in the
@@ -96,7 +93,7 @@
     private int mViewportMaximumScale = 0;
 
     private boolean mViewportUserScalable = true;
-    
+
     private int mRestoredScale = WebView.DEFAULT_SCALE_PERCENT;
     private int mRestoredX = 0;
     private int mRestoredY = 0;
@@ -143,6 +140,8 @@
         // The WebIconDatabase needs to be initialized within the UI thread so
         // just request the instance here.
         WebIconDatabase.getInstance();
+        // Create the WebStorage singleton
+        WebStorage.getInstance();
         // Send a message to initialize the WebViewCore.
         Message init = sWebCoreHandler.obtainMessage(
                 WebCoreThread.INITIALIZE, this);
@@ -162,6 +161,8 @@
         mSettings.syncSettingsAndCreateHandler(mBrowserFrame);
         // Create the handler and transfer messages for the IconDatabase
         WebIconDatabase.getInstance().createHandler();
+        // Create the handler for WebStorage
+        WebStorage.getInstance().createHandler();
         // The transferMessages call will transfer all pending messages to the
         // WebCore thread handler.
         mEventHub.transferMessages();
@@ -225,6 +226,16 @@
     }
 
     /**
+     * Add an error message to the client's console.
+     * @param message The message to add
+     * @param lineNumber the line on which the error occurred
+     * @param sourceID the filename of the source that caused the error.
+     */
+    protected void addMessageToConsole(String message, int lineNumber, String sourceID) {
+        mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID);
+    }
+
+    /**
      * Invoke a javascript alert.
      * @param message The message displayed in the alert.
      */
@@ -233,6 +244,27 @@
     }
 
     /**
+     * Notify the user that the origin has exceeded it's database quota.
+     * @param url The URL that caused the overflow.
+     * @param databaseIdentifier The identifier of the database.
+     * @param currentQuota The current quota for the origin.
+     */
+    protected void exceededDatabaseQuota(String url,
+                                         String databaseIdentifier,
+                                         long currentQuota) {
+        // Inform the callback proxy of the quota overflow. Send an object
+        // that encapsulates a call to the nativeSetDatabaseQuota method to
+        // awaken the sleeping webcore thread when a decision from the
+        // client to allow or deny quota is available.
+        mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier,
+                currentQuota, new WebStorage.QuotaUpdater() {
+                                  public void updateQuota(long quota) {
+                                      nativeSetDatabaseQuota(quota);
+                                  }
+                              });
+    }
+
+    /**
      * Invoke a javascript confirm dialog.
      * @param message The message displayed in the dialog.
      * @return True if the user confirmed or false if the user cancelled.
@@ -277,31 +309,36 @@
     // JNI methods
     //-------------------------------------------------------------------------
 
-    static native String nativeFindAddress(String addr);
+    static native String nativeFindAddress(String addr, boolean caseInsensitive);
 
     /**
      * Empty the picture set.
      */
     private native void nativeClearContent();
-    
+
     /**
      * Create a flat picture from the set of pictures.
      */
     private native void nativeCopyContentToPicture(Picture picture);
-   
+
     /**
      * Draw the picture set with a background color. Returns true
-     * if some individual picture took too long to draw and can be 
+     * if some individual picture took too long to draw and can be
      * split into parts. Called from the UI thread.
      */
     private native boolean nativeDrawContent(Canvas canvas, int color);
-    
+
+    /**
+     * check to see if picture is blank and in progress
+     */
+    private native boolean nativePictureReady();
+
     /**
      * Redraw a portion of the picture set. The Point wh returns the
      * width and height of the overall picture.
      */
     private native boolean nativeRecordContent(Region invalRegion, Point wh);
-    
+
     /**
      * Splits slow parts of the picture set. Called from the webkit
      * thread after nativeDrawContent returns true.
@@ -309,9 +346,10 @@
     private native void nativeSplitContent();
 
     private native boolean nativeKey(int keyCode, int unichar,
-            int repeatCount, boolean isShift, boolean isAlt, boolean isDown);
+            int repeatCount, boolean isShift, boolean isAlt, boolean isSym,
+            boolean isDown);
 
-    private native boolean nativeClick();
+    private native void nativeClick(int framePtr, int nodePtr);
 
     private native void nativeSendListBoxChoices(boolean[] choices, int size);
 
@@ -329,60 +367,56 @@
             float scale, int realScreenWidth, int screenHeight);
 
     private native int nativeGetContentMinPrefWidth();
-    
-    // Start: functions that deal with text editing
-    private native void nativeReplaceTextfieldText(int frame, int node, int x, 
-            int y, int oldStart, int oldEnd, String replace, int newStart, 
-            int newEnd);
 
-    private native void passToJs(int frame, int node, int x, int y, int gen,
+    // Start: functions that deal with text editing
+    private native void nativeReplaceTextfieldText(
+            int oldStart, int oldEnd, String replace, int newStart, int newEnd);
+
+    private native void passToJs(int gen,
             String currentText, int keyCode, int keyValue, boolean down,
             boolean cap, boolean fn, boolean sym);
 
+    private native void nativeSetFocusControllerActive(boolean active);
+
     private native void nativeSaveDocumentState(int frame);
 
-    private native void nativeSetFinalFocus(int framePtr, int nodePtr, int x,
-            int y, boolean block);
+    private native void nativeMoveMouse(int framePtr, int x, int y);
 
-    private native void nativeSetKitFocus(int moveGeneration,
-            int buildGeneration, int framePtr, int nodePtr, int x, int y,
-            boolean ignoreNullFocus);
+    private native void nativeMoveMouseIfLatest(int moveGeneration,
+            int framePtr, int x, int y);
 
     private native String nativeRetrieveHref(int framePtr, int nodePtr);
-    
-    private native void nativeTouchUp(int touchGeneration, 
-            int buildGeneration, int framePtr, int nodePtr, int x, int y, 
-            int size, boolean isClick, boolean retry);
+
+    private native void nativeTouchUp(int touchGeneration,
+            int framePtr, int nodePtr, int x, int y,
+            int size);
 
     private native boolean nativeHandleTouchEvent(int action, int x, int y);
 
-    private native void nativeUnblockFocus();
-    
     private native void nativeUpdateFrameCache();
-    
+
     private native void nativeSetSnapAnchor(int x, int y);
-    
+
     private native void nativeSnapToAnchor();
-    
+
     private native void nativeSetBackgroundColor(int color);
-    
+
     private native void nativeDumpDomTree(boolean useFile);
 
     private native void nativeDumpRenderTree(boolean useFile);
 
     private native void nativeDumpNavTree();
 
-    private native void nativeRefreshPlugins(boolean reloadOpenPages);
-    
+    private native void nativeSetJsFlags(String flags);
+
     /**
      *  Delete text from start to end in the focused textfield. If there is no
-     *  focus, or if start == end, silently fail.  If start and end are out of 
+     *  focus, or if start == end, silently fail.  If start and end are out of
      *  order, swap them.
      *  @param  start   Beginning of selection to delete.
      *  @param  end     End of selection to delete.
      */
-    private native void nativeDeleteSelection(int frame, int node, int x, int y,
-        int start, int end);
+    private native void nativeDeleteSelection(int start, int end);
 
     /**
      *  Set the selection to (start, end) in the focused textfield. If start and
@@ -390,15 +424,22 @@
      *  @param  start   Beginning of selection.
      *  @param  end     End of selection.
      */
-    private native void nativeSetSelection(int frame, int node, int x, int y,
-        int start, int end);
+    private native void nativeSetSelection(int start, int end);
 
     private native String nativeGetSelection(Region sel);
-    
+
     // Register a scheme to be treated as local scheme so that it can access
     // local asset files for resources
     private native void nativeRegisterURLSchemeAsLocal(String scheme);
 
+    /*
+     * Inform webcore that the user has decided whether to allow or deny new
+     * quota for the current origin and that the main thread should wake up
+     * now.
+     * @param quota The new quota.
+     */
+    private native void nativeSetDatabaseQuota(long quota);
+
     // EventHub for processing messages
     private final EventHub mEventHub;
     // WebCore thread handler
@@ -447,7 +488,7 @@
                                     CacheManager.endCacheTransaction();
                                     CacheManager.startCacheTransaction();
                                     sendMessageDelayed(
-                                            obtainMessage(CACHE_TICKER), 
+                                            obtainMessage(CACHE_TICKER),
                                             CACHE_TICKER_INTERVAL);
                                 }
                                 break;
@@ -472,36 +513,55 @@
         }
     }
 
-    static class FocusData {
-        FocusData() {}
-        FocusData(FocusData d) {
-            mMoveGeneration = d.mMoveGeneration;
-            mBuildGeneration = d.mBuildGeneration;
-            mFrame = d.mFrame;
-            mNode = d.mNode;
-            mX = d.mX;
-            mY = d.mY;
-            mIgnoreNullFocus = d.mIgnoreNullFocus;
+    static class BaseUrlData {
+        String mBaseUrl;
+        String mData;
+        String mMimeType;
+        String mEncoding;
+        String mFailUrl;
+    }
+
+    static class CursorData {
+        CursorData() {}
+        CursorData(int frame, int node, int x, int y) {
+            mFrame = frame;
+            mX = x;
+            mY = y;
         }
         int mMoveGeneration;
-        int mBuildGeneration;
         int mFrame;
-        int mNode;
         int mX;
         int mY;
-        boolean mIgnoreNullFocus;
+    }
+
+    static class JSInterfaceData {
+        Object mObject;
+        String mInterfaceName;
+    }
+
+    static class JSKeyData {
+        String mCurrentText;
+        KeyEvent mEvent;
+    }
+
+    static class PostUrlData {
+        String mUrl;
+        byte[] mPostData;
+    }
+
+    static class ReplaceTextData {
+        String mReplace;
+        int mNewStart;
+        int mNewEnd;
     }
 
     static class TouchUpData {
         int mMoveGeneration;
-        int mBuildGeneration;
         int mFrame;
         int mNode;
         int mX;
         int mY;
         int mSize;
-        boolean mIsClick;
-        boolean mRetry;
     }
 
     static class TouchEventData {
@@ -536,25 +596,27 @@
             "DELETE_SELECTION", // = 122;
             "LISTBOX_CHOICES", // = 123;
             "SINGLE_LISTBOX_CHOICE", // = 124;
-            "125",
+            "MESSAGE_RELAY", // = 125;
             "SET_BACKGROUND_COLOR", // = 126;
-            "UNBLOCK_FOCUS", // = 127;
+            "127", // = 127;
             "SAVE_DOCUMENT_STATE", // = 128;
             "GET_SELECTION", // = 129;
             "WEBKIT_DRAW", // = 130;
             "SYNC_SCROLL", // = 131;
-            "REFRESH_PLUGINS", // = 132;
-            // this will replace REFRESH_PLUGINS in the next release
-            "POST_URL", // = 142;
+            "POST_URL", // = 132;
             "SPLIT_PICTURE_SET", // = 133;
             "CLEAR_CONTENT", // = 134;
-            "SET_FINAL_FOCUS", // = 135;
-            "SET_KIT_FOCUS", // = 136;
-            "REQUEST_FOCUS_HREF", // = 137;
+            "SET_MOVE_MOUSE", // = 135;
+            "SET_MOVE_MOUSE_IF_LATEST", // = 136;
+            "REQUEST_CURSOR_HREF", // = 137;
             "ADD_JS_INTERFACE", // = 138;
             "LOAD_DATA", // = 139;
             "TOUCH_UP", // = 140;
             "TOUCH_EVENT", // = 141;
+            "SET_ACTIVE", // = 142;
+            "ON_PAUSE",     // = 143
+            "ON_RESUME",    // = 144
+            "FREE_MEMORY",  // = 145
         };
 
     class EventHub {
@@ -584,22 +646,20 @@
         static final int DELETE_SELECTION = 122;
         static final int LISTBOX_CHOICES = 123;
         static final int SINGLE_LISTBOX_CHOICE = 124;
+        static final int MESSAGE_RELAY = 125;
         static final int SET_BACKGROUND_COLOR = 126;
-        static final int UNBLOCK_FOCUS = 127;
         static final int SAVE_DOCUMENT_STATE = 128;
         static final int GET_SELECTION = 129;
         static final int WEBKIT_DRAW = 130;
         static final int SYNC_SCROLL = 131;
-        static final int REFRESH_PLUGINS = 132;
-        // this will replace REFRESH_PLUGINS in the next release
-        static final int POST_URL = 142;
+        static final int POST_URL = 132;
         static final int SPLIT_PICTURE_SET = 133;
         static final int CLEAR_CONTENT = 134;
-        
+
         // UI nav messages
-        static final int SET_FINAL_FOCUS = 135;
-        static final int SET_KIT_FOCUS = 136;
-        static final int REQUEST_FOCUS_HREF = 137;
+        static final int SET_MOVE_MOUSE = 135;
+        static final int SET_MOVE_MOUSE_IF_LATEST = 136;
+        static final int REQUEST_CURSOR_HREF = 137;
         static final int ADD_JS_INTERFACE = 138;
         static final int LOAD_DATA = 139;
 
@@ -608,6 +668,17 @@
         // message used to pass UI touch events to WebCore
         static final int TOUCH_EVENT = 141;
 
+        // Used to tell the focus controller not to draw the blinking cursor,
+        // based on whether the WebView has focus and whether the WebView's
+        // cursor matches the webpage's focus.
+        static final int SET_ACTIVE = 142;
+
+        // lifecycle activities for just this DOM (unlike pauseTimers, which
+        // is global)
+        static final int ON_PAUSE = 143;
+        static final int ON_RESUME = 144;
+        static final int FREE_MEMORY = 145;
+
         // Network-based messaging
         static final int CLEAR_SSL_PREF_TABLE = 150;
 
@@ -620,12 +691,10 @@
         static final int DUMP_RENDERTREE = 171;
         static final int DUMP_NAVTREE = 172;
 
+        static final int SET_JS_FLAGS = 173;
+
         // private message ids
         private static final int DESTROY =     200;
-        
-        // flag values passed to message SET_FINAL_FOCUS
-        static final int NO_FOCUS_CHANGE_BLOCK = 0;
-        static final int BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP = 1;
 
         // Private handler for WebCore messages.
         private Handler mHandler;
@@ -654,10 +723,12 @@
             mHandler = new Handler() {
                 @Override
                 public void handleMessage(Message msg) {
-                    if (LOGV_ENABLED) {
-                        Log.v(LOGTAG, msg.what < LOAD_URL || msg.what 
-                                > TOUCH_EVENT ? Integer.toString(msg.what)
-                                : HandlerDebugString[msg.what - LOAD_URL]);
+                    if (DebugFlags.WEB_VIEW_CORE) {
+                        Log.v(LOGTAG, (msg.what < LOAD_URL || msg.what
+                                > FREE_MEMORY ? Integer.toString(msg.what)
+                                : HandlerDebugString[msg.what - LOAD_URL])
+                                + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
+                                + " obj=" + msg.obj);
                     }
                     switch (msg.what) {
                         case WEBKIT_DRAW:
@@ -677,15 +748,13 @@
                             break;
 
                         case POST_URL: {
-                            HashMap param = (HashMap) msg.obj;
-                            String url = (String) param.get("url");
-                            byte[] data = (byte[]) param.get("data");
-                            mBrowserFrame.postUrl(url, data);
+                            PostUrlData param = (PostUrlData) msg.obj;
+                            mBrowserFrame.postUrl(param.mUrl, param.mPostData);
                             break;
                         }
                         case LOAD_DATA:
-                            HashMap loadParams = (HashMap) msg.obj;
-                            String baseUrl = (String) loadParams.get("baseUrl");
+                            BaseUrlData loadParams = (BaseUrlData) msg.obj;
+                            String baseUrl = loadParams.mBaseUrl;
                             if (baseUrl != null) {
                                 int i = baseUrl.indexOf(':');
                                 if (i > 0) {
@@ -698,7 +767,7 @@
                                      * we automatically add the scheme of the
                                      * baseUrl for local access as long as it is
                                      * not http(s)/ftp(s)/about/javascript
-                                     */ 
+                                     */
                                     String scheme = baseUrl.substring(0, i);
                                     if (!scheme.startsWith("http") &&
                                             !scheme.startsWith("ftp") &&
@@ -709,16 +778,16 @@
                                 }
                             }
                             mBrowserFrame.loadData(baseUrl,
-                                    (String) loadParams.get("data"),
-                                    (String) loadParams.get("mimeType"),
-                                    (String) loadParams.get("encoding"),
-                                    (String) loadParams.get("failUrl"));
+                                    loadParams.mData,
+                                    loadParams.mMimeType,
+                                    loadParams.mEncoding,
+                                    loadParams.mFailUrl);
                             break;
 
                         case STOP_LOADING:
-                            // If the WebCore has committed the load, but not 
-                            // finished the first layout yet, we need to set 
-                            // first layout done to trigger the interpreted side sync 
+                            // If the WebCore has committed the load, but not
+                            // finished the first layout yet, we need to set
+                            // first layout done to trigger the interpreted side sync
                             // up with native side
                             if (mBrowserFrame.committed()
                                     && !mBrowserFrame.firstLayoutDone()) {
@@ -741,7 +810,7 @@
                             break;
 
                         case CLICK:
-                            nativeClick();
+                            nativeClick(msg.arg1, msg.arg2);
                             break;
 
                         case VIEW_SIZE_CHANGED:
@@ -752,9 +821,10 @@
                         case SET_SCROLL_OFFSET:
                             // note: these are in document coordinates
                             // (inv-zoom)
-                            nativeSetScrollOffset(msg.arg1, msg.arg2);
+                            Point pt = (Point) msg.obj;
+                            nativeSetScrollOffset(msg.arg1, pt.x, pt.y);
                             break;
-                            
+
                         case SET_GLOBAL_BOUNDS:
                             Rect r = (Rect) msg.obj;
                             nativeSetGlobalBounds(r.left, r.top, r.width(),
@@ -765,7 +835,7 @@
                             // If it is a standard load and the load is not
                             // committed yet, we interpret BACK as RELOAD
                             if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
-                                    (mBrowserFrame.loadType() == 
+                                    (mBrowserFrame.loadType() ==
                                     BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
                                 mBrowserFrame.reload(true);
                             } else {
@@ -802,6 +872,19 @@
                             }
                             break;
 
+                        case ON_PAUSE:
+                            nativePause();
+                            break;
+
+                        case ON_RESUME:
+                            nativeResume();
+                            break;
+
+                        case FREE_MEMORY:
+                            clearCache(false);
+                            nativeFreeMemory();
+                            break;
+
                         case SET_NETWORK_STATE:
                             if (BrowserFrame.sJavaBridge == null) {
                                 throw new IllegalStateException("No WebView " +
@@ -812,10 +895,7 @@
                             break;
 
                         case CLEAR_CACHE:
-                            mBrowserFrame.clearCache();
-                            if (msg.arg1 == 1) {
-                                CacheManager.removeAllCacheFiles();
-                            }
+                            clearCache(msg.arg1 == 1);
                             break;
 
                         case CLEAR_HISTORY:
@@ -823,29 +903,20 @@
                                     close(mBrowserFrame.mNativeFrame);
                             break;
 
-                        case REPLACE_TEXT: 
-                            HashMap jMap = (HashMap) msg.obj;
-                            FocusData fData = (FocusData) jMap.get("focusData");
-                            String replace = (String) jMap.get("replace");
-                            int newStart = 
-                                    ((Integer) jMap.get("start")).intValue();
-                            int newEnd = 
-                                    ((Integer) jMap.get("end")).intValue();
-                            nativeReplaceTextfieldText(fData.mFrame,
-                                    fData.mNode, fData.mX, fData.mY, msg.arg1,
-                                    msg.arg2, replace, newStart, newEnd);
+                        case REPLACE_TEXT:
+                            ReplaceTextData rep = (ReplaceTextData) msg.obj;
+                            nativeReplaceTextfieldText(msg.arg1, msg.arg2,
+                                    rep.mReplace, rep.mNewStart, rep.mNewEnd);
                             break;
 
                         case PASS_TO_JS: {
-                            HashMap jsMap = (HashMap) msg.obj;
-                            FocusData fDat = (FocusData) jsMap.get("focusData");
-                            KeyEvent evt = (KeyEvent) jsMap.get("event");
+                            JSKeyData jsData = (JSKeyData) msg.obj;
+                            KeyEvent evt = jsData.mEvent;
                             int keyCode = evt.getKeyCode();
                             int keyValue = evt.getUnicodeChar();
                             int generation = msg.arg1;
-                            passToJs(fDat.mFrame, fDat.mNode, fDat.mX, fDat.mY,
-                                    generation,
-                                    (String) jsMap.get("currentText"),
+                            passToJs(generation,
+                                    jsData.mCurrentText,
                                     keyCode,
                                     keyValue,
                                     evt.isDown(),
@@ -855,8 +926,8 @@
                         }
 
                         case SAVE_DOCUMENT_STATE: {
-                            FocusData fDat = (FocusData) msg.obj;
-                            nativeSaveDocumentState(fDat.mFrame);
+                            CursorData cDat = (CursorData) msg.obj;
+                            nativeSaveDocumentState(cDat.mFrame);
                             break;
                         }
 
@@ -868,11 +939,9 @@
                         case TOUCH_UP:
                             TouchUpData touchUpData = (TouchUpData) msg.obj;
                             nativeTouchUp(touchUpData.mMoveGeneration,
-                                    touchUpData.mBuildGeneration,
                                     touchUpData.mFrame, touchUpData.mNode,
-                                    touchUpData.mX, touchUpData.mY, 
-                                    touchUpData.mSize, touchUpData.mIsClick,
-                                    touchUpData.mRetry);
+                                    touchUpData.mX, touchUpData.mY,
+                                    touchUpData.mSize);
                             break;
 
                         case TOUCH_EVENT: {
@@ -885,13 +954,14 @@
                             break;
                         }
 
+                        case SET_ACTIVE:
+                            nativeSetFocusControllerActive(msg.arg1 == 1);
+                            break;
+
                         case ADD_JS_INTERFACE:
-                            HashMap map = (HashMap) msg.obj;
-                            Object obj = map.get("object");
-                            String interfaceName = (String)
-                                    map.get("interfaceName");
-                            mBrowserFrame.addJavascriptInterface(obj,
-                                    interfaceName);
+                            JSInterfaceData jsData = (JSInterfaceData) msg.obj;
+                            mBrowserFrame.addJavascriptInterface(jsData.mObject,
+                                    jsData.mInterfaceName);
                             break;
 
                         case REQUEST_EXT_REPRESENTATION:
@@ -903,35 +973,27 @@
                             mBrowserFrame.documentAsText((Message) msg.obj);
                             break;
 
-                        case SET_FINAL_FOCUS:
-                            FocusData finalData = (FocusData) msg.obj;
-                            nativeSetFinalFocus(finalData.mFrame,
-                                     finalData.mNode, finalData.mX, 
-                                     finalData.mY, msg.arg1 
-                                     != EventHub.NO_FOCUS_CHANGE_BLOCK);
+                        case SET_MOVE_MOUSE:
+                            CursorData cursorData = (CursorData) msg.obj;
+                            nativeMoveMouse(cursorData.mFrame,
+                                     cursorData.mX, cursorData.mY);
                             break;
 
-                        case UNBLOCK_FOCUS:
-                            nativeUnblockFocus();
+                        case SET_MOVE_MOUSE_IF_LATEST:
+                            CursorData cData = (CursorData) msg.obj;
+                            nativeMoveMouseIfLatest(cData.mMoveGeneration,
+                                    cData.mFrame,
+                                    cData.mX, cData.mY);
                             break;
 
-                        case SET_KIT_FOCUS:
-                            FocusData focusData = (FocusData) msg.obj;
-                            nativeSetKitFocus(focusData.mMoveGeneration,
-                                    focusData.mBuildGeneration,
-                                    focusData.mFrame, focusData.mNode,
-                                    focusData.mX, focusData.mY,
-                                    focusData.mIgnoreNullFocus);
-                            break;
-
-                        case REQUEST_FOCUS_HREF: {
+                        case REQUEST_CURSOR_HREF: {
                             Message hrefMsg = (Message) msg.obj;
                             String res = nativeRetrieveHref(msg.arg1, msg.arg2);
                             hrefMsg.getData().putString("url", res);
                             hrefMsg.sendToTarget();
                             break;
                         }
-                            
+
                         case UPDATE_CACHE_AND_TEXT_ENTRY:
                             nativeUpdateFrameCache();
                             // FIXME: this should provide a minimal rectangle
@@ -951,21 +1013,15 @@
                         case SET_SNAP_ANCHOR:
                             nativeSetSnapAnchor(msg.arg1, msg.arg2);
                             break;
-                            
+
                         case DELETE_SELECTION:
-                            FocusData delData = (FocusData) msg.obj;
-                            nativeDeleteSelection(delData.mFrame,
-                                     delData.mNode, delData.mX, 
-                                     delData.mY, msg.arg1, msg.arg2);
+                            nativeDeleteSelection(msg.arg1, msg.arg2);
                             break;
 
                         case SET_SELECTION:
-                            FocusData selData = (FocusData) msg.obj;
-                            nativeSetSelection(selData.mFrame,
-                                     selData.mNode, selData.mX, 
-                                     selData.mY, msg.arg1, msg.arg2);
+                            nativeSetSelection(msg.arg1, msg.arg2);
                             break;
-                            
+
                         case LISTBOX_CHOICES:
                             SparseBooleanArray choices = (SparseBooleanArray)
                                     msg.obj;
@@ -974,18 +1030,18 @@
                             for (int c = 0; c < choicesSize; c++) {
                                 choicesArray[c] = choices.get(c);
                             }
-                            nativeSendListBoxChoices(choicesArray, 
+                            nativeSendListBoxChoices(choicesArray,
                                     choicesSize);
                             break;
 
                         case SINGLE_LISTBOX_CHOICE:
                             nativeSendListBoxChoice(msg.arg1);
                             break;
-                            
+
                         case SET_BACKGROUND_COLOR:
                             nativeSetBackgroundColor(msg.arg1);
                             break;
-                            
+
                         case GET_SELECTION:
                             String str = nativeGetSelection((Region) msg.obj);
                             Message.obtain(mWebView.mPrivateHandler
@@ -1005,26 +1061,32 @@
                             nativeDumpNavTree();
                             break;
 
+                        case SET_JS_FLAGS:
+                            nativeSetJsFlags((String)msg.obj);
+                            break;
+
                         case SYNC_SCROLL:
                             mWebkitScrollX = msg.arg1;
                             mWebkitScrollY = msg.arg2;
                             break;
 
-                        case REFRESH_PLUGINS:
-                            nativeRefreshPlugins(msg.arg1 != 0);
-                            break;
-                            
                         case SPLIT_PICTURE_SET:
                             nativeSplitContent();
                             mSplitPictureIsScheduled = false;
                             break;
-                            
+
                         case CLEAR_CONTENT:
                             // Clear the view so that onDraw() will draw nothing
                             // but white background
                             // (See public method WebView.clearView)
                             nativeClearContent();
                             break;
+
+                        case MESSAGE_RELAY:
+                            if (msg.obj instanceof Message) {
+                                ((Message) msg.obj).sendToTarget();
+                            }
+                            break;
                     }
                 }
             };
@@ -1114,7 +1176,7 @@
     //-------------------------------------------------------------------------
 
     void stopLoading() {
-        if (LOGV_ENABLED) Log.v(LOGTAG, "CORE stopLoading");
+        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
         if (mBrowserFrame != null) {
             mBrowserFrame.stopLoading();
         }
@@ -1189,20 +1251,42 @@
     // WebViewCore private methods
     //-------------------------------------------------------------------------
 
+    private void clearCache(boolean includeDiskFiles) {
+        mBrowserFrame.clearCache();
+        if (includeDiskFiles) {
+            CacheManager.removeAllCacheFiles();
+        }
+    }
+
     private void loadUrl(String url) {
-        if (LOGV_ENABLED) Log.v(LOGTAG, " CORE loadUrl " + url);
+        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
         mBrowserFrame.loadUrl(url);
     }
 
     private void key(KeyEvent evt, boolean isDown) {
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW_CORE) {
             Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
                     + evt);
         }
-        if (!nativeKey(evt.getKeyCode(), evt.getUnicodeChar(),
+        int keyCode = evt.getKeyCode();
+        if (!nativeKey(keyCode, evt.getUnicodeChar(),
                 evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
-                isDown)) {
+                evt.isSymPressed(),
+                isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
+            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
+                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
+                if (DebugFlags.WEB_VIEW_CORE) {
+                    Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
+                }
+                if (mWebView != null && evt.isDown()) {
+                    Message.obtain(mWebView.mPrivateHandler,
+                            WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
+                }
+                return;
+            }
             // bubble up the event handling
+            // but do not bubble up the ENTER key, which would open the search
+            // bar without any text.
             mCallbackProxy.onUnhandledKeyEvent(evt);
         }
     }
@@ -1213,7 +1297,7 @@
 
     // notify webkit that our virtual view size changed size (after inv-zoom)
     private void viewSizeChanged(int w, int h, float scale) {
-        if (LOGV_ENABLED) Log.v(LOGTAG, "CORE onSizeChanged");
+        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE onSizeChanged");
         if (w == 0) {
             Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
             return;
@@ -1222,7 +1306,7 @@
                 && (w < mViewportWidth || mViewportWidth == -1)) {
             int width = mViewportWidth;
             if (mViewportWidth == -1) {
-                if (mSettings.getLayoutAlgorithm() == 
+                if (mSettings.getLayoutAlgorithm() ==
                         WebSettings.LayoutAlgorithm.NORMAL) {
                     width = WebView.ZOOM_OUT_WIDTH;
                 } else {
@@ -1253,7 +1337,7 @@
         if (needInvalidate) {
             // ensure {@link #webkitDraw} is called as we were blocking in
             // {@link #contentDraw} when mCurrentViewWidth is 0
-            if (LOGV_ENABLED) Log.v(LOGTAG, "viewSizeChanged");
+            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
             contentDraw();
         }
         mEventHub.sendMessage(Message.obtain(null,
@@ -1269,7 +1353,7 @@
 
     // Used to avoid posting more than one draw message.
     private boolean mDrawIsScheduled;
-    
+
     // Used to avoid posting more than one split picture message.
     private boolean mSplitPictureIsScheduled;
 
@@ -1278,7 +1362,7 @@
 
     // Used to end scale+scroll mode, accessed by both threads
     boolean mEndScaleZoom = false;
-    
+
     public class DrawData {
         public DrawData() {
             mInvalRegion = new Region();
@@ -1288,21 +1372,21 @@
         public Point mViewPoint;
         public Point mWidthHeight;
     }
-    
+
     private void webkitDraw() {
         mDrawIsScheduled = false;
         DrawData draw = new DrawData();
-        if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw start");
-        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight) 
+        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
+        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
                 == false) {
-            if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw abort");
+            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
             return;
         }
         if (mWebView != null) {
             // Send the native view size that was used during the most recent
             // layout.
             draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
-            if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
+            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
             Message.obtain(mWebView.mPrivateHandler,
                     WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
             if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
@@ -1350,6 +1434,10 @@
         }
     }
 
+    /* package */ boolean pictureReady() {
+        return nativePictureReady();
+    }
+
     /*package*/ Picture copyContentPicture() {
         Picture result = new Picture();
         nativeCopyContentToPicture(result);
@@ -1363,9 +1451,9 @@
         sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
                 .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
         // Note: there is one possible failure mode. If pauseUpdate() is called
-        // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out 
-        // of the queue and about to be executed. mDrawIsScheduled may be set to 
-        // false in webkitDraw(). So update won't be blocked. But at least the 
+        // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
+        // of the queue and about to be executed. mDrawIsScheduled may be set to
+        // false in webkitDraw(). So update won't be blocked. But at least the
         // webcore thread priority is still lowered.
         if (core != null) {
             synchronized (core) {
@@ -1385,7 +1473,7 @@
             synchronized (core) {
                 core.mDrawIsScheduled = false;
                 core.mDrawIsPaused = false;
-                if (LOGV_ENABLED) Log.v(LOGTAG, "resumeUpdate");
+                if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate");
                 core.contentDraw();
             }
         }
@@ -1434,7 +1522,7 @@
             mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
         }
     }
-    
+
     // called by JNI
     private void contentScrollBy(int dx, int dy, boolean animate) {
         if (!mBrowserFrame.firstLayoutDone()) {
@@ -1442,9 +1530,14 @@
             return;
         }
         if (mWebView != null) {
-            Message.obtain(mWebView.mPrivateHandler,
-                    WebView.SCROLL_BY_MSG_ID, dx, dy, 
-                    new Boolean(animate)).sendToTarget();
+            Message msg = Message.obtain(mWebView.mPrivateHandler,
+                    WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate));
+            if (mDrawIsScheduled) {
+                mEventHub.sendMessage(Message.obtain(null,
+                        EventHub.MESSAGE_RELAY, msg));
+            } else {
+                msg.sendToTarget();
+            }
         }
     }
 
@@ -1461,8 +1554,14 @@
             return;
         }
         if (mWebView != null) {
-            Message.obtain(mWebView.mPrivateHandler,
-                    WebView.SCROLL_TO_MSG_ID, x, y).sendToTarget();
+            Message msg = Message.obtain(mWebView.mPrivateHandler,
+                    WebView.SCROLL_TO_MSG_ID, x, y);
+            if (mDrawIsScheduled) {
+                mEventHub.sendMessage(Message.obtain(null,
+                        EventHub.MESSAGE_RELAY, msg));
+            } else {
+                msg.sendToTarget();
+            }
         }
     }
 
@@ -1479,24 +1578,14 @@
             return;
         }
         if (mWebView != null) {
-            Message.obtain(mWebView.mPrivateHandler,
-                    WebView.SPAWN_SCROLL_TO_MSG_ID, x, y).sendToTarget();
-        }
-    }
-
-    // called by JNI
-    private void sendMarkNodeInvalid(int node) {
-        if (mWebView != null) {
-            Message.obtain(mWebView.mPrivateHandler,
-                    WebView.MARK_NODE_INVALID_ID, node, 0).sendToTarget();
-        }
-    }
-
-    // called by JNI
-    private void sendNotifyFocusSet() {
-        if (mWebView != null) {
-            Message.obtain(mWebView.mPrivateHandler,
-                    WebView.NOTIFY_FOCUS_SET_MSG_ID).sendToTarget();
+            Message msg = Message.obtain(mWebView.mPrivateHandler,
+                    WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
+            if (mDrawIsScheduled) {
+                mEventHub.sendMessage(Message.obtain(null,
+                        EventHub.MESSAGE_RELAY, msg));
+            } else {
+                msg.sendToTarget();
+            }
         }
     }
 
@@ -1511,14 +1600,6 @@
         contentDraw();
     }
 
-    // called by JNI
-    private void sendRecomputeFocus() {
-        if (mWebView != null) {
-            Message.obtain(mWebView.mPrivateHandler,
-                    WebView.RECOMPUTE_FOCUS_MSG_ID).sendToTarget();
-        }
-    }
-
     /*  Called by JNI. The coordinates are in doc coordinates, so they need to
         be scaled before they can be used by the view system, which happens
         in WebView since it (and its thread) know the current scale factor.
@@ -1536,12 +1617,12 @@
     }
 
     private native void setViewportSettingsFromNative();
-    
+
     // called by JNI
-    private void didFirstLayout() {
+    private void didFirstLayout(boolean standardLoad) {
         // Trick to ensure that the Picture has the exact height for the content
         // by forcing to layout with 0 height after the page is ready, which is
-        // indicated by didFirstLayout. This is essential to get rid of the 
+        // indicated by didFirstLayout. This is essential to get rid of the
         // white space in the GMail which uses WebView for message view.
         if (mWebView != null && mWebView.mHeightCanMeasure) {
             mWebView.mLastHeightSent = 0;
@@ -1609,9 +1690,9 @@
 
         // now notify webview
         if (mWebView != null) {
-            HashMap scaleLimit = new HashMap();
-            scaleLimit.put("minScale", mViewportMinimumScale);
-            scaleLimit.put("maxScale", mViewportMaximumScale);
+            WebView.ScaleLimitData scaleLimit = new WebView.ScaleLimitData();
+            scaleLimit.mMinScale = mViewportMinimumScale;
+            scaleLimit.mMaxScale = mViewportMaximumScale;
 
             if (mRestoredScale > 0) {
                 Message.obtain(mWebView.mPrivateHandler,
@@ -1619,16 +1700,14 @@
                         scaleLimit).sendToTarget();
                 mRestoredScale = 0;
             } else {
+                // if standardLoad is true, use mViewportInitialScale, otherwise
+                // pass -1 to the WebView to indicate no change of the scale.
                 Message.obtain(mWebView.mPrivateHandler,
-                        WebView.DID_FIRST_LAYOUT_MSG_ID, mViewportInitialScale,
+                        WebView.DID_FIRST_LAYOUT_MSG_ID,
+                        standardLoad ? mViewportInitialScale : -1,
                         mViewportWidth, scaleLimit).sendToTarget();
             }
 
-            // if no restored offset, move the new page to (0, 0)
-            Message.obtain(mWebView.mPrivateHandler, WebView.SCROLL_TO_MSG_ID,
-                    mRestoredX, mRestoredY).sendToTarget();
-            mRestoredX = mRestoredY = 0;
-
             // force an early draw for quick feedback after the first layout
             if (mCurrentViewWidth != 0) {
                 synchronized (this) {
@@ -1636,10 +1715,19 @@
                         mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
                     }
                     mDrawIsScheduled = true;
+                    // if no restored offset, move the new page to (0, 0)
+                    mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
+                            EventHub.MESSAGE_RELAY, Message.obtain(
+                                    mWebView.mPrivateHandler,
+                                    WebView.SCROLL_TO_MSG_ID, mRestoredX,
+                                    mRestoredY)));
                     mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
                             EventHub.WEBKIT_DRAW));
                 }
             }
+
+            // reset restored offset
+            mRestoredX = mRestoredY = 0;
         }
     }
 
@@ -1664,15 +1752,22 @@
             String text, int textGeneration) {
         if (mWebView != null) {
             Message msg = Message.obtain(mWebView.mPrivateHandler,
-                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, 
+                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
                     textGeneration, text);
             msg.getData().putBoolean("password", changeToPassword);
             msg.sendToTarget();
         }
     }
 
+    // called by JNI
+    private void clearTextEntry() {
+        if (mWebView == null) return;
+        Message.obtain(mWebView.mPrivateHandler,
+                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
+    }
+
     // these must be in document space (i.e. not scaled/zoomed).
-    private native void nativeSetScrollOffset(int dx, int dy);
+    private native void nativeSetScrollOffset(int gen, int dx, int dy);
 
     private native void nativeSetGlobalBounds(int x, int y, int w, int h);
 
@@ -1690,6 +1785,19 @@
         if (mWebView != null) {
             mWebView.requestListBox(array, enabledArray, selection);
         }
-        
+
     }
+
+    // called by JNI
+    private void requestKeyboard(boolean showKeyboard) {
+        if (mWebView != null) {
+            Message.obtain(mWebView.mPrivateHandler,
+                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
+                    .sendToTarget();
+        }
+    }
+
+    private native void nativePause();
+    private native void nativeResume();
+    private native void nativeFreeMemory();
 }
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index 1004e30..4e76254 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -48,7 +48,9 @@
     // 6 -> 7 Change cache localPath from int to String
     // 7 -> 8 Move cache to its own db
     // 8 -> 9 Store both scheme and host when storing passwords
-    private static final int CACHE_DATABASE_VERSION = 1;
+    private static final int CACHE_DATABASE_VERSION = 3;
+    // 1 -> 2 Add expires String
+    // 2 -> 3 Add content-disposition
 
     private static WebViewDatabase mInstance = null;
 
@@ -107,6 +109,8 @@
 
     private static final String CACHE_EXPIRES_COL = "expires";
 
+    private static final String CACHE_EXPIRES_STRING_COL = "expiresstring";
+
     private static final String CACHE_MIMETYPE_COL = "mimetype";
 
     private static final String CACHE_ENCODING_COL = "encoding";
@@ -117,6 +121,8 @@
 
     private static final String CACHE_CONTENTLENGTH_COL = "contentlength";
 
+    private static final String CACHE_CONTENTDISPOSITION_COL = "contentdisposition";
+
     // column id strings for "password" table
     private static final String PASSWORD_HOST_COL = "host";
 
@@ -150,11 +156,13 @@
     private static int mCacheLastModifyColIndex;
     private static int mCacheETagColIndex;
     private static int mCacheExpiresColIndex;
+    private static int mCacheExpiresStringColIndex;
     private static int mCacheMimeTypeColIndex;
     private static int mCacheEncodingColIndex;
     private static int mCacheHttpStatusColIndex;
     private static int mCacheLocationColIndex;
     private static int mCacheContentLengthColIndex;
+    private static int mCacheContentDispositionColIndex;
 
     private static int mCacheTransactionRefcount;
 
@@ -220,6 +228,8 @@
                         .getColumnIndex(CACHE_ETAG_COL);
                 mCacheExpiresColIndex = mCacheInserter
                         .getColumnIndex(CACHE_EXPIRES_COL);
+                mCacheExpiresStringColIndex = mCacheInserter
+                        .getColumnIndex(CACHE_EXPIRES_STRING_COL);
                 mCacheMimeTypeColIndex = mCacheInserter
                         .getColumnIndex(CACHE_MIMETYPE_COL);
                 mCacheEncodingColIndex = mCacheInserter
@@ -230,6 +240,8 @@
                         .getColumnIndex(CACHE_LOCATION_COL);
                 mCacheContentLengthColIndex = mCacheInserter
                         .getColumnIndex(CACHE_CONTENTLENGTH_COL);
+                mCacheContentDispositionColIndex = mCacheInserter
+                        .getColumnIndex(CACHE_CONTENTDISPOSITION_COL);
             }
         }
 
@@ -320,11 +332,12 @@
                     + " TEXT, " + CACHE_FILE_PATH_COL + " TEXT, "
                     + CACHE_LAST_MODIFY_COL + " TEXT, " + CACHE_ETAG_COL
                     + " TEXT, " + CACHE_EXPIRES_COL + " INTEGER, "
+                    + CACHE_EXPIRES_STRING_COL + " TEXT, "
                     + CACHE_MIMETYPE_COL + " TEXT, " + CACHE_ENCODING_COL
                     + " TEXT," + CACHE_HTTP_STATUS_COL + " INTEGER, "
                     + CACHE_LOCATION_COL + " TEXT, " + CACHE_CONTENTLENGTH_COL
-                    + " INTEGER, " + " UNIQUE (" + CACHE_URL_COL
-                    + ") ON CONFLICT REPLACE);");
+                    + " INTEGER, " + CACHE_CONTENTDISPOSITION_COL + " TEXT, "
+                    + " UNIQUE (" + CACHE_URL_COL + ") ON CONFLICT REPLACE);");
             mCacheDatabase.execSQL("CREATE INDEX cacheUrlIndex ON cache ("
                     + CACHE_URL_COL + ")");
         }
@@ -537,8 +550,8 @@
         }
 
         Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath, lastmodify, etag, expires, "
-                    + "mimetype, encoding, httpstatus, location, contentlength "
-                    + "FROM cache WHERE url = ?",
+                    + "expiresstring, mimetype, encoding, httpstatus, location, contentlength, "
+                    + "contentdisposition FROM cache WHERE url = ?",
                 new String[] { url });
 
         try {
@@ -548,11 +561,13 @@
                 ret.lastModified = cursor.getString(1);
                 ret.etag = cursor.getString(2);
                 ret.expires = cursor.getLong(3);
-                ret.mimeType = cursor.getString(4);
-                ret.encoding = cursor.getString(5);
-                ret.httpStatusCode = cursor.getInt(6);
-                ret.location = cursor.getString(7);
-                ret.contentLength = cursor.getLong(8);
+                ret.expiresString = cursor.getString(4);
+                ret.mimeType = cursor.getString(5);
+                ret.encoding = cursor.getString(6);
+                ret.httpStatusCode = cursor.getInt(7);
+                ret.location = cursor.getString(8);
+                ret.contentLength = cursor.getLong(9);
+                ret.contentdisposition = cursor.getString(10);
                 return ret;
             }
         } finally {
@@ -591,11 +606,14 @@
         mCacheInserter.bind(mCacheLastModifyColIndex, c.lastModified);
         mCacheInserter.bind(mCacheETagColIndex, c.etag);
         mCacheInserter.bind(mCacheExpiresColIndex, c.expires);
+        mCacheInserter.bind(mCacheExpiresStringColIndex, c.expiresString);
         mCacheInserter.bind(mCacheMimeTypeColIndex, c.mimeType);
         mCacheInserter.bind(mCacheEncodingColIndex, c.encoding);
         mCacheInserter.bind(mCacheHttpStatusColIndex, c.httpStatusCode);
         mCacheInserter.bind(mCacheLocationColIndex, c.location);
         mCacheInserter.bind(mCacheContentLengthColIndex, c.contentLength);
+        mCacheInserter.bind(mCacheContentDispositionColIndex,
+                c.contentdisposition);
         mCacheInserter.execute();
     }
 
diff --git a/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java b/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java
index 74d27ed..b3d7f69 100644
--- a/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java
+++ b/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java
@@ -38,7 +38,6 @@
 import java.io.OutputStream;
 import java.io.IOException;
 import java.lang.StringBuilder;
-import java.util.Date;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -57,7 +56,6 @@
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
 import org.apache.http.conn.ssl.StrictHostnameVerifier;
-import org.apache.http.impl.cookie.DateUtils;
 import org.apache.http.util.CharArrayBuffer;
 
 import java.util.concurrent.locks.Condition;
@@ -863,12 +861,9 @@
         mResponseHeaders = new HashMap<String, String[]>();
         String contentLength = Long.toString(cacheResult.getContentLength());
         setResponseHeader(KEY_CONTENT_LENGTH, contentLength);
-        long expires = cacheResult.getExpires();
-        if (expires >= 0) {
-            // "Expires" header is valid and finite. Milliseconds since 1970
-            // epoch, formatted as RFC-1123.
-            String expiresString = DateUtils.formatDate(new Date(expires));
-            setResponseHeader(KEY_EXPIRES, expiresString);
+        String expires = cacheResult.getExpiresString();
+        if (expires != null) {
+            setResponseHeader(KEY_EXPIRES, expires);
         }
         String lastModified = cacheResult.getLastModified();
         if (lastModified != null) {
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 04cb8a0..f92eb99 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -320,11 +320,6 @@
         
         final int max = getMax();
         progress += scale * max;
-        if (progress < 0) {
-            progress = 0;
-        } else if (progress > max) {
-            progress = max;
-        }
         
         setProgress((int) progress, true);
     }
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 7d2fcbc..fe6d91a 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -163,7 +163,7 @@
     /**
      * View to show if there are no items to show.
      */
-    View mEmptyView;
+    private View mEmptyView;
 
     /**
      * The number of items in the current adapter.
diff --git a/core/java/android/widget/AlphabetIndexer.java b/core/java/android/widget/AlphabetIndexer.java
index f50676a..59b2c2a 100644
--- a/core/java/android/widget/AlphabetIndexer.java
+++ b/core/java/android/widget/AlphabetIndexer.java
@@ -28,7 +28,7 @@
  * invalidates the cache if changes occur in the cursor.
  * <p/>
  * Your adapter is responsible for updating the cursor by calling {@link #setCursor} if the
- * cursor changes. {@link #getPositionForSection} method does the binary search for the starting 
+ * cursor changes. {@link #getPositionForSection} method does the binary search for the starting
  * index of a given section (alphabet).
  */
 public class AlphabetIndexer extends DataSetObserver implements SectionIndexer {
@@ -37,33 +37,33 @@
      * Cursor that is used by the adapter of the list view.
      */
     protected Cursor mDataCursor;
-    
+
     /**
      * The index of the cursor column that this list is sorted on.
      */
     protected int mColumnIndex;
-    
+
     /**
      * The string of characters that make up the indexing sections.
      */
     protected CharSequence mAlphabet;
-    
+
     /**
      * Cached length of the alphabet array.
      */
     private int mAlphabetLength;
-    
+
     /**
      * This contains a cache of the computed indices so far. It will get reset whenever
      * the dataset changes or the cursor changes.
      */
     private SparseIntArray mAlphaMap;
-    
+
     /**
      * Use a collator to compare strings in a localized manner.
      */
     private java.text.Collator mCollator;
-    
+
     /**
      * The section array converted from the alphabet string.
      */
@@ -72,9 +72,9 @@
     /**
      * Constructs the indexer.
      * @param cursor the cursor containing the data set
-     * @param sortedColumnIndex the column number in the cursor that is sorted 
+     * @param sortedColumnIndex the column number in the cursor that is sorted
      *        alphabetically
-     * @param alphabet string containing the alphabet, with space as the first character. 
+     * @param alphabet string containing the alphabet, with space as the first character.
      *        For example, use the string " ABCDEFGHIJKLMNOPQRSTUVWXYZ" for English indexing.
      *        The characters must be uppercase and be sorted in ascii/unicode order. Basically
      *        characters in the alphabet will show up as preview letters.
@@ -104,7 +104,7 @@
     public Object[] getSections() {
         return mAlphabetArray;
     }
-    
+
     /**
      * Sets a new cursor as the data set and resets the cache of indices.
      * @param cursor the new cursor to use as the data set
@@ -124,9 +124,16 @@
      * Default implementation compares the first character of word with letter.
      */
     protected int compare(String word, String letter) {
-        return mCollator.compare(word.substring(0, 1), letter);
+        final String firstLetter;
+        if (word.length() == 0) {
+            firstLetter = " ";
+        } else {
+            firstLetter = word.substring(0, 1);
+        }
+
+        return mCollator.compare(firstLetter, letter);
     }
-    
+
     /**
      * Performs a binary search or cache lookup to find the first row that
      * matches a given section's starting letter.
@@ -143,7 +150,7 @@
         if (cursor == null || mAlphabet == null) {
             return 0;
         }
-        
+
         // Check bounds
         if (sectionIndex <= 0) {
             return 0;
@@ -164,7 +171,7 @@
         int key = letter;
         // Check map
         if (Integer.MIN_VALUE != (pos = alphaMap.get(key, Integer.MIN_VALUE))) {
-            // Is it approximate? Using negative value to indicate that it's 
+            // Is it approximate? Using negative value to indicate that it's
             // an approximation and positive value when it is the accurate
             // position.
             if (pos < 0) {
@@ -204,7 +211,7 @@
             }
             int diff = compare(curName, targetLetter);
             if (diff != 0) {
-                // Commenting out approximation code because it doesn't work for certain 
+                // TODO: Commenting out approximation code because it doesn't work for certain
                 // lists with custom comparators
                 // Enter approximation in hash if a better solution doesn't exist
                 // String startingLetter = Character.toString(getFirstLetter(curName));
@@ -259,9 +266,9 @@
                 return i;
             }
         }
-        return 0; // Don't recognize the letter - falls under zero'th section    
+        return 0; // Don't recognize the letter - falls under zero'th section
     }
-    
+
     /*
      * @hide
      */
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java
index 32e5504..c28210d 100644
--- a/core/java/android/widget/ArrayAdapter.java
+++ b/core/java/android/widget/ArrayAdapter.java
@@ -348,12 +348,7 @@
                     "ArrayAdapter requires the resource ID to be a TextView", e);
         }
 
-        T item = getItem(position);
-        if (item instanceof CharSequence) {
-            text.setText((CharSequence)item);
-        } else {
-            text.setText(item.toString());
-        }
+        text.setText(getItem(position).toString());
 
         return view;
     }
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index e84e5b0..ba47df5 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -188,7 +188,7 @@
         setFocusable(true);
 
         addTextChangedListener(new MyWatcher());
-
+        
         mPassThroughClickListener = new PassThroughClickListener();
         super.setOnClickListener(mPassThroughClickListener);
     }
@@ -318,8 +318,6 @@
      * @return the background drawable
      * 
      * @attr ref android.R.styleable#PopupWindow_popupBackground
-     *
-     * @hide Pending API council approval
      */
     public Drawable getDropDownBackground() {
         return mPopup.getBackground();
@@ -331,8 +329,6 @@
      * @param d the drawable to set as the background
      * 
      * @attr ref android.R.styleable#PopupWindow_popupBackground
-     *
-     * @hide Pending API council approval
      */
     public void setDropDownBackgroundDrawable(Drawable d) {
         mPopup.setBackgroundDrawable(d);
@@ -344,14 +340,48 @@
      * @param id the id of the drawable to set as the background
      * 
      * @attr ref android.R.styleable#PopupWindow_popupBackground
-     *
-     * @hide Pending API council approval
      */
     public void setDropDownBackgroundResource(int id) {
         mPopup.setBackgroundDrawable(getResources().getDrawable(id));
     }
-
+    
     /**
+     * <p>Sets the vertical offset used for the auto-complete drop-down list.</p>
+     * 
+     * @param offset the vertical offset
+     */
+    public void setDropDownVerticalOffset(int offset) {
+        mDropDownVerticalOffset = offset;
+    }
+    
+    /**
+     * <p>Gets the vertical offset used for the auto-complete drop-down list.</p>
+     * 
+     * @return the vertical offset
+     */
+    public int getDropDownVerticalOffset() {
+        return mDropDownVerticalOffset;
+    }
+    
+    /**
+     * <p>Sets the horizontal offset used for the auto-complete drop-down list.</p>
+     * 
+     * @param offset the horizontal offset
+     */
+    public void setDropDownHorizontalOffset(int offset) {
+        mDropDownHorizontalOffset = offset;
+    }
+    
+    /**
+     * <p>Gets the horizontal offset used for the auto-complete drop-down list.</p>
+     * 
+     * @return the horizontal offset
+     */
+    public int getDropDownHorizontalOffset() {
+        return mDropDownHorizontalOffset;
+    }
+
+     /**
      * <p>Sets the animation style of the auto-complete drop-down list.</p>
      *
      * <p>If the drop-down is showing, calling this method will take effect only
@@ -378,50 +408,6 @@
     public int getDropDownAnimationStyle() {
         return mPopup.getAnimationStyle();
     }
-    
-    /**
-     * <p>Sets the vertical offset used for the auto-complete drop-down list.</p>
-     * 
-     * @param offset the vertical offset
-     *
-     * @hide Pending API council approval
-     */
-    public void setDropDownVerticalOffset(int offset) {
-        mDropDownVerticalOffset = offset;
-    }
-    
-    /**
-     * <p>Gets the vertical offset used for the auto-complete drop-down list.</p>
-     * 
-     * @return the vertical offset
-     *
-     * @hide Pending API council approval
-     */
-    public int getDropDownVerticalOffset() {
-        return mDropDownVerticalOffset;
-    }
-    
-    /**
-     * <p>Sets the horizontal offset used for the auto-complete drop-down list.</p>
-     * 
-     * @param offset the horizontal offset
-     *
-     * @hide Pending API council approval
-     */
-    public void setDropDownHorizontalOffset(int offset) {
-        mDropDownHorizontalOffset = offset;
-    }
-    
-    /**
-     * <p>Gets the horizontal offset used for the auto-complete drop-down list.</p>
-     * 
-     * @return the horizontal offset
-     *
-     * @hide Pending API council approval
-     */
-    public int getDropDownHorizontalOffset() {
-        return mDropDownHorizontalOffset;
-    }
 
     /**
      * @return Whether the drop-down is visible as long as there is {@link #enoughToFilter()}
@@ -1146,10 +1132,7 @@
 
             mPopup.setWindowLayoutMode(widthSpec, heightSpec);
             mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
-            
-            // use outside touchable to dismiss drop down when touching outside of it, so
-            // only set this if the dropdown is not always visible
-            mPopup.setOutsideTouchable(!mDropDownAlwaysVisible);
+            mPopup.setOutsideTouchable(true);
             mPopup.setTouchInterceptor(new PopupTouchIntercepter());
             mPopup.showAsDropDown(getDropDownAnchorView(),
                     mDropDownHorizontalOffset, mDropDownVerticalOffset);
@@ -1521,7 +1504,7 @@
          */
         CharSequence fixText(CharSequence invalidText);
     }
-
+    
     /**
      * Allows us a private hook into the on click event without preventing users from setting
      * their own click listener.
@@ -1537,5 +1520,5 @@
             if (mWrapped != null) mWrapped.onClick(v);
         }
     }
-
+    
 }
diff --git a/core/java/android/widget/SimpleCursorTreeAdapter.java b/core/java/android/widget/SimpleCursorTreeAdapter.java
index c456f56..a1c65f0 100644
--- a/core/java/android/widget/SimpleCursorTreeAdapter.java
+++ b/core/java/android/widget/SimpleCursorTreeAdapter.java
@@ -26,9 +26,16 @@
  * defined in an XML file. You can specify which columns you want, which views
  * you want to display the columns, and the XML file that defines the appearance
  * of these views. Separate XML files for child and groups are possible.
- * TextViews bind the values to their text property (see
- * {@link TextView#setText(CharSequence)}). ImageViews bind the values to their
- * image's Uri property (see {@link ImageView#setImageURI(android.net.Uri)}).
+ *
+ * Binding occurs in two phases. First, if a
+ * {@link android.widget.SimpleCursorTreeAdapter.ViewBinder} is available,
+ * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
+ * is invoked. If the returned value is true, binding has occurred. If the
+ * returned value is false and the view to bind is a TextView,
+ * {@link #setViewText(TextView, String)} is invoked. If the returned value
+ * is false and the view to bind is an ImageView,
+ * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
+ * binding can be found, an {@link IllegalStateException} is thrown.
  */
 public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter {
     /** The indices of columns that contain data to display for a group. */
@@ -48,6 +55,11 @@
     private int[] mChildTo;
     
     /**
+     * View binder, if supplied
+     */
+    private ViewBinder mViewBinder;
+
+    /**
      * Constructor.
      * 
      * @param context The context where the {@link ExpandableListView}
@@ -193,21 +205,53 @@
         initFromColumns(childCursor, childFromNames, mChildFrom);
     }
     
+    /**
+     * Returns the {@link ViewBinder} used to bind data to views.
+     *
+     * @return a ViewBinder or null if the binder does not exist
+     *
+     * @see #setViewBinder(android.widget.SimpleCursorTreeAdapter.ViewBinder)
+     */
+    public ViewBinder getViewBinder() {
+        return mViewBinder;
+    }
+
+    /**
+     * Sets the binder used to bind data to views.
+     *
+     * @param viewBinder the binder used to bind data to views, can be null to
+     *        remove the existing binder
+     *
+     * @see #getViewBinder()
+     */
+    public void setViewBinder(ViewBinder viewBinder) {
+        mViewBinder = viewBinder;
+    }
+
     private void bindView(View view, Context context, Cursor cursor, int[] from, int[] to) {
+        final ViewBinder binder = mViewBinder;
+        
         for (int i = 0; i < to.length; i++) {
             View v = view.findViewById(to[i]);
             if (v != null) {
-                String text = cursor.getString(from[i]);
-                if (text == null) {
-                    text = "";
+                boolean bound = false;
+                if (binder != null) {
+                    bound = binder.setViewValue(v, cursor, from[i]);
                 }
-                if (v instanceof TextView) {
-                    ((TextView) v).setText(text);
-                } else if (v instanceof ImageView) {
-                    setViewImage((ImageView) v, text);
-                } else {
-                    throw new IllegalStateException("SimpleCursorAdapter can bind values only to" +
-                            " TextView and ImageView!");
+                
+                if (!bound) {
+                    String text = cursor.getString(from[i]);
+                    if (text == null) {
+                        text = "";
+                    }
+                    if (v instanceof TextView) {
+                        setViewText((TextView) v, text);
+                    } else if (v instanceof ImageView) {
+                        setViewImage((ImageView) v, text);
+                    } else {
+                        throw new IllegalStateException("SimpleCursorTreeAdapter can bind values" +
+                                " only to TextView and ImageView!");
+                    }
                 }
             }
         }
@@ -238,4 +282,48 @@
             v.setImageURI(Uri.parse(value));
         }
     }
+
+    /**
+     * Called by bindView() to set the text for a TextView but only if
+     * there is no existing ViewBinder or if the existing ViewBinder cannot
+     * handle binding to an TextView.
+     *
+     * Intended to be overridden by Adapters that need to filter strings
+     * retrieved from the database.
+     * 
+     * @param v TextView to receive text
+     * @param text the text to be set for the TextView
+     */
+    public void setViewText(TextView v, String text) {
+        v.setText(text);
+    }
+
+    /**
+     * This class can be used by external clients of SimpleCursorTreeAdapter
+     * to bind values from the Cursor to views.
+     *
+     * You should use this class to bind values from the Cursor to views
+     * that are not directly supported by SimpleCursorTreeAdapter or to
+     * change the way binding occurs for views supported by
+     * SimpleCursorTreeAdapter.
+     *
+     * @see SimpleCursorTreeAdapter#setViewImage(ImageView, String) 
+     * @see SimpleCursorTreeAdapter#setViewText(TextView, String)
+     */
+    public static interface ViewBinder {
+        /**
+         * Binds the Cursor column defined by the specified index to the specified view.
+         *
+         * When binding is handled by this ViewBinder, this method must return true.
+         * If this method returns false, SimpleCursorTreeAdapter will attempts to handle
+         * the binding on its own.
+         *
+         * @param view the view to bind the data to
+         * @param cursor the cursor to get the data from
+         * @param columnIndex the column at which the data can be found in the cursor
+         *
+         * @return true if the data was bound to the view, false otherwise
+         */
+        boolean setViewValue(View view, Cursor cursor, int columnIndex);
+    }
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index f67a235..989286e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -93,7 +93,26 @@
 
     /** Controls whether we should preload resources during zygote init. */
     private static final boolean PRELOAD_RESOURCES = true;
-    
+
+    /**
+     * List of methods we "warm up" in the register map cache.  These were
+     * chosen because they appeared on the stack in GCs in multiple
+     * applications.
+     *
+     * This is in a VM-ready format, to minimize string processing.  If a
+     * class is not already loaded, or a method is not found, the entry
+     * will be skipped.
+     *
+     * This doesn't really merit a separately-generated input file at this
+     * time.  The list is fairly short, and the consequences of failure
+     * are minor.
+     */
+    private static final String[] REGISTER_MAP_METHODS = {
+        // (currently not doing any)
+        //"Landroid/app/Activity;.setContentView:(I)V",
+    };
+
+
     /**
      * Invokes a static "main(argv[]) method on class "className".
      * Converts various failing exceptions into RuntimeExceptions, with
@@ -319,6 +338,45 @@
     }
 
     /**
+     * Pre-caches register maps for methods that are commonly used.
+     */
+    private static void cacheRegisterMaps() {
+        String failed = null;
+        int failure;
+        long startTime = System.nanoTime();
+
+        failure = 0;
+
+        for (int i = 0; i < REGISTER_MAP_METHODS.length; i++) {
+            String str = REGISTER_MAP_METHODS[i];
+
+            if (!Debug.cacheRegisterMap(str)) {
+                if (failed == null)
+                    failed = str;
+                failure++;
+            }
+        }
+
+        long delta = System.nanoTime() - startTime;
+
+        if (failure == REGISTER_MAP_METHODS.length) {
+            if (REGISTER_MAP_METHODS.length > 0) {
+                Log.i(TAG,
+                    "Register map caching failed (precise GC not enabled?)");
+            }
+            return;
+        }
+
+        Log.i(TAG, "Register map cache: found " +
+            (REGISTER_MAP_METHODS.length - failure) + " of " +
+            REGISTER_MAP_METHODS.length + " methods in " +
+            (delta / 1000000L) + "ms");
+        if (failure > 0) {
+            Log.i(TAG, "  First failure: " + failed);
+        }
+    }
+
+    /**
      * Load in commonly used resources, so they can be shared across
      * processes.
      *
@@ -510,6 +568,7 @@
             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                 SystemClock.uptimeMillis());
             preloadClasses();
+            //cacheRegisterMaps();
             preloadResources();
             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                 SystemClock.uptimeMillis());
diff --git a/core/java/com/google/android/mms/pdu/EncodedStringValue.java b/core/java/com/google/android/mms/pdu/EncodedStringValue.java
index 7696c5e..a27962d 100644
--- a/core/java/com/google/android/mms/pdu/EncodedStringValue.java
+++ b/core/java/com/google/android/mms/pdu/EncodedStringValue.java
@@ -269,4 +269,16 @@
 
         return new EncodedStringValue(value.mCharacterSet, value.mData);
     }
+    
+    public static EncodedStringValue[] encodeStrings(String[] array) {
+        int count = array.length;
+        if (count > 0) {
+            EncodedStringValue[] encodedArray = new EncodedStringValue[count];
+            for (int i = 0; i < count; i++) {
+                encodedArray[i] = new EncodedStringValue(array[i]);
+            }
+            return encodedArray;
+        }
+        return null;
+    }
 }
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index d2a41f1..74af7650 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -31,6 +31,7 @@
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.net.Uri;
+import android.provider.Telephony;
 import android.provider.Telephony.Mms;
 import android.provider.Telephony.MmsSms;
 import android.provider.Telephony.Threads;
@@ -54,6 +55,8 @@
 import java.util.Set;
 import java.util.Map.Entry;
 
+import com.google.android.mms.pdu.EncodedStringValue;
+
 /**
  * This class is the high-level manager of PDU storage.
  */
@@ -159,6 +162,7 @@
         Part.CONTENT_TYPE,
         Part.FILENAME,
         Part.NAME,
+        Part.TEXT
     };
 
     private static final int PART_COLUMN_ID                  = 0;
@@ -169,6 +173,7 @@
     private static final int PART_COLUMN_CONTENT_TYPE        = 5;
     private static final int PART_COLUMN_FILENAME            = 6;
     private static final int PART_COLUMN_NAME                = 7;
+    private static final int PART_COLUMN_TEXT                = 8;
 
     private static final HashMap<Uri, Integer> MESSAGE_BOX_MAP;
     // These map are used for convenience in persist() and load().
@@ -414,26 +419,36 @@
                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
                     InputStream is = null;
 
-                    try {
-                        is = mContentResolver.openInputStream(partURI);
+                    // Store simple string values directly in the database instead of an
+                    // external file.  This makes the text searchable and retrieval slightly
+                    // faster.
+                    if ("text/plain".equals(type) || "application/smil".equals(type)) {
+                        String text = c.getString(PART_COLUMN_TEXT);
+                        byte [] blob = new EncodedStringValue(text).getTextString();
+                        baos.write(blob, 0, blob.length);
+                    } else {
 
-                        byte[] buffer = new byte[256];
-                        int len = is.read(buffer);
-                        while (len >= 0) {
-                            baos.write(buffer, 0, len);
-                            len = is.read(buffer);
-                        }
-                    } catch (IOException e) {
-                        Log.e(TAG, "Failed to load part data", e);
-                        c.close();
-                        throw new MmsException(e);
-                    } finally {
-                        if (is != null) {
-                            try {
-                                is.close();
-                            } catch (IOException e) {
-                                Log.e(TAG, "Failed to close stream", e);
-                            } // Ignore
+                        try {
+                            is = mContentResolver.openInputStream(partURI);
+
+                            byte[] buffer = new byte[256];
+                            int len = is.read(buffer);
+                            while (len >= 0) {
+                                baos.write(buffer, 0, len);
+                                len = is.read(buffer);
+                            }
+                        } catch (IOException e) {
+                            Log.e(TAG, "Failed to load part data", e);
+                            c.close();
+                            throw new MmsException(e);
+                        } finally {
+                            if (is != null) {
+                                try {
+                                    is.close();
+                                } catch (IOException e) {
+                                    Log.e(TAG, "Failed to close stream", e);
+                                } // Ignore
+                            }
                         }
                     }
                     part.setData(baos.toByteArray());
@@ -719,29 +734,37 @@
         InputStream is = null;
 
         try {
-            os = mContentResolver.openOutputStream(uri);
             byte[] data = part.getData();
-            if (data == null) {
-                Uri dataUri = part.getDataUri();
-                if ((dataUri == null) || (dataUri == uri)) {
-                    Log.w(TAG, "Can't find data for this part.");
-                    return;
-                }
-                is = mContentResolver.openInputStream(dataUri);
-                
-                if (LOCAL_LOGV) {
-                    Log.v(TAG, "Saving data to: " + uri);
-                }
-
-                byte[] buffer = new byte[256];
-                for (int len = 0; (len = is.read(buffer)) != -1; ) {
-                    os.write(buffer, 0, len);
+            if ("text/plain".equals(contentType) || "application/smil".equals(contentType)) {
+                ContentValues cv = new ContentValues();
+                cv.put(Telephony.Mms.Part.TEXT, new EncodedStringValue(data).getString());
+                if (mContentResolver.update(uri, cv, null, null) != 1) {
+                    throw new MmsException("unable to update " + uri.toString());
                 }
             } else {
-                if (LOCAL_LOGV) {
-                    Log.v(TAG, "Saving data to: " + uri);
+                os = mContentResolver.openOutputStream(uri);
+                if (data == null) {
+                    Uri dataUri = part.getDataUri();
+                    if ((dataUri == null) || (dataUri == uri)) {
+                        Log.w(TAG, "Can't find data for this part.");
+                        return;
+                    }
+                    is = mContentResolver.openInputStream(dataUri);
+
+                    if (LOCAL_LOGV) {
+                        Log.v(TAG, "Saving data to: " + uri);
+                    }
+
+                    byte[] buffer = new byte[256];
+                    for (int len = 0; (len = is.read(buffer)) != -1; ) {
+                        os.write(buffer, 0, len);
+                    }
+                } else {
+                    if (LOCAL_LOGV) {
+                        Log.v(TAG, "Saving data to: " + uri);
+                    }
+                    os.write(data);
                 }
-                os.write(data);
             }
         } catch (FileNotFoundException e) {
             Log.e(TAG, "Failed to open Input/Output stream.", e);
diff --git a/core/jni/ActivityManager.cpp b/core/jni/ActivityManager.cpp
index 9017827..8950dfb 100644
--- a/core/jni/ActivityManager.cpp
+++ b/core/jni/ActivityManager.cpp
@@ -16,9 +16,9 @@
 
 #include <unistd.h>
 #include <android_runtime/ActivityManager.h>
-#include <utils/IBinder.h>
-#include <utils/IServiceManager.h>
-#include <utils/Parcel.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
 #include <utils/String8.h>
 
 namespace android {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 888cb11..73eea15 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -11,6 +11,10 @@
 	LOCAL_CFLAGS += -DPACKED=""
 endif
 
+ifeq ($(WITH_JIT),true)
+	LOCAL_CFLAGS += -DWITH_JIT
+endif
+
 ifneq ($(USE_CUSTOM_RUNTIME_HEAP_MAX),)
   LOCAL_CFLAGS += -DCUSTOM_RUNTIME_HEAP_MAX=$(USE_CUSTOM_RUNTIME_HEAP_MAX)
 endif
@@ -39,7 +43,6 @@
 	android_text_AndroidCharacter.cpp \
 	android_text_KeyCharacterMap.cpp \
 	android_os_Debug.cpp \
-	android_os_Exec.cpp \
 	android_os_FileUtils.cpp \
 	android_os_MemoryFile.cpp \
 	android_os_ParcelFileDescriptor.cpp \
@@ -104,11 +107,10 @@
 	android_util_FileObserver.cpp \
 	android/opengl/poly_clip.cpp.arm \
 	android/opengl/util.cpp.arm \
-	android_bluetooth_Database.cpp \
 	android_bluetooth_HeadsetBase.cpp \
 	android_bluetooth_common.cpp \
 	android_bluetooth_BluetoothAudioGateway.cpp \
-	android_bluetooth_RfcommSocket.cpp \
+	android_bluetooth_BluetoothSocket.cpp \
 	android_bluetooth_ScoSocket.cpp \
 	android_server_BluetoothDeviceService.cpp \
 	android_server_BluetoothEventLoop.cpp \
@@ -150,6 +152,7 @@
 	libnativehelper \
 	libcutils \
 	libutils \
+	libbinder \
 	libnetutils \
 	libui \
 	libskiagl \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c815301..c322b17 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -19,12 +19,12 @@
 //#define LOG_NDEBUG 0
 
 #include <android_runtime/AndroidRuntime.h>
-#include <utils/IBinder.h>
-#include <utils/IServiceManager.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
 #include <utils/Log.h>
 #include <utils/misc.h>
-#include <utils/Parcel.h>
-#include <utils/string_array.h>
+#include <binder/Parcel.h>
+#include <utils/StringArray.h>
 #include <utils/threads.h>
 #include <cutils/properties.h>
 
@@ -130,7 +130,6 @@
 extern int register_android_os_StatFs(JNIEnv *env);
 extern int register_android_os_SystemProperties(JNIEnv *env);
 extern int register_android_os_Hardware(JNIEnv* env);
-extern int register_android_os_Exec(JNIEnv *env);
 extern int register_android_os_SystemClock(JNIEnv* env);
 extern int register_android_os_FileObserver(JNIEnv *env);
 extern int register_android_os_FileUtils(JNIEnv *env);
@@ -143,10 +142,9 @@
 extern int register_android_text_AndroidCharacter(JNIEnv *env);
 extern int register_android_text_KeyCharacterMap(JNIEnv *env);
 extern int register_android_opengl_classes(JNIEnv *env);
-extern int register_android_bluetooth_Database(JNIEnv* env);
 extern int register_android_bluetooth_HeadsetBase(JNIEnv* env);
 extern int register_android_bluetooth_BluetoothAudioGateway(JNIEnv* env);
-extern int register_android_bluetooth_RfcommSocket(JNIEnv *env);
+extern int register_android_bluetooth_BluetoothSocket(JNIEnv *env);
 extern int register_android_bluetooth_ScoSocket(JNIEnv *env);
 extern int register_android_server_BluetoothDeviceService(JNIEnv* env);
 extern int register_android_server_BluetoothEventLoop(JNIEnv *env);
@@ -509,11 +507,17 @@
     //LOGD("language=%s region=%s\n", language, region);
 }
 
-void AndroidRuntime::start(const char* className, const bool startSystemServer)
+/*
+ * Start the Dalvik Virtual Machine.
+ *
+ * Various arguments, most determined by system properties, are passed in.
+ * The "mOptions" vector is updated.
+ *
+ * Returns 0 on success.
+ */
+int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
 {
-    LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n");
-
-    JNIEnv* env;
+    int result = -1;
     JavaVMInitArgs initArgs;
     JavaVMOption opt;
     char propBuf[PROPERTY_VALUE_MAX];
@@ -522,24 +526,18 @@
     char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX];
     char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
     char* stackTraceFile = NULL;
-    char* slashClassName = NULL;
-    char* cp;
     bool checkJni = false;
+    bool checkDexSum = false;
     bool logStdio = false;
-    enum { kEMDefault, kEMIntPortable, kEMIntFast } executionMode = kEMDefault;
+    enum {
+      kEMDefault,
+      kEMIntPortable,
+      kEMIntFast,
+#if defined(WITH_JIT)
+      kEMJitCompiler,
+#endif
+    } executionMode = kEMDefault;
 
-    blockSigpipe();
-
-    /* 
-     * 'startSystemServer == true' means runtime is obslete and not run from 
-     * init.rc anymore, so we print out the boot start event here.
-     */
-    if (startSystemServer) {
-        /* track our progress through the boot sequence */
-        const int LOG_BOOT_PROGRESS_START = 3000;
-        LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, 
-                       ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
-    }
 
     property_get("dalvik.vm.checkjni", propBuf, "");
     if (strcmp(propBuf, "true") == 0) {
@@ -557,10 +555,19 @@
         executionMode = kEMIntPortable;
     } else if (strcmp(propBuf, "int:fast") == 0) {
         executionMode = kEMIntFast;
+#if defined(WITH_JIT)
+    } else if (strcmp(propBuf, "int:jit") == 0) {
+        executionMode = kEMJitCompiler;
+#endif
     }
 
     property_get("dalvik.vm.stack-trace-file", stackTraceFileBuf, "");
 
+    property_get("dalvik.vm.check-dex-sum", propBuf, "");
+    if (strcmp(propBuf, "true") == 0) {
+        checkDexSum = true;
+    }
+
     property_get("log.redirect-stdio", propBuf, "");
     if (strcmp(propBuf, "true") == 0) {
         logStdio = true;
@@ -572,19 +579,6 @@
     strcpy(jniOptsBuf, "-Xjniopts:");
     property_get("dalvik.vm.jniopts", jniOptsBuf+10, "");
 
-    const char* rootDir = getenv("ANDROID_ROOT");
-    if (rootDir == NULL) {
-        rootDir = "/system";
-        if (!hasDir("/system")) {
-            LOG_FATAL("No root directory specified, and /android does not exist.");
-            return;
-        }
-        setenv("ANDROID_ROOT", rootDir, 1);
-    }
-
-    const char* kernelHack = getenv("LD_ASSUME_KERNEL");
-    //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
-
     /* route exit() to our handler */
     opt.extraInfo = (void*) runtime_exit;
     opt.optionString = "exit";
@@ -658,6 +652,10 @@
         if (opc != NULL) {
             opt.optionString = "-Xgenregmap";
             mOptions.add(opt);
+
+            /* turn on precise GC while we're at it */
+            opt.optionString = "-Xgc:precise";
+            mOptions.add(opt);
         }
     }
 
@@ -697,13 +695,78 @@
         //opt.optionString = "-verbose:jni";
         //mOptions.add(opt);
     }
+
+#if defined(WITH_JIT)
+    /* Minimal profile threshold to trigger JIT compilation */
+    char jitThresholdBuf[sizeof("-Xthreshold:") + PROPERTY_VALUE_MAX];
+    property_get("dalvik.vm.jit.threshold", propBuf, "");
+    if (strlen(propBuf) > 0) {
+        strcpy(jitThresholdBuf, "-Xthreshold:");
+        strcat(jitThresholdBuf, propBuf);
+        opt.optionString = jitThresholdBuf;
+        mOptions.add(opt);
+    }
+
+    /* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */
+    char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX];
+    property_get("dalvik.vm.jit.op", propBuf, "");
+    if (strlen(propBuf) > 0) {
+        strcpy(jitOpBuf, "-Xjitop:");
+        strcat(jitOpBuf, propBuf);
+        opt.optionString = jitOpBuf;
+        mOptions.add(opt);
+    }
+
+    /*
+     * Reverse the polarity of dalvik.vm.jit.op and force interpreter-only
+     * for non-selected opcodes.
+     */
+    property_get("dalvik.vm.jit.includeop", propBuf, "");
+    if (strlen(propBuf) > 0) {
+        opt.optionString = "-Xincludeselectedop";
+        mOptions.add(opt);
+    }
+
+    /* Force interpreter-only mode for selected methods */
+    char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];
+    property_get("dalvik.vm.jit.method", propBuf, "");
+    if (strlen(propBuf) > 0) {
+        strcpy(jitMethodBuf, "-Xjitmethod:");
+        strcat(jitMethodBuf, propBuf);
+        opt.optionString = jitMethodBuf;
+        mOptions.add(opt);
+    }
+
+    /*
+     * Reverse the polarity of dalvik.vm.jit.method and force interpreter-only
+     * for non-selected methods.
+     */
+    property_get("dalvik.vm.jit.includemethod", propBuf, "");
+    if (strlen(propBuf) > 0) {
+        opt.optionString = "-Xincludeselectedmethod";
+        mOptions.add(opt);
+    }
+#endif
+
     if (executionMode == kEMIntPortable) {
         opt.optionString = "-Xint:portable";
         mOptions.add(opt);
     } else if (executionMode == kEMIntFast) {
         opt.optionString = "-Xint:fast";
         mOptions.add(opt);
+#if defined(WITH_JIT)
+    } else if (executionMode == kEMJitCompiler) {
+        opt.optionString = "-Xint:jit";
+        mOptions.add(opt);
+#endif
     }
+
+    if (checkDexSum) {
+        /* perform additional DEX checksum tests */
+        opt.optionString = "-Xcheckdexsum";
+        mOptions.add(opt);
+    }
+
     if (logStdio) {
         /* convert stdout/stderr to log messages */
         opt.optionString = "-Xlog-stdio";
@@ -771,11 +834,61 @@
      * If this call succeeds, the VM is ready, and we can start issuing
      * JNI calls.
      */
-    if (JNI_CreateJavaVM(&mJavaVM, &env, &initArgs) < 0) {
+    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
         LOGE("JNI_CreateJavaVM failed\n");
         goto bail;
     }
 
+    result = 0;
+
+bail:
+    free(stackTraceFile);
+    return result;
+}
+
+/*
+ * Start the Android runtime.  This involves starting the virtual machine
+ * and calling the "static void main(String[] args)" method in the class
+ * named by "className".
+ */
+void AndroidRuntime::start(const char* className, const bool startSystemServer)
+{
+    LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n");
+
+    char* slashClassName = NULL;
+    char* cp;
+    JNIEnv* env;
+
+    blockSigpipe();
+
+    /* 
+     * 'startSystemServer == true' means runtime is obslete and not run from 
+     * init.rc anymore, so we print out the boot start event here.
+     */
+    if (startSystemServer) {
+        /* track our progress through the boot sequence */
+        const int LOG_BOOT_PROGRESS_START = 3000;
+        LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, 
+                       ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
+    }
+
+    const char* rootDir = getenv("ANDROID_ROOT");
+    if (rootDir == NULL) {
+        rootDir = "/system";
+        if (!hasDir("/system")) {
+            LOG_FATAL("No root directory specified, and /android does not exist.");
+            goto bail;
+        }
+        setenv("ANDROID_ROOT", rootDir, 1);
+    }
+
+    //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
+    //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
+
+    /* start the virtual machine */
+    if (startVm(&mJavaVM, &env) != 0)
+        goto bail;
+
     /*
      * Register android functions.
      */
@@ -845,7 +958,6 @@
 
 bail:
     free(slashClassName);
-    free(stackTraceFile);
 }
 
 void AndroidRuntime::start()
@@ -1095,7 +1207,6 @@
     REG_JNI(register_android_database_SQLiteQuery),
     REG_JNI(register_android_database_SQLiteStatement),
     REG_JNI(register_android_os_Debug),
-    REG_JNI(register_android_os_Exec),
     REG_JNI(register_android_os_FileObserver),
     REG_JNI(register_android_os_FileUtils),
     REG_JNI(register_android_os_ParcelFileDescriptor),
@@ -1117,10 +1228,9 @@
     REG_JNI(register_android_media_ToneGenerator),
 
     REG_JNI(register_android_opengl_classes),
-    REG_JNI(register_android_bluetooth_Database),
     REG_JNI(register_android_bluetooth_HeadsetBase),
     REG_JNI(register_android_bluetooth_BluetoothAudioGateway),
-    REG_JNI(register_android_bluetooth_RfcommSocket),
+    REG_JNI(register_android_bluetooth_BluetoothSocket),
     REG_JNI(register_android_bluetooth_ScoSocket),
     REG_JNI(register_android_server_BluetoothDeviceService),
     REG_JNI(register_android_server_BluetoothEventLoop),
diff --git a/core/jni/CursorWindow.cpp b/core/jni/CursorWindow.cpp
index fb891c9..7864189 100644
--- a/core/jni/CursorWindow.cpp
+++ b/core/jni/CursorWindow.cpp
@@ -18,7 +18,7 @@
 #define LOG_TAG "CursorWindow"
 
 #include <utils/Log.h>
-#include <utils/MemoryDealer.h>
+#include <binder/MemoryDealer.h>
 
 #include <assert.h>
 #include <string.h>
diff --git a/core/jni/CursorWindow.h b/core/jni/CursorWindow.h
index 0fb074f..e98b009 100644
--- a/core/jni/CursorWindow.h
+++ b/core/jni/CursorWindow.h
@@ -21,7 +21,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <utils/MemoryDealer.h>
+#include <binder/MemoryDealer.h>
 #include <utils/RefBase.h>
 
 #include <jni.h>
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index af8ecf5..957b825 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -5,7 +5,7 @@
 #include "SkDither.h"

 #include "SkUnPreMultiply.h"

 

-#include "Parcel.h"

+#include <binder/Parcel.h>

 #include "android_util_Binder.h"

 #include "android_nio_utils.h"

 #include "CreateJavaOutputStreamAdaptor.h"

diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 93d68cb..c61b2ed 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -23,6 +23,7 @@
 #include "SkGLCanvas.h"
 #include "SkGraphics.h"
 #include "SkImageRef_GlobalPool.h"
+#include "SkPorterDuff.h"
 #include "SkShader.h"
 #include "SkTemplates.h"
 
@@ -324,7 +325,7 @@
  
     static void drawColor__II(JNIEnv* env, jobject, SkCanvas* canvas,
                               jint color, SkPorterDuff::Mode mode) {
-        canvas->drawColor(color, mode);
+        canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
     }
  
     static void drawPaint(JNIEnv* env, jobject, SkCanvas* canvas,
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index b6ec4a2..ebfb209 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -21,6 +21,7 @@
 
 #include "SkColorFilter.h"
 #include "SkColorMatrixFilter.h"
+#include "SkPorterDuff.h"
 
 namespace android {
 
@@ -32,8 +33,9 @@
     }
 
     static SkColorFilter* CreatePorterDuffFilter(JNIEnv* env, jobject,
-                            jint srcColor, SkPorterDuff::Mode porterDuffMode) {
-        return SkColorFilter::CreatePorterDuffFilter(srcColor, porterDuffMode);
+                            jint srcColor, SkPorterDuff::Mode mode) {
+        return SkColorFilter::CreateModeFilter(srcColor,
+                                           SkPorterDuff::ToXfermodeMode(mode));
     }
  
     static SkColorFilter* CreateLightingFilter(JNIEnv* env, jobject,
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index effb1c8..11c608c 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -75,9 +75,8 @@
         return result;
     }
  
-    static void computeBounds(JNIEnv* env, jobject clazz, SkPath* obj, jobject bounds, SkPath::BoundsType btype) {
-        SkRect bounds_;
-        obj->computeBounds(&bounds_, btype);
+    static void computeBounds(JNIEnv* env, jobject clazz, SkPath* obj, jobject bounds, int boundstype) {
+        const SkRect& bounds_ = obj->getBounds();
         GraphicsJNI::rect_to_jrectf(bounds_, env, bounds);
     }
  
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index 1dc0314..723cd37 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -134,7 +134,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-#include "Parcel.h"
+#include <binder/Parcel.h>
 #include "android_util_Binder.h"
 
 static SkRegion* Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel)
diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp
new file mode 100644
index 0000000..9c4f7c7
--- /dev/null
+++ b/core/jni/android_bluetooth_BluetoothSocket.cpp
@@ -0,0 +1,517 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BluetoothSocket.cpp"
+
+#include "android_bluetooth_common.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "JNIHelp.h"
+#include "utils/Log.h"
+#include "cutils/abort_socket.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_BLUETOOTH
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sco.h>
+#endif
+
+#define TYPE_AS_STR(t) \
+    ((t) == TYPE_RFCOMM ? "RFCOMM" : ((t) == TYPE_SCO ? "SCO" : "L2CAP"))
+
+namespace android {
+
+static jfieldID  field_mAuth;     /* read-only */
+static jfieldID  field_mEncrypt;  /* read-only */
+static jfieldID  field_mType;     /* read-only */
+static jfieldID  field_mAddress;  /* read-only */
+static jfieldID  field_mPort;     /* read-only */
+static jfieldID  field_mSocketData;
+static jmethodID method_BluetoothSocket_ctor;
+static jclass    class_BluetoothSocket;
+
+/* Keep TYPE_RFCOMM etc in sync with BluetoothSocket.java */
+static const int TYPE_RFCOMM = 1;
+static const int TYPE_SCO = 2;
+static const int TYPE_L2CAP = 3;  // TODO: Test l2cap code paths
+
+static struct asocket *get_socketData(JNIEnv *env, jobject obj) {
+    struct asocket *s =
+            (struct asocket *) env->GetIntField(obj, field_mSocketData);
+    if (!s)
+        jniThrowException(env, "java/io/IOException", "null socketData");
+    return s;
+}
+
+static void initSocketFromFdNative(JNIEnv *env, jobject obj, jint fd) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+
+    struct asocket *s = asocket_init(fd);
+
+    if (!s) {
+        LOGV("asocket_init() failed, throwing");
+        jniThrowIOException(env, errno);
+        return;
+    }
+
+    env->SetIntField(obj, field_mSocketData, (jint)s);
+
+    return;
+#endif
+    jniThrowIOException(env, ENOSYS);
+}
+
+static void initSocketNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+
+    int fd;
+    int lm = 0;
+    jboolean auth;
+    jboolean encrypt;
+    jint type;
+
+    type = env->GetIntField(obj, field_mType);
+
+    switch (type) {
+    case TYPE_RFCOMM:
+        fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+        break;
+    case TYPE_SCO:
+        fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+        break;
+    case TYPE_L2CAP:
+        fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+        break;
+    default:
+        jniThrowIOException(env, ENOSYS);
+        return;
+    }
+
+    if (fd < 0) {
+        LOGV("socket() failed, throwing");
+        jniThrowIOException(env, errno);
+        return;
+    }
+
+    auth = env->GetBooleanField(obj, field_mAuth);
+    encrypt = env->GetBooleanField(obj, field_mEncrypt);
+
+    /* kernel does not yet support LM for SCO */
+    switch (type) {
+    case TYPE_RFCOMM:
+        lm |= auth ? RFCOMM_LM_AUTH : 0;
+        lm |= encrypt? RFCOMM_LM_ENCRYPT : 0;
+        break;
+    case TYPE_L2CAP:
+        lm |= auth ? L2CAP_LM_AUTH : 0;
+        lm |= encrypt? L2CAP_LM_ENCRYPT : 0;
+        break;
+    }
+
+    if (lm) {
+        if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
+            LOGV("setsockopt() failed, throwing");
+            jniThrowIOException(env, errno);
+            return;
+        }
+    }
+
+    LOGV("...fd %d created (%s, lm = %x)", fd, TYPE_AS_STR(type), lm);
+
+    initSocketFromFdNative(env, obj, fd);
+    return;
+#endif
+    jniThrowIOException(env, ENOSYS);
+}
+
+static void connectNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+
+    int ret;
+    jint type;
+    const char *c_address;
+    jstring address;
+    bdaddr_t bdaddress;
+    socklen_t addr_sz;
+    struct sockaddr *addr;
+    struct asocket *s = get_socketData(env, obj);
+
+    if (!s)
+        return;
+
+    type = env->GetIntField(obj, field_mType);
+
+    /* parse address into bdaddress */
+    address = (jstring) env->GetObjectField(obj, field_mAddress);
+    c_address = env->GetStringUTFChars(address, NULL);
+    if (get_bdaddr(c_address, &bdaddress)) {
+        env->ReleaseStringUTFChars(address, c_address);
+        jniThrowIOException(env, EINVAL);
+        return;
+    }
+    env->ReleaseStringUTFChars(address, c_address);
+
+    switch (type) {
+    case TYPE_RFCOMM:
+        struct sockaddr_rc addr_rc;
+        addr = (struct sockaddr *)&addr_rc;
+        addr_sz = sizeof(addr_rc);
+
+        memset(addr, 0, addr_sz);
+        addr_rc.rc_family = AF_BLUETOOTH;
+        addr_rc.rc_channel = env->GetIntField(obj, field_mPort);
+        memcpy(&addr_rc.rc_bdaddr, &bdaddress, sizeof(bdaddr_t));
+
+        break;
+    case TYPE_SCO:
+        struct sockaddr_sco addr_sco;
+        addr = (struct sockaddr *)&addr_sco;
+        addr_sz = sizeof(addr_sco);
+
+        memset(addr, 0, addr_sz);
+        addr_sco.sco_family = AF_BLUETOOTH;
+        memcpy(&addr_sco.sco_bdaddr, &bdaddress, sizeof(bdaddr_t));
+
+        break;
+    case TYPE_L2CAP:
+        struct sockaddr_l2 addr_l2;
+        addr = (struct sockaddr *)&addr_l2;
+        addr_sz = sizeof(addr_l2);
+
+        memset(addr, 0, addr_sz);
+        addr_l2.l2_family = AF_BLUETOOTH;
+        addr_l2.l2_psm = env->GetIntField(obj, field_mPort);
+        memcpy(&addr_l2.l2_bdaddr, &bdaddress, sizeof(bdaddr_t));
+
+        break;
+    default:
+        jniThrowIOException(env, ENOSYS);
+        return;
+    }
+
+    ret = asocket_connect(s, addr, addr_sz, -1);
+    LOGV("...connect(%d, %s) = %d (errno %d)",
+            s->fd, TYPE_AS_STR(type), ret, errno);
+
+    if (ret)
+        jniThrowIOException(env, errno);
+
+    return;
+#endif
+    jniThrowIOException(env, ENOSYS);
+}
+
+static void bindListenNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+
+    jint type;
+    socklen_t addr_sz;
+    struct sockaddr *addr;
+    bdaddr_t bdaddr = *BDADDR_ANY;
+    struct asocket *s = get_socketData(env, obj);
+
+    if (!s)
+        return;
+
+    type = env->GetIntField(obj, field_mType);
+
+    switch (type) {
+    case TYPE_RFCOMM:
+        struct sockaddr_rc addr_rc;
+        addr = (struct sockaddr *)&addr_rc;
+        addr_sz = sizeof(addr_rc);
+
+        memset(addr, 0, addr_sz);
+        addr_rc.rc_family = AF_BLUETOOTH;
+        addr_rc.rc_channel = env->GetIntField(obj, field_mPort);
+        memcpy(&addr_rc.rc_bdaddr, &bdaddr, sizeof(bdaddr_t));
+        break;
+    case TYPE_SCO:
+        struct sockaddr_sco addr_sco;
+        addr = (struct sockaddr *)&addr_sco;
+        addr_sz = sizeof(addr_sco);
+
+        memset(addr, 0, addr_sz);
+        addr_sco.sco_family = AF_BLUETOOTH;
+        memcpy(&addr_sco.sco_bdaddr, &bdaddr, sizeof(bdaddr_t));
+        break;
+    case TYPE_L2CAP:
+        struct sockaddr_l2 addr_l2;
+        addr = (struct sockaddr *)&addr_l2;
+        addr_sz = sizeof(addr_l2);
+
+        memset(addr, 0, addr_sz);
+        addr_l2.l2_family = AF_BLUETOOTH;
+        addr_l2.l2_psm = env->GetIntField(obj, field_mPort);
+        memcpy(&addr_l2.l2_bdaddr, &bdaddr, sizeof(bdaddr_t));
+        break;
+    default:
+        jniThrowIOException(env, ENOSYS);
+        return;
+    }
+
+    if (bind(s->fd, addr, addr_sz)) {
+        jniThrowIOException(env, errno);
+        return;
+    }
+
+    if (listen(s->fd, 1)) {
+        jniThrowIOException(env, errno);
+        return;
+    }
+
+    return;
+#endif
+    jniThrowIOException(env, ENOSYS);
+}
+
+static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+
+    int fd;
+    jint type;
+    struct sockaddr *addr;
+    socklen_t addr_sz;
+    jstring addr_jstr;
+    char addr_cstr[BTADDR_SIZE];
+    bdaddr_t *bdaddr;
+    jboolean auth;
+    jboolean encrypt;
+
+    struct asocket *s = get_socketData(env, obj);
+
+    if (!s)
+        return NULL;
+
+    type = env->GetIntField(obj, field_mType);
+
+    switch (type) {
+    case TYPE_RFCOMM:
+        struct sockaddr_rc addr_rc;
+        addr = (struct sockaddr *)&addr_rc;
+        addr_sz = sizeof(addr_rc);
+        bdaddr = &addr_rc.rc_bdaddr;
+        memset(addr, 0, addr_sz);
+        break;
+    case TYPE_SCO:
+        struct sockaddr_sco addr_sco;
+        addr = (struct sockaddr *)&addr_sco;
+        addr_sz = sizeof(addr_sco);
+        bdaddr = &addr_sco.sco_bdaddr;
+        memset(addr, 0, addr_sz);
+        break;
+    case TYPE_L2CAP:
+        struct sockaddr_l2 addr_l2;
+        addr = (struct sockaddr *)&addr_l2;
+        addr_sz = sizeof(addr_l2);
+        bdaddr = &addr_l2.l2_bdaddr;
+        memset(addr, 0, addr_sz);
+        break;
+    default:
+        jniThrowIOException(env, ENOSYS);
+        return NULL;
+    }
+
+    fd = asocket_accept(s, addr, &addr_sz, timeout);
+
+    LOGV("...accept(%d, %s) = %d (errno %d)",
+            s->fd, TYPE_AS_STR(type), fd, errno);
+
+    if (fd < 0) {
+        jniThrowIOException(env, errno);
+        return NULL;
+    }
+
+    /* Connected - return new BluetoothSocket */
+    auth = env->GetBooleanField(obj, field_mAuth);
+    encrypt = env->GetBooleanField(obj, field_mEncrypt);
+
+    get_bdaddr_as_string(bdaddr, addr_cstr);
+
+    addr_jstr = env->NewStringUTF(addr_cstr);
+    return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor,
+            type, fd, auth, encrypt, addr_jstr, -1);
+
+#endif
+    jniThrowIOException(env, ENOSYS);
+    return NULL;
+}
+
+static jint availableNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+
+    int available;
+    struct asocket *s = get_socketData(env, obj);
+
+    if (!s)
+        return -1;
+
+    if (ioctl(s->fd, FIONREAD, &available) < 0) {
+        jniThrowIOException(env, errno);
+        return -1;
+    }
+
+    return available;
+
+#endif
+    jniThrowIOException(env, ENOSYS);
+    return -1;
+}
+
+/** jb must not be null. offset and offset+length must be within array */
+static jint readNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
+        jint length) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+
+    int ret;
+    jbyte *b;
+    struct asocket *s = get_socketData(env, obj);
+
+    if (!s)
+        return -1;
+
+    b = env->GetByteArrayElements(jb, NULL);
+    if (b == NULL) {
+        jniThrowIOException(env, EINVAL);
+        return -1;
+    }
+
+    ret = asocket_read(s, &b[offset], length, -1);
+    if (ret < 0) {
+        jniThrowIOException(env, errno);
+        env->ReleaseByteArrayElements(jb, b, JNI_ABORT);
+        return -1;
+    }
+
+    env->ReleaseByteArrayElements(jb, b, 0);
+    return (jint)ret;
+
+#endif
+    jniThrowIOException(env, ENOSYS);
+    return -1;
+}
+
+/** jb must not be null. offset and offset+length must be within array */
+static jint writeNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
+        jint length) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+
+    int ret;
+    jbyte *b;
+    struct asocket *s = get_socketData(env, obj);
+
+    if (!s)
+        return -1;
+
+    b = env->GetByteArrayElements(jb, NULL);
+    if (b == NULL) {
+        jniThrowIOException(env, EINVAL);
+        return -1;
+    }
+
+    ret = asocket_write(s, &b[offset], length, -1);
+    if (ret < 0) {
+        jniThrowIOException(env, errno);
+        env->ReleaseByteArrayElements(jb, b, JNI_ABORT);
+        return -1;
+    }
+
+    env->ReleaseByteArrayElements(jb, b, JNI_ABORT);  // no need to commit
+    return (jint)ret;
+
+#endif
+    jniThrowIOException(env, ENOSYS);
+    return -1;
+}
+
+static void closeNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+    struct asocket *s = get_socketData(env, obj);
+
+    if (!s)
+        return;
+
+    asocket_abort(s);
+
+    LOGV("...asocket_abort(%d) complete", s->fd);
+    return;
+#endif
+    jniThrowIOException(env, ENOSYS);
+}
+
+static void destroyNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+    struct asocket *s = get_socketData(env, obj);
+    int fd = s->fd;
+
+    if (!s)
+        return;
+
+    asocket_destroy(s);
+
+    LOGV("...asocket_destroy(%d) complete", fd);
+    return;
+#endif
+    jniThrowIOException(env, ENOSYS);
+}
+
+static JNINativeMethod sMethods[] = {
+    {"initSocketNative", "()V",  (void*) initSocketNative},
+    {"initSocketFromFdNative", "(I)V",  (void*) initSocketFromFdNative},
+    {"connectNative", "()V", (void *) connectNative},
+    {"bindListenNative", "()V", (void *) bindListenNative},
+    {"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative},
+    {"availableNative", "()I",    (void *) availableNative},
+    {"readNative", "([BII)I",    (void *) readNative},
+    {"writeNative", "([BII)I",    (void *) writeNative},
+    {"closeNative", "()V",    (void *) closeNative},
+    {"destroyNative", "()V",    (void *) destroyNative},
+};
+
+int register_android_bluetooth_BluetoothSocket(JNIEnv *env) {
+    jclass clazz = env->FindClass("android/bluetooth/BluetoothSocket");
+    if (clazz == NULL)
+        return -1;
+    class_BluetoothSocket = (jclass) env->NewGlobalRef(clazz);
+    field_mType = env->GetFieldID(clazz, "mType", "I");
+    field_mAddress = env->GetFieldID(clazz, "mAddress", "Ljava/lang/String;");
+    field_mPort = env->GetFieldID(clazz, "mPort", "I");
+    field_mAuth = env->GetFieldID(clazz, "mAuth", "Z");
+    field_mEncrypt = env->GetFieldID(clazz, "mEncrypt", "Z");
+    field_mSocketData = env->GetFieldID(clazz, "mSocketData", "I");
+    method_BluetoothSocket_ctor = env->GetMethodID(clazz, "<init>", "(IIZZLjava/lang/String;I)V");
+    return AndroidRuntime::registerNativeMethods(env,
+        "android/bluetooth/BluetoothSocket", sMethods, NELEM(sMethods));
+}
+
+} /* namespace android */
+
diff --git a/core/jni/android_bluetooth_Database.cpp b/core/jni/android_bluetooth_Database.cpp
deleted file mode 100644
index 73b8efd..0000000
--- a/core/jni/android_bluetooth_Database.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DBUS_CLASS_NAME BLUEZ_DBUS_BASE_IFC ".Database"
-#define LOG_TAG "bluetooth_Database.cpp"
-
-#include "android_bluetooth_common.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "JNIHelp.h"
-#include "jni.h"
-#include "utils/Log.h"
-
-#ifdef HAVE_BLUETOOTH
-#include <dbus/dbus.h>
-#endif
-
-namespace android {
-
-#ifdef HAVE_BLUETOOTH
-static DBusConnection* conn = NULL;   // Singleton thread-safe connection
-#endif
-
-static void classInitNative(JNIEnv* env, jclass clazz) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    conn = NULL;
-#endif
-}
-
-static void initializeNativeDataNative(JNIEnv* env, jobject object) {
-    LOGV(__FUNCTION__);
-
-#ifdef HAVE_BLUETOOTH
-    if (conn == NULL) {
-        DBusError err;
-        dbus_error_init(&err);
-        dbus_threads_init_default();
-        conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
-        if (dbus_error_is_set(&err)) {
-            LOGE("Could not get onto the system bus!");
-            dbus_error_free(&err);
-        }
-        dbus_connection_set_exit_on_disconnect(conn, FALSE);
-    }
-#endif
-}
-
-static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
-    LOGV(__FUNCTION__);
-}
-
-static jint addServiceRecordNative(JNIEnv *env, jobject object,
-                                   jbyteArray record) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    if (conn != NULL) {
-        jbyte* c_record = env->GetByteArrayElements(record, NULL);
-        DBusMessage *reply = dbus_func_args(env,
-                                            conn,
-                                            BLUEZ_DBUS_BASE_PATH,
-                                            DBUS_CLASS_NAME,
-                                            "AddServiceRecord",
-                                            DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
-                                            &c_record,
-                                            env->GetArrayLength(record),
-                                            DBUS_TYPE_INVALID);
-        env->ReleaseByteArrayElements(record, c_record, JNI_ABORT);
-        return reply ? dbus_returns_uint32(env, reply) : -1;
-    }
-#endif
-    return -1;
-}
-
-static jint addServiceRecordFromXmlNative(JNIEnv *env, jobject object,
-                                          jstring record) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    if (conn != NULL) {
-        const char *c_record = env->GetStringUTFChars(record, NULL);
-        DBusMessage *reply = dbus_func_args(env,
-                                            conn,
-                                            BLUEZ_DBUS_BASE_PATH,
-                                            DBUS_CLASS_NAME,
-                                            "AddServiceRecordFromXML",
-                                            DBUS_TYPE_STRING, &c_record,
-                                            DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(record, c_record);
-        return reply ? dbus_returns_uint32(env, reply) : -1;
-    }
-#endif
-    return -1;
-}
-
-static void updateServiceRecordNative(JNIEnv *env, jobject object,
-                                      jint handle,
-                                      jbyteArray record) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    if (conn != NULL) {
-        jbyte* c_record = env->GetByteArrayElements(record, NULL);
-        DBusMessage *reply = dbus_func_args(env,
-                                            conn,
-                                            BLUEZ_DBUS_BASE_PATH,
-                                            DBUS_CLASS_NAME,
-                                            "UpdateServiceRecord",
-                                            DBUS_TYPE_UINT32, &handle,
-                                            DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
-                                            &c_record,
-                                            env->GetArrayLength(record),
-                                            DBUS_TYPE_INVALID);
-        env->ReleaseByteArrayElements(record, c_record, JNI_ABORT);
-    }
-#endif
-}
-
-static void updateServiceRecordFromXmlNative(JNIEnv *env, jobject object,
-                                             jint handle,
-                                             jstring record) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    if (conn != NULL) {
-        const char *c_record = env->GetStringUTFChars(record, NULL);
-        DBusMessage *reply = dbus_func_args(env,
-                                            conn,
-                                            BLUEZ_DBUS_BASE_PATH,
-                                            DBUS_CLASS_NAME,
-                                            "UpdateServiceRecordFromXML",
-                                            DBUS_TYPE_UINT32, &handle,
-                                            DBUS_TYPE_STRING, &c_record,
-                                            DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(record, c_record);
-    }
-#endif
-}
-
-/* private static native void removeServiceRecordNative(int handle); */
-static void removeServiceRecordNative(JNIEnv *env, jobject object,
-                                      jint handle) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    if (conn != NULL) {
-        DBusMessage *reply = dbus_func_args(env,
-                                            conn,
-                                            BLUEZ_DBUS_BASE_PATH,
-                                            DBUS_CLASS_NAME,
-                                            "RemoveServiceRecord",
-                                            DBUS_TYPE_UINT32, &handle,
-                                            DBUS_TYPE_INVALID);
-    }
-#endif
-}
-
-
-static JNINativeMethod sMethods[] = {
-     /* name, signature, funcPtr */
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
-    {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
-    {"addServiceRecordNative", "([B)I", (void*)addServiceRecordNative},
-    {"addServiceRecordFromXmlNative", "(Ljava/lang/String;)I", (void*)addServiceRecordFromXmlNative},
-    {"updateServiceRecordNative", "(I[B)V", (void*)updateServiceRecordNative},
-    {"updateServiceRecordFromXmlNative", "(ILjava/lang/String;)V", (void*)updateServiceRecordFromXmlNative},
-    {"removeServiceRecordNative", "(I)V", (void*)removeServiceRecordNative},
-};
-
-int register_android_bluetooth_Database(JNIEnv *env) {
-    return AndroidRuntime::registerNativeMethods(env,
-            "android/bluetooth/Database", sMethods, NELEM(sMethods));
-}
-
-} /* namespace android */
diff --git a/core/jni/android_bluetooth_RfcommSocket.cpp b/core/jni/android_bluetooth_RfcommSocket.cpp
deleted file mode 100644
index 3ed35d9..0000000
--- a/core/jni/android_bluetooth_RfcommSocket.cpp
+++ /dev/null
@@ -1,621 +0,0 @@
-/*
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "bluetooth_RfcommSocket.cpp"
-
-#include "android_bluetooth_common.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "JNIHelp.h"
-#include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <sys/poll.h>
-
-#ifdef HAVE_BLUETOOTH
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/sco.h>
-#endif
-
-namespace android {
-
-#ifdef HAVE_BLUETOOTH
-static jfieldID field_mNativeData;
-static jfieldID field_mTimeoutRemainingMs;
-static jfieldID field_mAcceptTimeoutRemainingMs;
-static jfieldID field_mAddress;
-static jfieldID field_mPort;
-
-typedef struct {
-    jstring address;
-    const char *c_address;
-    int rfcomm_channel;
-    int last_read_err;
-    int rfcomm_sock;
-    // < 0 -- in progress, 
-    //   0 -- not connected
-    // > 0 connected
-    //     1 input is open
-    //     2 output is open
-    //     3 both input and output are open
-    int rfcomm_connected; 
-    int rfcomm_sock_flags;
-} native_data_t;
-
-static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
-    return (native_data_t *)(env->GetIntField(object, field_mNativeData));
-}
-
-static inline void init_socket_info(
-    JNIEnv *env, jobject object,
-    native_data_t *nat,
-    jstring address,
-    jint rfcomm_channel) {
-    nat->address = (jstring)env->NewGlobalRef(address);
-    nat->c_address = env->GetStringUTFChars(nat->address, NULL);
-    nat->rfcomm_channel = (int)rfcomm_channel;
-}
-
-static inline void cleanup_socket_info(JNIEnv *env, native_data_t *nat) {
-    if (nat->c_address != NULL) {
-        env->ReleaseStringUTFChars(nat->address, nat->c_address);
-        env->DeleteGlobalRef(nat->address);
-        nat->c_address = NULL;
-    }
-}
-#endif
-
-static void classInitNative(JNIEnv* env, jclass clazz) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    field_mNativeData = get_field(env, clazz, "mNativeData", "I");
-    field_mTimeoutRemainingMs = get_field(env, clazz, "mTimeoutRemainingMs", "I");
-    field_mAcceptTimeoutRemainingMs = get_field(env, clazz, "mAcceptTimeoutRemainingMs", "I");
-    field_mAddress = get_field(env, clazz, "mAddress", "Ljava/lang/String;");
-    field_mPort = get_field(env, clazz, "mPort", "I");
-#endif
-}
-
-static void initializeNativeDataNative(JNIEnv* env, jobject object) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-
-    native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
-    if (nat == NULL) {
-        LOGE("%s: out of memory!", __FUNCTION__);
-        return;
-    }
-
-    env->SetIntField(object, field_mNativeData, (jint)nat);
-    nat->rfcomm_sock = -1;
-    nat->rfcomm_connected = 0;
-#endif
-}
-
-static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        free(nat);
-    }
-#endif
-}
-
-static jobject createNative(JNIEnv *env, jobject obj) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    int lm;
-    native_data_t *nat = get_native_data(env, obj);
-    nat->rfcomm_sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-
-    if (nat->rfcomm_sock < 0) {
-        LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__,
-             strerror(errno));
-        return NULL;
-    }
-        
-    lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT;
-
-    if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm,
-                sizeof(lm)) < 0) {
-        LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__);
-        close(nat->rfcomm_sock);
-        return NULL;
-    }
-
-    return jniCreateFileDescriptor(env, nat->rfcomm_sock);
-#else
-    return NULL;
-#endif
-}
-
-static void destroyNative(JNIEnv *env, jobject obj) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, obj);
-    cleanup_socket_info(env, nat);
-    if (nat->rfcomm_sock >= 0) {
-        close(nat->rfcomm_sock);
-        nat->rfcomm_sock = -1;
-    }
-#endif
-}
-
-
-static jboolean connectNative(JNIEnv *env, jobject obj,
-                              jstring address, jint port) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, obj);
-
-    if (nat->rfcomm_sock >= 0) {
-        if (nat->rfcomm_connected) {
-            LOGI("RFCOMM socket: %s.",
-                 (nat->rfcomm_connected > 0) ? "already connected" : "connection is in progress");
-            return JNI_TRUE;
-        }
-
-        init_socket_info(env, obj, nat, address, port);
-
-        struct sockaddr_rc addr;
-        memset(&addr, 0, sizeof(struct sockaddr_rc));
-        get_bdaddr(nat->c_address, &addr.rc_bdaddr);
-        addr.rc_channel = nat->rfcomm_channel;
-        addr.rc_family = AF_BLUETOOTH;
-        nat->rfcomm_connected = 0;
-
-        while (nat->rfcomm_connected == 0) {
-            if (connect(nat->rfcomm_sock, (struct sockaddr *)&addr,
-                    sizeof(addr)) < 0) {
-                if (errno == EINTR) continue;
-                LOGE("connect error: %s (%d)\n", strerror(errno), errno);
-                break;
-            } else {
-                nat->rfcomm_connected = 3; // input and output
-            }
-        }
-    } else {
-        LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__);
-    }
-
-    if (nat->rfcomm_connected > 0) {
-        env->SetIntField(obj, field_mPort, port);
-        return JNI_TRUE;
-    }
-#endif
-    return JNI_FALSE;
-}
-
-static jboolean connectAsyncNative(JNIEnv *env, jobject obj,
-                                   jstring address, jint port) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, obj);
-
-    if (nat->rfcomm_sock < 0) {
-        LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__);
-        return JNI_FALSE;
-    }
-
-    if (nat->rfcomm_connected) {
-        LOGI("RFCOMM socket: %s.",
-             (nat->rfcomm_connected > 0) ?
-             "already connected" : "connection is in progress");
-        return JNI_TRUE;
-    }
-
-    init_socket_info(env, obj, nat, address, port);
-
-    struct sockaddr_rc addr;
-    memset(&addr, 0, sizeof(struct sockaddr_rc));
-    get_bdaddr(nat->c_address, &addr.rc_bdaddr);
-    addr.rc_channel = nat->rfcomm_channel;
-    addr.rc_family = AF_BLUETOOTH;
-
-    nat->rfcomm_sock_flags = fcntl(nat->rfcomm_sock, F_GETFL, 0);
-    if (fcntl(nat->rfcomm_sock,
-              F_SETFL, nat->rfcomm_sock_flags | O_NONBLOCK) >= 0) {
-        int rc;
-        nat->rfcomm_connected = 0;
-        errno = 0;
-        rc = connect(nat->rfcomm_sock,
-                     (struct sockaddr *)&addr,
-                     sizeof(addr));
-
-        if (rc >= 0) {
-            nat->rfcomm_connected = 3;
-            LOGI("RFCOMM async connect immediately successful");
-            env->SetIntField(obj, field_mPort, port);
-            return JNI_TRUE;
-        }
-        else if (rc < 0) {
-            if (errno == EINPROGRESS || errno == EAGAIN)
-                {
-                    LOGI("RFCOMM async connect is in progress (%s)",
-                         strerror(errno));
-                    nat->rfcomm_connected = -1;
-                    env->SetIntField(obj, field_mPort, port);
-                    return JNI_TRUE;
-                }
-            else
-                {
-                    LOGE("RFCOMM async connect error (%d): %s (%d)",
-                         nat->rfcomm_sock, strerror(errno), errno);
-                    return JNI_FALSE;
-                }
-        }
-    } // fcntl(nat->rfcomm_sock ...)
-#endif
-    return JNI_FALSE;
-}
-
-static jboolean interruptAsyncConnectNative(JNIEnv *env, jobject obj) {
-    //WRITEME
-    return JNI_TRUE;
-}
-
-static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj,
-                                      jint timeout_ms) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    struct sockaddr_rc addr;
-    native_data_t *nat = get_native_data(env, obj);
-
-    env->SetIntField(obj, field_mTimeoutRemainingMs, timeout_ms);
-
-    if (nat->rfcomm_sock < 0) {
-        LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__);
-        return -1;
-    }
-
-    if (nat->rfcomm_connected > 0) {
-        LOGI("%s: RFCOMM is already connected!", __FUNCTION__);
-        return 1;
-    }
-
-    /* Do an asynchronous select() */
-    int n;
-    fd_set rset, wset;
-    struct timeval to;
-
-    FD_ZERO(&rset);
-    FD_ZERO(&wset);
-    FD_SET(nat->rfcomm_sock, &rset);
-    FD_SET(nat->rfcomm_sock, &wset);
-    if (timeout_ms >= 0) {
-        to.tv_sec = timeout_ms / 1000;
-        to.tv_usec = 1000 * (timeout_ms % 1000);
-    }
-    n = select(nat->rfcomm_sock + 1,
-               &rset,
-               &wset,
-               NULL,
-               (timeout_ms < 0 ? NULL : &to));
-
-    if (timeout_ms > 0) {
-        jint remaining = to.tv_sec*1000 + to.tv_usec/1000;
-        LOGI("Remaining time %ldms", (long)remaining);
-        env->SetIntField(obj, field_mTimeoutRemainingMs,
-                         remaining);
-    }
-
-    if (n <= 0) {
-        if (n < 0)  {
-            LOGE("select() on RFCOMM socket: %s (%d)",
-                 strerror(errno),
-                 errno);
-            return -1;
-        }
-        return 0;
-    }
-    /* n must be equal to 1 and either rset or wset must have the
-       file descriptor set. */
-    LOGI("select() returned %d.", n);
-    if (FD_ISSET(nat->rfcomm_sock, &rset) ||
-        FD_ISSET(nat->rfcomm_sock, &wset)) {
-        /* A trial async read() will tell us if everything is OK. */
-        char ch;
-        errno = 0;
-        int nr = read(nat->rfcomm_sock, &ch, 1);
-        /* It should be that nr != 1 because we just opened a socket
-           and we haven't sent anything over it for the other side to
-           respond... but one can't be paranoid enough.
-        */
-        if (nr >= 0 || errno != EAGAIN) {
-            LOGE("RFCOMM async connect() error: %s (%d), nr = %d\n",
-                 strerror(errno),
-                 errno,
-                 nr);
-            /* Clear the rfcomm_connected flag to cause this function
-               to re-create the socket and re-attempt the connect()
-               the next time it is called.
-            */
-            nat->rfcomm_connected = 0;
-            /* Restore the blocking properties of the socket. */
-            fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags);
-            return -1;
-        }
-        /* Restore the blocking properties of the socket. */
-        fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags);
-        LOGI("Successful RFCOMM socket connect.");
-        nat->rfcomm_connected = 3; // input and output
-        return 1;
-    }
-#endif
-    return -1;
-}
-
-static jboolean shutdownNative(JNIEnv *env, jobject obj,
-                jboolean shutdownInput) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    /* NOTE: If you change the bcode to modify nat, make sure you 
-       add synchronize(this) to the method calling this native
-       method. 
-    */
-    native_data_t *nat = get_native_data(env, obj);
-    if (nat->rfcomm_sock < 0) {
-        LOGE("socket(RFCOMM) error: socket not created");
-        return JNI_FALSE;
-    }
-    int rc = shutdown(nat->rfcomm_sock, 
-            shutdownInput ? SHUT_RD : SHUT_WR);
-    if (!rc) {
-        nat->rfcomm_connected &= 
-            shutdownInput ? ~1 : ~2;
-        return JNI_TRUE;
-    }
-#endif
-    return JNI_FALSE;
-}
-
-static jint isConnectedNative(JNIEnv *env, jobject obj) {
-    LOGI(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    const native_data_t *nat = get_native_data(env, obj);
-    return nat->rfcomm_connected;
-#endif
-    return 0;
-}
-
-//@@@@@@@@@ bind to device???
-static jboolean bindNative(JNIEnv *env, jobject obj, jstring device,
-                           jint port) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-
-    /* NOTE: If you change the code to modify nat, make sure you
-       add synchronize(this) to the method calling this native
-       method.
-    */
-    const native_data_t *nat = get_native_data(env, obj);
-    if (nat->rfcomm_sock < 0) {
-        LOGE("socket(RFCOMM) error: socket not created");
-        return JNI_FALSE;
-    }
-
-    struct sockaddr_rc laddr;
-    int lm;
-
-    lm = 0;
-/*
-    lm |= RFCOMM_LM_MASTER;
-    lm |= RFCOMM_LM_AUTH;
-    lm |= RFCOMM_LM_ENCRYPT;
-    lm |= RFCOMM_LM_SECURE;
-*/
-
-    if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
-        LOGE("Can't set RFCOMM link mode");
-        return JNI_FALSE;
-    }
-
-    laddr.rc_family = AF_BLUETOOTH;
-    bacpy(&laddr.rc_bdaddr, BDADDR_ANY);
-    laddr.rc_channel = port;
-
-    if (bind(nat->rfcomm_sock, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {
-        LOGE("Can't bind RFCOMM socket");
-        return JNI_FALSE;
-    }
-
-    env->SetIntField(obj, field_mPort, port);
-
-    return JNI_TRUE;
-#endif
-    return JNI_FALSE;
-}
-
-static jboolean listenNative(JNIEnv *env, jobject obj, jint backlog) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    /* NOTE: If you change the code to modify nat, make sure you
-       add synchronize(this) to the method calling this native
-       method.
-    */
-    const native_data_t *nat = get_native_data(env, obj);
-    if (nat->rfcomm_sock < 0) {
-        LOGE("socket(RFCOMM) error: socket not created");
-        return JNI_FALSE;
-    }
-    return listen(nat->rfcomm_sock, backlog) < 0 ? JNI_FALSE : JNI_TRUE;
-#else
-    return JNI_FALSE;
-#endif
-}
-
-static int set_nb(int sk, bool nb) {
-    int flags = fcntl(sk, F_GETFL);
-    if (flags < 0) {
-        LOGE("Can't get socket flags with fcntl(): %s (%d)",
-             strerror(errno), errno);
-        close(sk);
-        return -1;
-    }
-    flags &= ~O_NONBLOCK;
-    if (nb) flags |= O_NONBLOCK;
-    int status = fcntl(sk, F_SETFL, flags);
-    if (status < 0) {
-        LOGE("Can't set socket to nonblocking mode with fcntl(): %s (%d)",
-             strerror(errno), errno);
-        close(sk);
-        return -1;
-    }
-    return 0;
-}
-
-// Note: the code should check at a higher level to see whether
-// listen() has been called.
-#ifdef HAVE_BLUETOOTH
-static int do_accept(JNIEnv* env, jobject object, int sock,
-                     jobject newsock,
-                     jfieldID out_address,
-                     bool must_succeed) {
-
-    if (must_succeed && set_nb(sock, true) < 0)
-        return -1;
-
-    struct sockaddr_rc raddr;
-    int alen = sizeof(raddr);
-    int nsk = accept(sock, (struct sockaddr *) &raddr, &alen);
-    if (nsk < 0) {
-        LOGE("Error on accept from socket fd %d: %s (%d).",
-             sock,
-             strerror(errno),
-             errno);
-        if (must_succeed) set_nb(sock, false);
-        return -1;
-    }
-
-    char addr[BTADDR_SIZE];
-    get_bdaddr_as_string(&raddr.rc_bdaddr, addr);
-    env->SetObjectField(newsock, out_address, env->NewStringUTF(addr));
-
-    LOGI("Successful accept() on AG socket %d: new socket %d, address %s, RFCOMM channel %d",
-         sock,
-         nsk,
-         addr,
-         raddr.rc_channel);
-    if (must_succeed) set_nb(sock, false);
-    return nsk;
-}
-#endif /*HAVE_BLUETOOTH*/
-
-static jobject acceptNative(JNIEnv *env, jobject obj,
-                            jobject newsock, jint timeoutMs) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, obj);
-    if (nat->rfcomm_sock < 0) {
-        LOGE("socket(RFCOMM) error: socket not created");
-        return JNI_FALSE;
-    }
-
-    if (newsock == NULL) {
-        LOGE("%s: newsock = NULL\n", __FUNCTION__);
-        return JNI_FALSE;
-    }
-
-    int nsk = -1;
-    if (timeoutMs < 0) {
-        /* block until accept() succeeds */
-        nsk = do_accept(env, obj, nat->rfcomm_sock,
-                        newsock, field_mAddress, false);
-        if (nsk < 0) {
-            return NULL;
-        }
-    }
-    else {
-        /* wait with a timeout */
-
-        struct pollfd fds;
-        fds.fd = nat->rfcomm_sock;
-        fds.events = POLLIN | POLLPRI | POLLOUT | POLLERR;
-
-        env->SetIntField(obj, field_mAcceptTimeoutRemainingMs, 0);
-        int n = poll(&fds, 1, timeoutMs);
-        if (n <= 0) {
-            if (n < 0)  {
-                LOGE("listening poll() on RFCOMM socket: %s (%d)",
-                     strerror(errno),
-                     errno);
-                env->SetIntField(obj, field_mAcceptTimeoutRemainingMs, timeoutMs);
-            }
-            else {
-                LOGI("listening poll() on RFCOMM socket timed out");
-            }
-            return NULL;
-        }
-
-        LOGI("listening poll() on RFCOMM socket returned %d", n);
-        if (fds.fd == nat->rfcomm_sock) {
-            if (fds.revents & (POLLIN | POLLPRI | POLLOUT)) {
-                LOGI("Accepting connection.\n");
-                nsk = do_accept(env, obj, nat->rfcomm_sock,
-                                newsock, field_mAddress, true);
-                if (nsk < 0) {
-                    return NULL;
-                }
-            }
-        }
-    }
-
-    LOGI("Connection accepted, new socket fd = %d.", nsk);
-    native_data_t *newnat = get_native_data(env, newsock);
-    newnat->rfcomm_sock = nsk;
-    newnat->rfcomm_connected = 3;
-    return jniCreateFileDescriptor(env, nsk);
-#else
-    return NULL;
-#endif
-}
-
-static JNINativeMethod sMethods[] = {
-     /* name, signature, funcPtr */
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
-    {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
-
-    {"createNative", "()Ljava/io/FileDescriptor;", (void *)createNative},
-    {"destroyNative", "()V", (void *)destroyNative},
-    {"connectNative", "(Ljava/lang/String;I)Z", (void *)connectNative},
-    {"connectAsyncNative", "(Ljava/lang/String;I)Z", (void *)connectAsyncNative},
-    {"interruptAsyncConnectNative", "()Z", (void *)interruptAsyncConnectNative},
-    {"waitForAsyncConnectNative", "(I)I", (void *)waitForAsyncConnectNative},
-    {"shutdownNative", "(Z)Z", (void *)shutdownNative},
-    {"isConnectedNative", "()I", (void *)isConnectedNative},
-
-    {"bindNative", "(Ljava/lang/String;I)Z", (void*)bindNative},
-    {"listenNative", "(I)Z", (void*)listenNative},
-    {"acceptNative", "(Landroid/bluetooth/RfcommSocket;I)Ljava/io/FileDescriptor;", (void*)acceptNative},
-};
-
-int register_android_bluetooth_RfcommSocket(JNIEnv *env) {
-    return AndroidRuntime::registerNativeMethods(env,
-        "android/bluetooth/RfcommSocket", sMethods, NELEM(sMethods));
-}
-
-} /* namespace android */
diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp
index 0b8a604..343fa53 100644
--- a/core/jni/android_bluetooth_common.cpp
+++ b/core/jni/android_bluetooth_common.cpp
@@ -36,6 +36,43 @@
 namespace android {
 
 #ifdef HAVE_BLUETOOTH
+
+static Properties remote_device_properties[] = {
+    {"Address",  DBUS_TYPE_STRING},
+    {"Name", DBUS_TYPE_STRING},
+    {"Icon", DBUS_TYPE_STRING},
+    {"Class", DBUS_TYPE_UINT32},
+    {"UUIDs", DBUS_TYPE_ARRAY},
+    {"Paired", DBUS_TYPE_BOOLEAN},
+    {"Connected", DBUS_TYPE_BOOLEAN},
+    {"Trusted", DBUS_TYPE_BOOLEAN},
+    {"Alias", DBUS_TYPE_STRING},
+    {"Nodes", DBUS_TYPE_ARRAY},
+    {"Adapter", DBUS_TYPE_OBJECT_PATH},
+    {"LegacyPairing", DBUS_TYPE_BOOLEAN},
+    {"RSSI", DBUS_TYPE_INT16},
+    {"TX", DBUS_TYPE_UINT32}
+};
+
+static Properties adapter_properties[] = {
+    {"Address", DBUS_TYPE_STRING},
+    {"Name", DBUS_TYPE_STRING},
+    {"Class", DBUS_TYPE_UINT32},
+    {"Powered", DBUS_TYPE_BOOLEAN},
+    {"Discoverable", DBUS_TYPE_BOOLEAN},
+    {"DiscoverableTimeout", DBUS_TYPE_UINT32},
+    {"Pairable", DBUS_TYPE_BOOLEAN},
+    {"PairableTimeout", DBUS_TYPE_UINT32},
+    {"Discovering", DBUS_TYPE_BOOLEAN},
+    {"Devices", DBUS_TYPE_ARRAY},
+};
+
+typedef union {
+    char *str_val;
+    int int_val;
+    char **array_val;
+} property_value;
+
 jfieldID get_field(JNIEnv *env, jclass clazz, const char *member,
                    const char *mtype) {
     jfieldID field = env->GetFieldID(clazz, member, mtype);
@@ -332,6 +369,44 @@
     return ret;
 }
 
+static void set_object_array_element(JNIEnv *env, jobjectArray strArray,
+                                     const char *value, int index) {
+    jstring obj;
+    obj = env->NewStringUTF(value);
+    env->SetObjectArrayElement(strArray, index, obj);
+    env->DeleteLocalRef(obj);
+}
+
+jobjectArray dbus_returns_array_of_object_path(JNIEnv *env,
+                                               DBusMessage *reply) {
+
+    DBusError err;
+    char **list;
+    int i, len;
+    jobjectArray strArray = NULL;
+
+    dbus_error_init(&err);
+    if (dbus_message_get_args (reply,
+                               &err,
+                               DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
+                               &list, &len,
+                               DBUS_TYPE_INVALID)) {
+        jclass stringClass;
+        jstring classNameStr;
+
+        stringClass = env->FindClass("java/lang/String");
+        strArray = env->NewObjectArray(len, stringClass, NULL);
+
+        for (i = 0; i < len; i++)
+            set_object_array_element(env, strArray, list[i], i);
+    } else {
+        LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
+    }
+
+    dbus_message_unref(reply);
+    return strArray;
+}
+
 jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply) {
 
     DBusError err;
@@ -353,11 +428,8 @@
         stringClass = env->FindClass("java/lang/String");
         strArray = env->NewObjectArray(len, stringClass, NULL);
 
-        for (i = 0; i < len; i++) {
-            //LOGV("%s:    array[%d] = [%s]", __FUNCTION__, i, list[i]);
-            env->SetObjectArrayElement(strArray, i,
-                                       env->NewStringUTF(list[i]));
-        }
+        for (i = 0; i < len; i++)
+            set_object_array_element(env, strArray, list[i], i);
     } else {
         LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
     }
@@ -390,17 +462,263 @@
     return byteArray;
 }
 
-void get_bdaddr(const char *str, bdaddr_t *ba) {
+void append_variant(DBusMessageIter *iter, int type, void *val)
+{
+    DBusMessageIter value_iter;
+    char var_type[2] = { type, '\0'};
+    dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, var_type, &value_iter);
+    dbus_message_iter_append_basic(&value_iter, type, val);
+    dbus_message_iter_close_container(iter, &value_iter);
+}
+
+int get_property(DBusMessageIter iter, Properties *properties,
+                  int max_num_properties, int *prop_index, property_value *value, int *len) {
+    DBusMessageIter prop_val, array_val_iter;
+    char *property = NULL;
+    uint32_t array_type;
+    char *str_val;
+    int i, j, type, int_val;
+
+    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+        return -1;
+    dbus_message_iter_get_basic(&iter, &property);
+    if (!dbus_message_iter_next(&iter))
+        return -1;
+    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+        return -1;
+    for (i = 0; i <  max_num_properties; i++) {
+        if (!strncmp(property, properties[i].name, strlen(property)))
+            break;
+    }
+    *prop_index = i;
+    if (i == max_num_properties)
+        return -1;
+
+    dbus_message_iter_recurse(&iter, &prop_val);
+    type = properties[*prop_index].type;
+    if (dbus_message_iter_get_arg_type(&prop_val) != type) {
+        LOGE("Property type mismatch in get_property: %d, expected:%d, index:%d",
+             dbus_message_iter_get_arg_type(&prop_val), type, *prop_index);
+        return -1;
+    }
+
+    switch(type) {
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+        dbus_message_iter_get_basic(&prop_val, &value->str_val);
+        *len = 1;
+        break;
+    case DBUS_TYPE_UINT32:
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_BOOLEAN:
+        dbus_message_iter_get_basic(&prop_val, &int_val);
+        value->int_val = int_val;
+        *len = 1;
+        break;
+    case DBUS_TYPE_ARRAY:
+        dbus_message_iter_recurse(&prop_val, &array_val_iter);
+        array_type = dbus_message_iter_get_arg_type(&array_val_iter);
+        *len = 0;
+        value->array_val = NULL;
+        if (array_type == DBUS_TYPE_OBJECT_PATH ||
+            array_type == DBUS_TYPE_STRING){
+            j = 0;
+            do {
+               j ++;
+            } while(dbus_message_iter_next(&array_val_iter));
+            dbus_message_iter_recurse(&prop_val, &array_val_iter);
+            // Allocate  an array of char *
+            *len = j;
+            char **tmp = (char **)malloc(sizeof(char *) * *len);
+            if (!tmp)
+                return -1;
+            j = 0;
+            do {
+               dbus_message_iter_get_basic(&array_val_iter, &tmp[j]);
+               j ++;
+            } while(dbus_message_iter_next(&array_val_iter));
+            value->array_val = tmp;
+        }
+        break;
+    default:
+        return -1;
+    }
+    return 0;
+}
+
+void create_prop_array(JNIEnv *env, jobjectArray strArray, Properties *property,
+                       property_value *value, int len, int *array_index ) {
+    char **prop_val = NULL;
+    char buf[32] = {'\0'}, buf1[32] = {'\0'};
+    int i;
+
+    char *name = property->name;
+    int prop_type = property->type;
+
+    set_object_array_element(env, strArray, name, *array_index);
+    *array_index += 1;
+
+    if (prop_type == DBUS_TYPE_UINT32 || prop_type == DBUS_TYPE_INT16) {
+        sprintf(buf, "%d", value->int_val);
+        set_object_array_element(env, strArray, buf, *array_index);
+        *array_index += 1;
+    } else if (prop_type == DBUS_TYPE_BOOLEAN) {
+        sprintf(buf, "%s", value->int_val ? "true" : "false");
+
+        set_object_array_element(env, strArray, buf, *array_index);
+        *array_index += 1;
+    } else if (prop_type == DBUS_TYPE_ARRAY) {
+        // Write the length first
+        sprintf(buf1, "%d", len);
+        set_object_array_element(env, strArray, buf1, *array_index);
+        *array_index += 1;
+
+        prop_val = value->array_val;
+        for (i = 0; i < len; i++) {
+            set_object_array_element(env, strArray, prop_val[i], *array_index);
+            *array_index += 1;
+        }
+    } else {
+        set_object_array_element(env, strArray, (const char *) value->str_val, *array_index);
+        *array_index += 1;
+    }
+}
+
+jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties,
+                              const int max_num_properties) {
+    DBusMessageIter dict_entry, dict;
+    jobjectArray strArray = NULL;
+    property_value value;
+    int i, size = 0,array_index = 0;
+    int len = 0, prop_type = DBUS_TYPE_INVALID, prop_index = -1, type;
+    struct {
+        property_value value;
+        int len;
+        bool used;
+    } values[max_num_properties];
+    int t, j;
+
+    jclass stringClass = env->FindClass("java/lang/String");
+    DBusError err;
+    dbus_error_init(&err);
+
+    for (i = 0; i < max_num_properties; i++) {
+        values[i].used = false;
+    }
+
+    if(dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+        goto failure;
+    dbus_message_iter_recurse(iter, &dict);
+    do {
+        len = 0;
+        if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_DICT_ENTRY)
+            goto failure;
+        dbus_message_iter_recurse(&dict, &dict_entry);
+
+        if (!get_property(dict_entry, properties, max_num_properties, &prop_index,
+                          &value, &len)) {
+            size += 2;
+            if (properties[prop_index].type == DBUS_TYPE_ARRAY)
+                size += len;
+            values[prop_index].value = value;
+            values[prop_index].len = len;
+            values[prop_index].used = true;
+        } else {
+            goto failure;
+        }
+    } while(dbus_message_iter_next(&dict));
+
+    strArray = env->NewObjectArray(size, stringClass, NULL);
+
+    for (i = 0; i < max_num_properties; i++) {
+        if (values[i].used) {
+            create_prop_array(env, strArray, &properties[i], &values[i].value, values[i].len,
+                              &array_index);
+
+            if (properties[i].type == DBUS_TYPE_ARRAY && values[i].used
+                   && values[i].value.array_val != NULL)
+                free(values[i].value.array_val);
+        }
+
+    }
+    return strArray;
+
+failure:
+    if (dbus_error_is_set(&err))
+        LOG_AND_FREE_DBUS_ERROR(&err);
+    for (i = 0; i < max_num_properties; i++)
+        if (properties[i].type == DBUS_TYPE_ARRAY && values[i].used == true
+                                        && values[i].value.array_val != NULL)
+            free(values[i].value.array_val);
+    return NULL;
+}
+
+jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg,
+                           Properties *properties, int max_num_properties) {
+    DBusMessageIter iter;
+    DBusError err;
+    jobjectArray strArray = NULL;
+    jclass stringClass= env->FindClass("java/lang/String");
+    int len = 0, prop_index = -1;
+    int array_index = 0, size = 0;
+    property_value value;
+
+    dbus_error_init(&err);
+    if (!dbus_message_iter_init(msg, &iter))
+        goto failure;
+
+    if (!get_property(iter, properties, max_num_properties,
+                      &prop_index, &value, &len)) {
+        size += 2;
+        if (properties[prop_index].type == DBUS_TYPE_ARRAY)
+            size += len;
+        strArray = env->NewObjectArray(size, stringClass, NULL);
+
+        create_prop_array(env, strArray, &properties[prop_index],
+                          &value, len, &array_index);
+
+        if (properties[prop_index].type == DBUS_TYPE_ARRAY && value.array_val != NULL)
+             free(value.array_val);
+
+        return strArray;
+    }
+failure:
+    LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+    return NULL;
+}
+
+jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg) {
+    return parse_property_change(env, msg, (Properties *) &adapter_properties,
+                    sizeof(adapter_properties) / sizeof(Properties));
+}
+
+jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg) {
+    return parse_property_change(env, msg, (Properties *) &remote_device_properties,
+                    sizeof(remote_device_properties) / sizeof(Properties));
+}
+
+jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter) {
+    return parse_properties(env, iter, (Properties *) &adapter_properties,
+                            sizeof(adapter_properties) / sizeof(Properties));
+}
+
+jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter) {
+    return parse_properties(env, iter, (Properties *) &remote_device_properties,
+                          sizeof(remote_device_properties) / sizeof(Properties));
+}
+
+int get_bdaddr(const char *str, bdaddr_t *ba) {
     char *d = ((char *)ba) + 5, *endp;
     int i;
     for(i = 0; i < 6; i++) {
         *d-- = strtol(str, &endp, 16);
         if (*endp != ':' && i != 5) {
             memset(ba, 0, sizeof(bdaddr_t));
-            return;
+            return -1;
         }
         str = endp + 1;
     }
+    return 0;
 }
 
 void get_bdaddr_as_string(const bdaddr_t *ba, char *str) {
diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h
index 69092dd..ef9b66b 100644
--- a/core/jni/android_bluetooth_common.h
+++ b/core/jni/android_bluetooth_common.h
@@ -68,6 +68,7 @@
 
 struct event_loop_native_data_t {
     DBusConnection *conn;
+    const char *adapter;
 
     /* protects the thread */
     pthread_mutex_t thread_mutex;
@@ -89,6 +90,12 @@
     jobject me;
 };
 
+struct _Properties {
+    char name[32];
+    int type;
+};
+typedef struct _Properties Properties;
+
 dbus_bool_t dbus_func_args_async(JNIEnv *env,
                                  DBusConnection *conn,
                                  int timeout_ms,
@@ -142,9 +149,19 @@
 jstring dbus_returns_string(JNIEnv *env, DBusMessage *reply);
 jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply);
 jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply);
+jobjectArray dbus_returns_array_of_object_path(JNIEnv *env, DBusMessage *reply);
 jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply);
 
-void get_bdaddr(const char *str, bdaddr_t *ba);
+jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties,
+                              const int max_num_properties);
+jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg,
+                                   Properties *properties, int max_num_properties);
+jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter);
+jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter);
+jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg);
+jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg);
+void append_variant(DBusMessageIter *iter, int type, void *val);
+int get_bdaddr(const char *str, bdaddr_t *ba);
 void get_bdaddr_as_string(const bdaddr_t *ba, char *str);
 
 bool debug_no_encrypt();
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index f19fbbf..91449bc 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -229,7 +229,7 @@
 {
     int32_t err;
     CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window);
+LOG_WINDOW("Checking if column is a blob or null for %d,%d from %p", row, column, window);
 
     field_slot_t field;
     err = window->read_field_slot(row, column, &field);
@@ -241,6 +241,54 @@
     return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL;
 }
 
+static jboolean isString_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+    int32_t err;
+    CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is a string or null for %d,%d from %p", row, column, window);
+
+    field_slot_t field;
+    err = window->read_field_slot(row, column, &field);
+    if (err != 0) {
+        throwExceptionWithRowCol(env, row, column);
+        return NULL;
+    }
+
+    return field.type == FIELD_TYPE_STRING || field.type == FIELD_TYPE_NULL;
+}
+
+static jboolean isInteger_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+    int32_t err;
+    CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is an integer for %d,%d from %p", row, column, window);
+
+    field_slot_t field;
+    err = window->read_field_slot(row, column, &field);
+    if (err != 0) {
+        throwExceptionWithRowCol(env, row, column);
+        return NULL;
+    }
+
+    return field.type == FIELD_TYPE_INTEGER;
+}
+
+static jboolean isFloat_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+    int32_t err;
+    CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is a float for %d,%d from %p", row, column, window);
+
+    field_slot_t field;
+    err = window->read_field_slot(row, column, &field);
+    if (err != 0) {
+        throwExceptionWithRowCol(env, row, column);
+        return NULL;
+    }
+
+    return field.type == FIELD_TYPE_FLOAT;
+}
+
 static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column)
 {
     int32_t err;
@@ -326,11 +374,11 @@
         jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot");
         return NULL;
     }
-    
+
     jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField);
     if (buffer == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null");
-        return NULL;        
+        return NULL;
     }
     jchar* dst = env->GetCharArrayElements(buffer, NULL);
     uint8_t type = field.type;
@@ -338,7 +386,7 @@
     jcharArray newArray = NULL;
     if (type == FIELD_TYPE_STRING) {
         uint32_t size = field.data.buffer.size;
-        if (size > 0) {            
+        if (size > 0) {
 #if WINDOW_STORAGE_UTF8
             // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string
             String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1);
@@ -346,7 +394,7 @@
             if (strSize > bufferSize || dst == NULL) {
                 newArray = env->NewCharArray(strSize);
                 env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string());
-            } else {                
+            } else {
                 memcpy(dst, (jchar const *)utf16.string(), strSize * 2);
             }
             sizeCopied = strSize;
@@ -359,7 +407,7 @@
                 memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size);
             }
 #endif
-        } 
+        }
     } else if (type == FIELD_TYPE_INTEGER) {
         int64_t value;
         if (window->getLong(row, column, &value)) {
@@ -628,6 +676,9 @@
     {"putDouble_native", "(DII)Z", (void *)putDouble_native},
     {"freeLastRow_native", "()V", (void *)freeLastRow},
     {"putNull_native", "(II)Z", (void *)putNull_native},
+    {"isString_native", "(II)Z", (void *)isString_native},
+    {"isFloat_native", "(II)Z", (void *)isFloat_native},
+    {"isInteger_native", "(II)Z", (void *)isInteger_native},
 };
 
 int register_android_database_CursorWindow(JNIEnv * env)
@@ -646,7 +697,7 @@
         LOGE("Error locating fields");
         return -1;
     }
-    
+
     clazz =  env->FindClass("android/database/CharArrayBuffer");
     if (clazz == NULL) {
         LOGE("Can't find android/database/CharArrayBuffer");
diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
index 66858f9..020aff4a 100644
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ b/core/jni/android_database_SQLiteDatabase.cpp
@@ -28,7 +28,10 @@
 #include <sqlite3.h>
 #include <sqlite3_android.h>
 #include <string.h>
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
 #include <ctype.h>
 
 #include <stdio.h>
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 3dcc09f..0420918 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -25,7 +25,7 @@
 
 #include <ui/Surface.h>
 #include <ui/Camera.h>
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 
 using namespace android;
 
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 42ada54..0cce3a6 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -30,8 +30,8 @@
 #include "media/AudioSystem.h"
 #include "media/AudioTrack.h"
 
-#include <utils/MemoryHeapBase.h>
-#include <utils/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
 
 
 // ----------------------------------------------------------------------------
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 9f93e2f..75ae4d9 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -307,14 +307,15 @@
     return result;
 }
 
-static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getRssiHelper(const char *cmd)
 {
     char reply[256];
     int rssi = -200;
 
-    if (doCommand("DRIVER RSSI", reply, sizeof(reply)) != 0) {
+    if (doCommand(cmd, reply, sizeof(reply)) != 0) {
         return (jint)-1;
     }
+
     // reply comes back in the form "<SSID> rssi XX" where XX is the
     // number we're interested in.  if we're associating, it returns "OK".
     // beware - <SSID> can contain spaces.
@@ -328,6 +329,16 @@
     return (jint)rssi;
 }
 
+static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz)
+{
+    return android_net_wifi_getRssiHelper("DRIVER RSSI");
+}
+
+static jint android_net_wifi_getRssiApproxCommand(JNIEnv* env, jobject clazz)
+{
+    return android_net_wifi_getRssiHelper("DRIVER RSSI-APPROX");
+}
+
 static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject clazz)
 {
     char reply[256];
@@ -523,6 +534,8 @@
     { "setBluetoothCoexistenceScanModeCommand", "(Z)Z",
     		(void*) android_net_wifi_setBluetoothCoexistenceScanModeCommand },
     { "getRssiCommand", "()I", (void*) android_net_wifi_getRssiCommand },
+    { "getRssiApproxCommand", "()I",
+            (void*) android_net_wifi_getRssiApproxCommand},
     { "getLinkSpeedCommand", "()I", (void*) android_net_wifi_getLinkSpeedCommand },
     { "getMacAddressCommand", "()Ljava/lang/String;", (void*) android_net_wifi_getMacAddressCommand },
     { "saveConfigCommand", "()Z", (void*) android_net_wifi_saveConfigCommand },
diff --git a/core/jni/android_os_Exec.cpp b/core/jni/android_os_Exec.cpp
deleted file mode 100644
index ca5e695..0000000
--- a/core/jni/android_os_Exec.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Exec"
-
-#include "JNIHelp.h"
-#include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
-#include "android_runtime/AndroidRuntime.h"
-
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <termios.h>
-
-namespace android
-{
-
-static jclass class_fileDescriptor;
-static jfieldID field_fileDescriptor_descriptor;
-static jmethodID method_fileDescriptor_init;
- 
-
-static int create_subprocess(const char *cmd, const char *arg0, const char *arg1,
-    int* pProcessId)
-{
-    char *devname;
-    int ptm;
-    pid_t pid;
-
-    ptm = open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
-    if(ptm < 0){
-        LOGE("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
-        return -1;
-    }
-    fcntl(ptm, F_SETFD, FD_CLOEXEC);
-
-    if(grantpt(ptm) || unlockpt(ptm) ||
-       ((devname = (char*) ptsname(ptm)) == 0)){
-        LOGE("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
-        return -1;
-    }
-    
-    pid = fork();
-    if(pid < 0) {
-        LOGE("- fork failed: %s -\n", strerror(errno));
-        return -1;
-    }
-
-    if(pid == 0){
-        int pts;
-
-        setsid();
-        
-        pts = open(devname, O_RDWR);
-        if(pts < 0) exit(-1);
-
-        dup2(pts, 0);
-        dup2(pts, 1);
-        dup2(pts, 2);
-
-        close(ptm);
-
-        execl(cmd, cmd, arg0, arg1, NULL);
-        exit(-1);
-    } else {
-        *pProcessId = (int) pid;
-        return ptm;
-    }
-}
-
-
-static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz,
-    jstring cmd, jstring arg0, jstring arg1, jintArray processIdArray)
-{
-    const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0;
-    String8 cmd_8;
-    if (str) {
-        cmd_8 = String8(str, env->GetStringLength(cmd));
-        env->ReleaseStringCritical(cmd, str);
-    }
-
-    str = arg0 ? env->GetStringCritical(arg0, 0) : 0;
-    const char* arg0Str = 0;
-    String8 arg0_8;
-    if (str) {
-        arg0_8 = String8(str, env->GetStringLength(arg0));
-        env->ReleaseStringCritical(arg0, str);
-        arg0Str = arg0_8.string();
-    }
-
-    str = arg1 ? env->GetStringCritical(arg1, 0) : 0;
-    const char* arg1Str = 0;
-    String8 arg1_8;
-    if (str) {
-        arg1_8 = String8(str, env->GetStringLength(arg1));
-        env->ReleaseStringCritical(arg1, str);
-        arg1Str = arg1_8.string();
-    }
-
-    int procId;
-    int ptm = create_subprocess(cmd_8.string(), arg0Str, arg1Str, &procId);
-    
-    if (processIdArray) {
-        int procIdLen = env->GetArrayLength(processIdArray);
-        if (procIdLen > 0) {
-            jboolean isCopy;
-    
-            int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy);
-            if (pProcId) {
-                *pProcId = procId;
-                env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0);
-            }
-        }
-    }
-    
-    jobject result = env->NewObject(class_fileDescriptor, method_fileDescriptor_init);
-    
-    if (!result) {
-        LOGE("Couldn't create a FileDescriptor.");
-    }
-    else {
-        env->SetIntField(result, field_fileDescriptor_descriptor, ptm);
-    }
-    
-    return result;
-}
-
-
-static void android_os_Exec_setPtyWindowSize(JNIEnv *env, jobject clazz,
-    jobject fileDescriptor, jint row, jint col, jint xpixel, jint ypixel)
-{
-    int fd;
-    struct winsize sz;
-
-    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-
-    if (env->ExceptionOccurred() != NULL) {
-        return;
-    }
-    
-    sz.ws_row = row;
-    sz.ws_col = col;
-    sz.ws_xpixel = xpixel;
-    sz.ws_ypixel = ypixel;
-    
-    ioctl(fd, TIOCSWINSZ, &sz);
-}
-
-static int android_os_Exec_waitFor(JNIEnv *env, jobject clazz,
-    jint procId) {
-    int status;
-    waitpid(procId, &status, 0);
-    int result = 0;
-    if (WIFEXITED(status)) {
-        result = WEXITSTATUS(status);
-    }
-    return result;
-}
-
-static JNINativeMethod method_table[] = {
-    { "createSubprocess", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I)Ljava/io/FileDescriptor;",
-        (void*) android_os_Exec_createSubProcess },
-    { "setPtyWindowSize", "(Ljava/io/FileDescriptor;IIII)V",
-        (void*) android_os_Exec_setPtyWindowSize},
-    { "waitFor", "(I)I",
-        (void*) android_os_Exec_waitFor}
-};
-
-int register_android_os_Exec(JNIEnv *env)
-{
-    class_fileDescriptor = env->FindClass("java/io/FileDescriptor");
-
-    if (class_fileDescriptor == NULL) {
-        LOGE("Can't find java/io/FileDescriptor");
-        return -1;
-    }
-
-    field_fileDescriptor_descriptor = env->GetFieldID(class_fileDescriptor, "descriptor", "I");
-
-    if (field_fileDescriptor_descriptor == NULL) {
-        LOGE("Can't find FileDescriptor.descriptor");
-        return -1;
-    }
-
-    method_fileDescriptor_init = env->GetMethodID(class_fileDescriptor, "<init>", "()V");
-    if (method_fileDescriptor_init == NULL) {
-        LOGE("Can't find FileDescriptor.init");
-        return -1;
-     }
-
-    return AndroidRuntime::registerNativeMethods(
-        env, "android/os/Exec",
-        method_table, NELEM(method_table));
-}
-
-};
diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp
index 8643393..1ae3ec7 100644
--- a/core/jni/android_os_MemoryFile.cpp
+++ b/core/jni/android_os_MemoryFile.cpp
@@ -118,7 +118,7 @@
     }
 }
 
-static jboolean android_os_MemoryFile_is_ashmem_region(JNIEnv* env, jobject clazz,
+static jint android_os_MemoryFile_get_mapped_size(JNIEnv* env, jobject clazz,
         jobject fileDescriptor) {
     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region.
@@ -129,13 +129,13 @@
         if (errno == ENOTTY) {
             // ENOTTY means that the ioctl does not apply to this object,
             // i.e., it is not an ashmem region.
-            return JNI_FALSE;
+            return (jint) -1;
         }
         // Some other error, throw exception
         jniThrowIOException(env, errno);
-        return JNI_FALSE;
+        return (jint) -1;
     }
-    return JNI_TRUE;
+    return (jint) result;
 }
 
 static const JNINativeMethod methods[] = {
@@ -146,8 +146,8 @@
     {"native_read",  "(Ljava/io/FileDescriptor;I[BIIIZ)I", (void*)android_os_MemoryFile_read},
     {"native_write", "(Ljava/io/FileDescriptor;I[BIIIZ)V", (void*)android_os_MemoryFile_write},
     {"native_pin",   "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin},
-    {"native_is_ashmem_region", "(Ljava/io/FileDescriptor;)Z",
-            (void*)android_os_MemoryFile_is_ashmem_region}
+    {"native_get_mapped_size", "(Ljava/io/FileDescriptor;)I",
+            (void*)android_os_MemoryFile_get_mapped_size}
 };
 
 static const char* const kClassPathName = "android/os/MemoryFile";
diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp
index 91a8e8e..14da1fd 100644
--- a/core/jni/android_server_BluetoothA2dpService.cpp
+++ b/core/jni/android_server_BluetoothA2dpService.cpp
@@ -37,12 +37,7 @@
 namespace android {
 
 #ifdef HAVE_BLUETOOTH
-static jmethodID method_onHeadsetCreated;
-static jmethodID method_onHeadsetRemoved;
-static jmethodID method_onSinkConnected;
-static jmethodID method_onSinkDisconnected;
-static jmethodID method_onSinkPlaying;
-static jmethodID method_onSinkStopped;
+static jmethodID method_onSinkPropertyChanged;
 
 typedef struct {
     JavaVM *vm;
@@ -53,11 +48,11 @@
 
 static native_data_t *nat = NULL;  // global native data
 
-#endif
-
-#ifdef HAVE_BLUETOOTH
-static void onConnectSinkResult(DBusMessage *msg, void *user, void *nat);
-static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *nat);
+static Properties sink_properties[] = {
+        {"State", DBUS_TYPE_STRING},
+        {"Connected", DBUS_TYPE_BOOLEAN},
+        {"Playing", DBUS_TYPE_BOOLEAN},
+      };
 #endif
 
 /* Returns true on success (even if adapter is present but disabled).
@@ -100,91 +95,58 @@
     }
 #endif
 }
-static jobjectArray listHeadsetsNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    if (nat) {
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, "/org/bluez/audio",
-                           "org.bluez.audio.Manager", "ListHeadsets",
-                           DBUS_TYPE_INVALID);
-        return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
-    }
-#endif
-    return NULL;
-}
 
-static jstring createHeadsetNative(JNIEnv *env, jobject object,
-                                     jstring address) {
+static jobjectArray getSinkPropertiesNative(JNIEnv *env, jobject object,
+                                            jstring path) {
 #ifdef HAVE_BLUETOOTH
     LOGV(__FUNCTION__);
     if (nat) {
-        const char *c_address = env->GetStringUTFChars(address, NULL);
-        LOGV("... address = %s\n", c_address);
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, "/org/bluez/audio",
-                           "org.bluez.audio.Manager", "CreateHeadset",
-                           DBUS_TYPE_STRING, &c_address,
-                           DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(address, c_address);
-        return reply ? dbus_returns_string(env, reply) : NULL;
-    }
-#endif
-    return NULL;
-}
+        DBusMessage *msg, *reply;
+        DBusError err;
+        dbus_error_init(&err);
 
-static jstring removeHeadsetNative(JNIEnv *env, jobject object, jstring path) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    if (nat) {
         const char *c_path = env->GetStringUTFChars(path, NULL);
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, "/org/bluez/audio",
-                           "org.bluez.audio.Manager", "RemoveHeadset",
-                           DBUS_TYPE_STRING, &c_path,
-                           DBUS_TYPE_INVALID);
+        reply = dbus_func_args_timeout(env,
+                                   nat->conn, -1, c_path,
+                                   "org.bluez.AudioSink", "GetProperties",
+                                   DBUS_TYPE_INVALID);
         env->ReleaseStringUTFChars(path, c_path);
-        return reply ? dbus_returns_string(env, reply) : NULL;
+        if (!reply && dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
+            return NULL;
+        } else if (!reply) {
+            LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+            return NULL;
+        }
+        DBusMessageIter iter;
+        if (dbus_message_iter_init(reply, &iter))
+            return parse_properties(env, &iter, (Properties *)&sink_properties,
+                                 sizeof(sink_properties) / sizeof(Properties));
     }
 #endif
     return NULL;
 }
 
-static jstring getAddressNative(JNIEnv *env, jobject object, jstring path) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    if (nat) {
-        const char *c_path = env->GetStringUTFChars(path, NULL);
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, c_path,
-                           "org.bluez.audio.Device", "GetAddress",
-                           DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(path, c_path);
-        return reply ? dbus_returns_string(env, reply) : NULL;
-    }
-#endif
-    return NULL;
-}
 
 static jboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) {
 #ifdef HAVE_BLUETOOTH
     LOGV(__FUNCTION__);
     if (nat) {
         const char *c_path = env->GetStringUTFChars(path, NULL);
-        size_t path_sz = env->GetStringUTFLength(path) + 1;
-        char *c_path_copy = (char *)malloc(path_sz);  // callback data
-        strncpy(c_path_copy, c_path, path_sz);
+        DBusError err;
+        dbus_error_init(&err);
 
-        bool ret =
-            dbus_func_args_async(env, nat->conn, -1,
-                           onConnectSinkResult, (void *)c_path_copy, nat,
-                           c_path,
-                           "org.bluez.audio.Sink", "Connect",
-                           DBUS_TYPE_INVALID);
-
+        DBusMessage *reply =
+                dbus_func_args_timeout(env, nat->conn, -1, c_path,
+                               "org.bluez.AudioSink", "Connect",
+                                DBUS_TYPE_INVALID);
         env->ReleaseStringUTFChars(path, c_path);
-        if (!ret) {
-            free(c_path_copy);
+
+        if (!reply && dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
+            return JNI_FALSE;
+        } else if (!reply) {
+            LOGE("DBus reply is NULL in function %s", __FUNCTION__);
             return JNI_FALSE;
         }
         return JNI_TRUE;
@@ -199,19 +161,20 @@
     LOGV(__FUNCTION__);
     if (nat) {
         const char *c_path = env->GetStringUTFChars(path, NULL);
-        size_t path_sz = env->GetStringUTFLength(path) + 1;
-        char *c_path_copy = (char *)malloc(path_sz);  // callback data
-        strncpy(c_path_copy, c_path, path_sz);
+        DBusError err;
+        dbus_error_init(&err);
 
-        bool ret =
-            dbus_func_args_async(env, nat->conn, -1,
-                           onDisconnectSinkResult, (void *)c_path_copy, nat,
-                           c_path,
-                           "org.bluez.audio.Sink", "Disconnect",
-                           DBUS_TYPE_INVALID);
+        DBusMessage *reply =
+              dbus_func_args_timeout(env, nat->conn, -1, c_path,
+                                     "org.bluez.AudioSink", "Disconnect",
+                                     DBUS_TYPE_INVALID);
         env->ReleaseStringUTFChars(path, c_path);
-        if (!ret) {
-            free(c_path_copy);
+
+        if (!reply && dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
+            return JNI_FALSE;
+        } else if (!reply) {
+            LOGE("DBus reply is NULL in function %s", __FUNCTION__);
             return JNI_FALSE;
         }
         return JNI_TRUE;
@@ -220,93 +183,7 @@
     return JNI_FALSE;
 }
 
-static jboolean isSinkConnectedNative(JNIEnv *env, jobject object, jstring path) {
 #ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    if (nat) {
-        const char *c_path = env->GetStringUTFChars(path, NULL);
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, c_path,
-                           "org.bluez.audio.Sink", "IsConnected",
-                           DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(path, c_path);
-        return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE;
-    }
-#endif
-    return JNI_FALSE;
-}
-
-#ifdef HAVE_BLUETOOTH
-static void onConnectSinkResult(DBusMessage *msg, void *user, void *natData) {
-    LOGV(__FUNCTION__);
-
-    char *c_path = (char *)user;
-    DBusError err;
-    JNIEnv *env;
-
-    if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) {
-        LOGE("%s: error finding Env for our VM\n", __FUNCTION__);
-        return;
-    }
-
-    dbus_error_init(&err);
-
-    LOGV("... path = %s", c_path);
-    if (dbus_set_error_from_message(&err, msg)) {
-        /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */
-        LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
-        dbus_error_free(&err);
-        env->CallVoidMethod(nat->me,
-                            method_onSinkDisconnected,
-                            env->NewStringUTF(c_path));
-        if (env->ExceptionCheck()) {
-            LOGE("VM Exception occurred in native function %s (%s:%d)",
-                 __FUNCTION__, __FILE__, __LINE__);
-        }
-    } // else Java callback is triggered by signal in a2dp_event_filter
-
-    free(c_path);
-}
-
-static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *natData) {
-    LOGV(__FUNCTION__);
-
-    char *c_path = (char *)user;
-    DBusError err;
-    JNIEnv *env;
-
-    if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) {
-        LOGE("%s: error finding Env for our VM\n", __FUNCTION__);
-        return;
-    }
-
-    dbus_error_init(&err);
-
-    LOGV("... path = %s", c_path);
-    if (dbus_set_error_from_message(&err, msg)) {
-        /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */
-        LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
-        if (strcmp(err.name, "org.bluez.Error.NotConnected") == 0) {
-            // we were already disconnected, so report disconnect
-            env->CallVoidMethod(nat->me,
-                                method_onSinkDisconnected,
-                                env->NewStringUTF(c_path));
-        } else {
-            // Assume it is still connected
-            env->CallVoidMethod(nat->me,
-                                method_onSinkConnected,
-                                env->NewStringUTF(c_path));
-        }
-        dbus_error_free(&err);
-        if (env->ExceptionCheck()) {
-            LOGE("VM Exception occurred in native function %s (%s:%d)",
-                 __FUNCTION__, __FILE__, __LINE__);
-        }
-    } // else Java callback is triggered by signal in a2dp_event_filter
-
-    free(c_path);
-}
-
 DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) {
     DBusError err;
 
@@ -324,71 +201,23 @@
 
     DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
-    if (dbus_message_is_signal(msg,
-                               "org.bluez.audio.Manager",
-                               "HeadsetCreated")) {
-        char *c_path;
-        if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_path,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... path = %s", c_path);
-            env->CallVoidMethod(nat->me,
-                                method_onHeadsetCreated,
-                                env->NewStringUTF(c_path));
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        result = DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.audio.Manager",
-                                      "HeadsetRemoved")) {
-        char *c_path;
-        if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_path,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... path = %s", c_path);
-            env->CallVoidMethod(nat->me,
-                                method_onHeadsetRemoved,
-                                env->NewStringUTF(c_path));
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        result = DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.audio.Sink",
-                                      "Connected")) {
+    if (dbus_message_is_signal(msg, "org.bluez.AudioSink",
+                                      "PropertyChanged")) {
+        jobjectArray str_array =
+                    parse_property_change(env, msg, (Properties *)&sink_properties,
+                                sizeof(sink_properties) / sizeof(Properties));
         const char *c_path = dbus_message_get_path(msg);
-        LOGV("... path = %s", c_path);
         env->CallVoidMethod(nat->me,
-                            method_onSinkConnected,
-                            env->NewStringUTF(c_path));
+                            method_onSinkPropertyChanged,
+                            env->NewStringUTF(c_path),
+                            str_array);
+        for (int i = 0; i < env->GetArrayLength(str_array); i++) {
+            env->DeleteLocalRef(env->GetObjectArrayElement(str_array, i));
+        }
+        env->DeleteLocalRef(str_array);
         result = DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.audio.Sink",
-                                      "Disconnected")) {
-        const char *c_path = dbus_message_get_path(msg);
-        LOGV("... path = %s", c_path);
-        env->CallVoidMethod(nat->me,
-                            method_onSinkDisconnected,
-                            env->NewStringUTF(c_path));
-        result = DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.audio.Sink",
-                                      "Playing")) {
-        const char *c_path = dbus_message_get_path(msg);
-        LOGV("... path = %s", c_path);
-        env->CallVoidMethod(nat->me,
-                            method_onSinkPlaying,
-                            env->NewStringUTF(c_path));
-        result = DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.audio.Sink",
-                                      "Stopped")) {
-        const char *c_path = dbus_message_get_path(msg);
-        LOGV("... path = %s", c_path);
-        env->CallVoidMethod(nat->me,
-                            method_onSinkStopped,
-                            env->NewStringUTF(c_path));
-        result = DBUS_HANDLER_RESULT_HANDLED;
-    }
-
-    if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED) {
+        return result;
+    } else {
         LOGV("... ignored");
     }
     if (env->ExceptionCheck()) {
@@ -407,14 +236,11 @@
     {"initNative", "()Z", (void *)initNative},
     {"cleanupNative", "()V", (void *)cleanupNative},
 
-    /* Bluez audio 3.36 API */
-    {"listHeadsetsNative", "()[Ljava/lang/String;", (void*)listHeadsetsNative},
-    {"createHeadsetNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)createHeadsetNative},
-    {"removeHeadsetNative", "(Ljava/lang/String;)Z", (void*)removeHeadsetNative},
-    {"getAddressNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getAddressNative},
-    {"connectSinkNative", "(Ljava/lang/String;)Z", (void*)connectSinkNative},
-    {"disconnectSinkNative", "(Ljava/lang/String;)Z", (void*)disconnectSinkNative},
-    {"isSinkConnectedNative", "(Ljava/lang/String;)Z", (void*)isSinkConnectedNative},
+    /* Bluez audio 4.40 API */
+    {"connectSinkNative", "(Ljava/lang/String;)Z", (void *)connectSinkNative},
+    {"disconnectSinkNative", "(Ljava/lang/String;)Z", (void *)disconnectSinkNative},
+    {"getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
+                                    (void *)getSinkPropertiesNative},
 };
 
 int register_android_server_BluetoothA2dpService(JNIEnv *env) {
@@ -425,12 +251,8 @@
     }
 
 #ifdef HAVE_BLUETOOTH
-    method_onHeadsetCreated = env->GetMethodID(clazz, "onHeadsetCreated", "(Ljava/lang/String;)V");
-    method_onHeadsetRemoved = env->GetMethodID(clazz, "onHeadsetRemoved", "(Ljava/lang/String;)V");
-    method_onSinkConnected = env->GetMethodID(clazz, "onSinkConnected", "(Ljava/lang/String;)V");
-    method_onSinkDisconnected = env->GetMethodID(clazz, "onSinkDisconnected", "(Ljava/lang/String;)V");
-    method_onSinkPlaying = env->GetMethodID(clazz, "onSinkPlaying", "(Ljava/lang/String;)V");
-    method_onSinkStopped = env->GetMethodID(clazz, "onSinkStopped", "(Ljava/lang/String;)V");
+    method_onSinkPropertyChanged = env->GetMethodID(clazz, "onSinkPropertyChanged",
+                                          "(Ljava/lang/String;[Ljava/lang/String;)V");
 #endif
 
     return AndroidRuntime::registerNativeMethods(env,
diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp
index 58ae4f6..b02a19b 100644
--- a/core/jni/android_server_BluetoothDeviceService.cpp
+++ b/core/jni/android_server_BluetoothDeviceService.cpp
@@ -14,7 +14,8 @@
 ** limitations under the License.
 */
 
-#define DBUS_CLASS_NAME BLUEZ_DBUS_BASE_IFC ".Adapter"
+#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"
+#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
 #define LOG_TAG "BluetoothDeviceService.cpp"
 
 #include "android_bluetooth_common.h"
@@ -60,8 +61,11 @@
 
 extern event_loop_native_data_t *get_EventLoop_native_data(JNIEnv *,
                                                            jobject);
-void onCreateBondingResult(DBusMessage *msg, void *user, void *nat);
-void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *nat);
+extern DBusHandlerResult agent_event_filter(DBusConnection *conn,
+                                            DBusMessage *msg,
+                                            void *data);
+void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat);
+
 
 /** Get native data stored in the opaque (Java code maintained) pointer mNativeData
  *  Perform quick sanity check, if there are any problems return NULL
@@ -110,12 +114,61 @@
         return false;
     }
     dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
-
-    nat->adapter = BLUEZ_ADAPTER_OBJECT_NAME;
 #endif  /*HAVE_BLUETOOTH*/
     return true;
 }
 
+static const char *get_adapter_path(JNIEnv* env, jobject object) {
+#ifdef HAVE_BLUETOOTH
+    event_loop_native_data_t *event_nat =
+        get_EventLoop_native_data(env, env->GetObjectField(object,
+                                                           field_mEventLoop));
+    if (event_nat == NULL)
+        return NULL;
+    return event_nat->adapter;
+#else
+    return NULL;
+#endif
+}
+
+// This function is called when the adapter is enabled.
+static jboolean setupNativeDataNative(JNIEnv* env, jobject object) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat =
+        (native_data_t *)env->GetIntField(object, field_mNativeData);
+    event_loop_native_data_t *event_nat =
+        get_EventLoop_native_data(env, env->GetObjectField(object,
+                                                           field_mEventLoop));
+    // Register agent for remote devices.
+    const char *device_agent_path = "/android/bluetooth/remote_device_agent";
+    static const DBusObjectPathVTable agent_vtable = {
+                 NULL, agent_event_filter, NULL, NULL, NULL, NULL };
+
+    if (!dbus_connection_register_object_path(nat->conn, device_agent_path,
+                                              &agent_vtable, event_nat)) {
+        LOGE("%s: Can't register object path %s for remote device agent!",
+                               __FUNCTION__, device_agent_path);
+        return JNI_FALSE;
+    }
+#endif /*HAVE_BLUETOOTH*/
+    return JNI_TRUE;
+}
+
+static jboolean tearDownNativeDataNative(JNIEnv *env, jobject object) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat =
+               (native_data_t *)env->GetIntField(object, field_mNativeData);
+    if (nat != NULL) {
+        const char *device_agent_path =
+            "/android/bluetooth/remote_device_agent";
+        dbus_connection_unregister_object_path (nat->conn, device_agent_path);
+    }
+#endif /*HAVE_BLUETOOTH*/
+    return JNI_TRUE;
+}
+
 static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
     LOGV(__FUNCTION__);
 #ifdef HAVE_BLUETOOTH
@@ -128,26 +181,12 @@
 #endif
 }
 
-static jstring getNameNative(JNIEnv *env, jobject object){
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter,
-                                            DBUS_CLASS_NAME, "GetName",
-                                            DBUS_TYPE_INVALID);
-        return reply ? dbus_returns_string(env, reply) : NULL;
-    }
-#endif
-    return NULL;
-}
-
 static jstring getAdapterPathNative(JNIEnv *env, jobject object) {
     LOGV(__FUNCTION__);
 #ifdef HAVE_BLUETOOTH
     native_data_t *nat = get_native_data(env, object);
     if (nat) {
-        return (env->NewStringUTF(nat->adapter));
+        return (env->NewStringUTF(get_adapter_path(env, object)));
     }
 #endif
     return NULL;
@@ -171,28 +210,23 @@
     dbus_error_init(&err);
 
     /* Compose the command */
-    msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter,
-                                       DBUS_CLASS_NAME, "DiscoverDevices");
+    msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
+                                       get_adapter_path(env, object),
+                                       DBUS_ADAPTER_IFACE, "StartDiscovery");
 
     if (msg == NULL) {
-        LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__);
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+        }
         goto done;
     }
 
     /* Send the command. */
     reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
     if (dbus_error_is_set(&err)) {
-        /* We treat the in-progress error code as success. */
-        if(strcmp(err.message, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) {
-            LOGW("%s: D-Bus error: %s, treating as startDiscoveryNative success\n",
-                 __FUNCTION__, err.message);
-            ret = JNI_TRUE;
-            goto done;
-        } else {
-            LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
-            ret = JNI_FALSE;
-            goto done;
-        }
+         LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+         ret = JNI_FALSE;
+         goto done;
     }
 
     ret = JNI_TRUE;
@@ -205,7 +239,7 @@
 #endif
 }
 
-static void cancelDiscoveryNative(JNIEnv *env, jobject object) {
+static void stopDiscoveryNative(JNIEnv *env, jobject object) {
     LOGV(__FUNCTION__);
 #ifdef HAVE_BLUETOOTH
     DBusMessage *msg = NULL;
@@ -223,18 +257,20 @@
     }
 
     /* Compose the command */
-    msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter,
-                                       DBUS_CLASS_NAME, "CancelDiscovery");
-
+    msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
+                                       get_adapter_path(env, object),
+                                       DBUS_ADAPTER_IFACE, "StopDiscovery");
     if (msg == NULL) {
-        LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__);
+        if (dbus_error_is_set(&err))
+            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
         goto done;
     }
 
     /* Send the command. */
     reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
     if (dbus_error_is_set(&err)) {
-        if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized") == 0) {
+        if(strncmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized",
+                   strlen(BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized")) == 0) {
             // hcid sends this if there is no active discovery to cancel
             LOGV("%s: There was no active discovery to cancel", __FUNCTION__);
             dbus_error_free(&err);
@@ -249,232 +285,8 @@
 #endif
 }
 
-static jboolean startPeriodicDiscoveryNative(JNIEnv *env, jobject object) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    DBusMessage *msg = NULL;
-    DBusMessage *reply = NULL;
-    DBusError err;
-    jboolean ret = JNI_FALSE;
-
-    native_data_t *nat = get_native_data(env, object);
-    if (nat == NULL) {
-        goto done;
-    }
-
-    dbus_error_init(&err);
-    msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter,
-            DBUS_CLASS_NAME, "StartPeriodicDiscovery");
-    if (msg == NULL) {
-        LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__);
-        goto done;
-    }
-
-    /* Send the command. */
-    reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
-    if (dbus_error_is_set(&err)) {
-        /* We treat the in-progress error code as success. */
-        if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) {
-            LOGW("%s: D-Bus error: %s (%s), treating as "
-                 "startPeriodicDiscoveryNative success\n",
-                 __FUNCTION__, err.name, err.message);
-        } else {
-            LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
-            ret = JNI_FALSE;
-            goto done;
-        }
-    }
-
-    ret = JNI_TRUE;
-done:
-    if (reply) dbus_message_unref(reply);
-    if (msg) dbus_message_unref(msg);
-    return ret;
-#else
-    return JNI_FALSE;
-#endif
-}
-
-static jboolean stopPeriodicDiscoveryNative(JNIEnv *env, jobject object) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    DBusMessage *msg = NULL;
-    DBusMessage *reply = NULL;
-    DBusError err;
-    const char *name;
-    jboolean ret = JNI_FALSE;
-
-    native_data_t *nat = get_native_data(env, object);
-    if (nat == NULL) {
-        goto done;
-    }
-
-    dbus_error_init(&err);
-    msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter,
-            DBUS_CLASS_NAME, "StopPeriodicDiscovery");
-    if (msg == NULL) {
-        LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__);
-        goto done;
-    }
-
-    /* Send the command. */
-    reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
-    if (dbus_error_is_set(&err)) {
-        /* We treat the in-progress error code as success. */
-        if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) {
-            LOGW("%s: D-Bus error: %s (%s), treating as "
-                 "stopPeriodicDiscoveryNative success\n",
-                 __FUNCTION__, err.name, err.message);
-        } else {
-            LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
-            ret = JNI_FALSE;
-            goto done;
-        }
-    }
-
-    ret = JNI_TRUE;
-done:
-    if (reply) dbus_message_unref(reply);
-    if (msg) dbus_message_unref(msg);
-    return ret;
-#else
-    return JNI_FALSE;
-#endif
-}
-
-static jboolean isPeriodicDiscoveryNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "IsPeriodicDiscovery",
-                           DBUS_TYPE_INVALID);
-        return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE;
-    }
-#endif
-    return JNI_FALSE;
-}
-
-static jboolean setDiscoverableTimeoutNative(JNIEnv *env, jobject object, jint timeout_s) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-
-    if (timeout_s < 0) {
-        return JNI_FALSE;
-    }
-
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "SetDiscoverableTimeout",
-                           DBUS_TYPE_UINT32, &timeout_s,
-                           DBUS_TYPE_INVALID);
-        if (reply != NULL) {
-            dbus_message_unref(reply);
-            return JNI_TRUE;
-        }
-    }
-#endif
-    return JNI_FALSE;
-}
-
-static jint getDiscoverableTimeoutNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "GetDiscoverableTimeout",
-                           DBUS_TYPE_INVALID);
-        return reply ? dbus_returns_uint32(env, reply) : -1;
-    }
-#endif
-    return -1;
-}
-
-static jboolean isConnectedNative(JNIEnv *env, jobject object, jstring address) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        const char *c_address = env->GetStringUTFChars(address, NULL);
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "IsConnected",
-                           DBUS_TYPE_STRING, &c_address,
-                           DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(address, c_address);
-        return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE;
-    }
-#endif
-    return JNI_FALSE;
-}
-
-static void disconnectRemoteDeviceNative(JNIEnv *env, jobject object, jstring address) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        const char *c_address = env->GetStringUTFChars(address, NULL);
-        // Set a timeout of 5 seconds.  Specifying the default timeout is
-        // not long enough, as a remote-device disconnect results in
-        // signal RemoteDisconnectRequested being sent, followed by a
-        // delay of 2 seconds, after which the actual disconnect takes
-        // place.
-        DBusMessage *reply =
-            dbus_func_args_timeout(env, nat->conn, 60000, nat->adapter,
-                                   DBUS_CLASS_NAME, "DisconnectRemoteDevice",
-                                   DBUS_TYPE_STRING, &c_address,
-                                   DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(address, c_address);
-        if (reply) dbus_message_unref(reply);
-    }
-#endif
-}
-
-static jstring getModeNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "GetMode",
-                           DBUS_TYPE_INVALID);
-        return reply ? dbus_returns_string(env, reply) : NULL;
-    }
-#endif
-    return NULL;
-}
-
-static jboolean setModeNative(JNIEnv *env, jobject object, jstring mode) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        const char *c_mode = env->GetStringUTFChars(mode, NULL);
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "SetMode",
-                           DBUS_TYPE_STRING, &c_mode,
-                           DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(mode, c_mode);
-        if (reply) {
-            dbus_message_unref(reply);
-            return JNI_TRUE;
-        }
-        return JNI_FALSE;
-    }
-#endif
-    return JNI_FALSE;
-}
-
-static jboolean createBondingNative(JNIEnv *env, jobject object,
-                                    jstring address, jint timeout_ms) {
+static jboolean createPairedDeviceNative(JNIEnv *env, jobject object,
+                                         jstring address, jint timeout_ms) {
     LOGV(__FUNCTION__);
 #ifdef HAVE_BLUETOOTH
     native_data_t *nat = get_native_data(env, object);
@@ -486,14 +298,20 @@
         const char *c_address = env->GetStringUTFChars(address, NULL);
         LOGV("... address = %s", c_address);
         char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
+        const char *capabilities = "DisplayYesNo";
+        const char *agent_path = "/android/bluetooth/remote_device_agent";
+
         strlcpy(context_address, c_address, BTADDR_SIZE);  // for callback
         bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms,
-                                        onCreateBondingResult, // callback
+                                        onCreatePairedDeviceResult, // callback
                                         context_address,
                                         eventLoopNat,
-                                        nat->adapter,
-                                        DBUS_CLASS_NAME, "CreateBonding",
+                                        get_adapter_path(env, object),
+                                        DBUS_ADAPTER_IFACE,
+                                        "CreatePairedDevice",
                                         DBUS_TYPE_STRING, &c_address,
+                                        DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                        DBUS_TYPE_STRING, &capabilities,
                                         DBUS_TYPE_INVALID);
         env->ReleaseStringUTFChars(address, c_address);
         return ret ? JNI_TRUE : JNI_FALSE;
@@ -503,367 +321,9 @@
     return JNI_FALSE;
 }
 
-static jboolean cancelBondingProcessNative(JNIEnv *env, jobject object,
-                                       jstring address) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        const char *c_address = env->GetStringUTFChars(address, NULL);
-        LOGV("... address = %s", c_address);
-        DBusMessage *reply =
-            dbus_func_args_timeout(env, nat->conn, -1, nat->adapter,
-                                   DBUS_CLASS_NAME, "CancelBondingProcess",
-                                   DBUS_TYPE_STRING, &c_address,
-                                   DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(address, c_address);
-        if (reply) {
-            dbus_message_unref(reply);
-        }
-        return JNI_TRUE;
-    }
-#endif
-    return JNI_FALSE;
-}
-
-static jboolean removeBondingNative(JNIEnv *env, jobject object, jstring address) {
-    LOGV(__FUNCTION__);
-    jboolean result = JNI_FALSE;
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        const char *c_address = env->GetStringUTFChars(address, NULL);
-        LOGV("... address = %s", c_address);
-        DBusError err;
-        dbus_error_init(&err);
-        DBusMessage *reply =
-            dbus_func_args_error(env, nat->conn, &err, nat->adapter,
-                                 DBUS_CLASS_NAME, "RemoveBonding",
-                                 DBUS_TYPE_STRING, &c_address,
-                                 DBUS_TYPE_INVALID);
-        if (dbus_error_is_set(&err)) {
-            if (dbus_error_has_name(&err,
-                    BLUEZ_DBUS_BASE_IFC ".Error.DoesNotExist")) {
-                LOGW("%s: Warning: %s (%s)", __FUNCTION__, err.message,
-                     c_address);
-                result = JNI_TRUE;
-            } else {
-                LOGE("%s: D-Bus error %s (%s)", __FUNCTION__, err.name,
-                        err.message);
-            }
-        } else {
-            result = JNI_TRUE;
-        }
-
-        env->ReleaseStringUTFChars(address, c_address);
-        dbus_error_free(&err);
-        if (reply) dbus_message_unref(reply);
-    }
-#endif
-    return result;
-}
-
-static jobjectArray listBondingsNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "ListBondings",
-                           DBUS_TYPE_INVALID);
-        // return String[]
-        return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
-    }
-#endif
-    return NULL;
-}
-
-static jobjectArray listConnectionsNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "ListConnections",
-                           DBUS_TYPE_INVALID);
-        // return String[]
-        return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
-    }
-#endif
-    return NULL;
-}
-
-static jobjectArray listRemoteDevicesNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "ListRemoteDevices",
-                           DBUS_TYPE_INVALID);
-        return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
-    }
-#endif
-    return NULL;
-}
-
-static jstring common_Get(JNIEnv *env, jobject object, const char *func) {
-    LOGV("%s:%s", __FUNCTION__, func);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        DBusError err;
-        dbus_error_init(&err);
-        DBusMessage *reply =
-            dbus_func_args_error(env, nat->conn, &err, nat->adapter,
-                                 DBUS_CLASS_NAME, func,
-                                 DBUS_TYPE_INVALID);
-        if (reply) {
-            return dbus_returns_string(env, reply);
-        } else {
-            LOG_AND_FREE_DBUS_ERROR(&err);
-            return NULL;
-        }
-    }
-#endif
-    return NULL;
-}
-
-static jstring getAddressNative(JNIEnv *env, jobject obj) {
-    return common_Get(env, obj, "GetAddress");
-}
-
-static jstring getVersionNative(JNIEnv *env, jobject obj) {
-    return common_Get(env, obj, "GetVersion");
-}
-
-static jstring getRevisionNative(JNIEnv *env, jobject obj) {
-    return common_Get(env, obj, "GetRevision");
-}
-
-static jstring getManufacturerNative(JNIEnv *env, jobject obj) {
-    return common_Get(env, obj, "GetManufacturer");
-}
-
-static jstring getCompanyNative(JNIEnv *env, jobject obj) {
-    return common_Get(env, obj, "GetCompany");
-}
-
-static jboolean setNameNative(JNIEnv *env, jobject obj, jstring name) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, obj);
-    if (nat) {
-        const char *c_name = env->GetStringUTFChars(name, NULL);
-        DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter,
-                                            DBUS_CLASS_NAME, "SetName",
-                                            DBUS_TYPE_STRING, &c_name,
-                                            DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(name, c_name);
-        if (reply) {
-            dbus_message_unref(reply);
-            return JNI_TRUE;
-        }
-    }
-#endif
-    return JNI_FALSE;
-}
-
-static jstring common_getRemote(JNIEnv *env, jobject object, const char *func,
-                                jstring address) {
-    LOGV("%s:%s", __FUNCTION__, func);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        const char *c_address = env->GetStringUTFChars(address, NULL);
-        DBusError err;
-        dbus_error_init(&err);
-
-        LOGV("... address = %s", c_address);
-
-        DBusMessage *reply =
-            dbus_func_args_error(env, nat->conn, &err, nat->adapter,
-                                 DBUS_CLASS_NAME, func,
-                                 DBUS_TYPE_STRING, &c_address,
-                                 DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(address, c_address);
-        if (reply) {
-            return dbus_returns_string(env, reply);
-        } else if (!strcmp(func, "GetRemoteName") &&
-                dbus_error_has_name(&err, "org.bluez.Error.RequestDeferred")) {
-            // This error occurs if we request name during device discovery,
-            // its fine
-            LOGV("... %s: %s", func, err.message);
-            dbus_error_free(&err);
-            return NULL;
-        } else {
-            LOG_AND_FREE_DBUS_ERROR(&err);
-            return NULL;
-        }
-    }
-#endif
-    return NULL;
-}
-
-static jstring getRemoteVersionNative(JNIEnv *env, jobject obj, jstring address) {
-    return common_getRemote(env, obj, "GetRemoteVersion", address);
-}
-
-static jstring getRemoteRevisionNative(JNIEnv *env, jobject obj, jstring address) {
-    return common_getRemote(env, obj, "GetRemoteRevision", address);
-}
-
-static jstring getRemoteManufacturerNative(JNIEnv *env, jobject obj, jstring address) {
-    return common_getRemote(env, obj, "GetRemoteManufacturer", address);
-}
-
-static jstring getRemoteCompanyNative(JNIEnv *env, jobject obj, jstring address) {
-    return common_getRemote(env, obj, "GetRemoteCompany", address);
-}
-
-static jstring getRemoteNameNative(JNIEnv *env, jobject obj, jstring address) {
-    return common_getRemote(env, obj, "GetRemoteName", address);
-}
-
-static jstring lastSeenNative(JNIEnv *env, jobject obj, jstring address) {
-    return common_getRemote(env, obj, "LastSeen", address);
-}
-
-static jstring lastUsedNative(JNIEnv *env, jobject obj, jstring address) {
-    return common_getRemote(env, obj, "LastUsed", address);
-}
-
-static jint getRemoteClassNative(JNIEnv *env, jobject object, jstring address) {
-    jint result = BLUETOOTH_CLASS_ERROR;
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        const char *c_address = env->GetStringUTFChars(address, NULL);
-
-        LOGV("... address = %s", c_address);
-
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "GetRemoteClass",
-                           DBUS_TYPE_STRING, &c_address,
-                           DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(address, c_address);
-        if (reply)
-        {
-            DBusError err;
-            dbus_error_init(&err);
-            if (!dbus_message_get_args(reply, &err,
-                                      DBUS_TYPE_UINT32, &result,
-                                      DBUS_TYPE_INVALID)) {
-                LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
-            }
-            dbus_message_unref(reply);
-        }
-    }
-#endif
-    return result;
-}
-
-static jbyteArray getRemoteFeaturesNative(JNIEnv *env, jobject object,
-                                          jstring address) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        const char *c_address = env->GetStringUTFChars(address, NULL);
-
-        LOGV("... address = %s", c_address);
-
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "GetRemoteFeatures",
-                           DBUS_TYPE_STRING, &c_address,
-                           DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(address, c_address);
-        /* array of DBUS_TYPE_BYTE_AS_STRING */
-        return reply ? dbus_returns_array_of_bytes(env, reply) : NULL;
-    }
-#endif
-    return NULL;
-}
-
-static jintArray getRemoteServiceHandlesNative(JNIEnv *env, jobject object,
-                                               jstring address, jstring match) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        jintArray intArray = NULL;
-        const char *c_address = env->GetStringUTFChars(address, NULL);
-        const char *c_match = env->GetStringUTFChars(match, NULL);
-
-        LOGV("... address = %s match = %s", c_address, c_match);
-
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "GetRemoteServiceHandles",
-                           DBUS_TYPE_STRING, &c_address,
-                           DBUS_TYPE_STRING, &c_match,
-                           DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(address, c_address);
-        env->ReleaseStringUTFChars(match, c_match);
-        if (reply)
-        {
-            DBusError err;
-            jint *list;
-            int i, len;
-
-            dbus_error_init(&err);
-            if (dbus_message_get_args (reply, &err,
-                                       DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
-                                       &list, &len,
-                                       DBUS_TYPE_INVALID)) {
-                if (len) {
-                    intArray = env->NewIntArray(len);
-                    if (intArray)
-                        env->SetIntArrayRegion(intArray, 0, len, list);
-                }
-            } else {
-                LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
-            }
-
-            dbus_message_unref(reply);
-        }
-        return intArray;
-    }
-#endif
-    return NULL;
-}
-
-static jbyteArray getRemoteServiceRecordNative(JNIEnv *env, jobject object,
-                                                 jstring address, jint handle) {
-#ifdef HAVE_BLUETOOTH
-    LOGV(__FUNCTION__);
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        const char *c_address = env->GetStringUTFChars(address, NULL);
-
-        LOGV("... address = %s", c_address);
-
-        DBusMessage *reply =
-            dbus_func_args(env, nat->conn, nat->adapter,
-                           DBUS_CLASS_NAME, "GetRemoteServiceRecord",
-                           DBUS_TYPE_STRING, &c_address,
-                           DBUS_TYPE_UINT32, &handle,
-                           DBUS_TYPE_INVALID);
-        env->ReleaseStringUTFChars(address, c_address);
-        return reply ? dbus_returns_array_of_bytes(env, reply) : NULL;
-    }
-#endif
-    return NULL;
-}
-
-static jboolean getRemoteServiceChannelNative(JNIEnv *env, jobject object,
-                                          jstring address, jshort uuid16) {
+static jint getDeviceServiceChannelNative(JNIEnv *env, jobject object,
+                                          jstring path,
+                                          jstring pattern, jint attr_id) {
 #ifdef HAVE_BLUETOOTH
     LOGV(__FUNCTION__);
     native_data_t *nat = get_native_data(env, object);
@@ -871,28 +331,88 @@
     struct event_loop_native_data_t *eventLoopNat =
             get_EventLoop_native_data(env, eventLoop);
     if (nat && eventLoopNat) {
-        const char *c_address = env->GetStringUTFChars(address, NULL);
-        char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
-        strlcpy(context_address, c_address, BTADDR_SIZE);
-
-        LOGV("... address = %s", c_address);
-        LOGV("... uuid16 = %#X", uuid16);
-
-        bool ret = dbus_func_args_async(env, nat->conn, 20000,  // ms
-                           onGetRemoteServiceChannelResult, context_address,
-                           eventLoopNat,
-                           nat->adapter,
-                           DBUS_CLASS_NAME, "GetRemoteServiceChannel",
-                           DBUS_TYPE_STRING, &c_address,
-                           DBUS_TYPE_UINT16, &uuid16,
+        const char *c_pattern = env->GetStringUTFChars(pattern, NULL);
+        const char *c_path = env->GetStringUTFChars(path, NULL);
+        LOGV("... pattern = %s", c_pattern);
+        LOGV("... attr_id = %#X", attr_id);
+        DBusMessage *reply =
+            dbus_func_args(env, nat->conn, c_path,
+                           DBUS_DEVICE_IFACE, "GetServiceAttributeValue",
+                           DBUS_TYPE_STRING, &c_pattern,
+                           DBUS_TYPE_UINT16, &attr_id,
                            DBUS_TYPE_INVALID);
+        env->ReleaseStringUTFChars(pattern, c_pattern);
+        env->ReleaseStringUTFChars(path, c_path);
+        return reply ? dbus_returns_int32(env, reply) : -1;
+    }
+#endif
+    return -1;
+}
+
+static jboolean cancelDeviceCreationNative(JNIEnv *env, jobject object,
+                                           jstring address) {
+    LOGV(__FUNCTION__);
+    jboolean result = JNI_FALSE;
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat = get_native_data(env, object);
+    if (nat) {
+        const char *c_address = env->GetStringUTFChars(address, NULL);
+        DBusError err;
+        dbus_error_init(&err);
+        LOGV("... address = %s", c_address);
+        DBusMessage *reply =
+            dbus_func_args_timeout(env, nat->conn, -1,
+                                   get_adapter_path(env, object),
+                                   DBUS_ADAPTER_IFACE, "CancelDeviceCreation",
+                                   DBUS_TYPE_STRING, &c_address,
+                                   DBUS_TYPE_INVALID);
         env->ReleaseStringUTFChars(address, c_address);
-        return ret ? JNI_TRUE : JNI_FALSE;
+        if (!reply) {
+            if (dbus_error_is_set(&err)) {
+                LOG_AND_FREE_DBUS_ERROR(&err);
+            } else
+                LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+            return JNI_FALSE;
+        } else {
+            result = JNI_TRUE;
+        }
+        dbus_message_unref(reply);
     }
 #endif
     return JNI_FALSE;
 }
 
+static jboolean removeDeviceNative(JNIEnv *env, jobject object, jstring object_path) {
+    LOGV(__FUNCTION__);
+    jboolean result = JNI_FALSE;
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat = get_native_data(env, object);
+    if (nat) {
+        const char *c_object_path = env->GetStringUTFChars(object_path, NULL);
+        DBusError err;
+        dbus_error_init(&err);
+        DBusMessage *reply =
+            dbus_func_args_error(env, nat->conn, &err,
+                                 get_adapter_path(env, object),
+                                 DBUS_ADAPTER_IFACE, "RemoveDevice",
+                                 DBUS_TYPE_OBJECT_PATH, &c_object_path,
+                                 DBUS_TYPE_INVALID);
+        if (!reply) {
+            if (dbus_error_is_set(&err)) {
+                LOG_AND_FREE_DBUS_ERROR(&err);
+            } else
+                LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+            result = JNI_FALSE;
+        } else {
+            result = JNI_TRUE;
+        }
+        env->ReleaseStringUTFChars(object_path, c_object_path);
+        if (reply) dbus_message_unref(reply);
+    }
+#endif
+    return result;
+}
+
 static jint enableNative(JNIEnv *env, jobject object) {
 #ifdef HAVE_BLUETOOTH
     LOGV(__FUNCTION__);
@@ -972,10 +492,153 @@
     return JNI_FALSE;
 }
 
+static jobjectArray getDevicePropertiesNative(JNIEnv *env, jobject object,
+                                                    jstring path)
+{
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+    native_data_t *nat = get_native_data(env, object);
+    if (nat) {
+        DBusMessage *msg, *reply;
+        DBusError err;
+        dbus_error_init(&err);
+
+        const char *c_path = env->GetStringUTFChars(path, NULL);
+        reply = dbus_func_args_timeout(env,
+                                   nat->conn, -1, c_path,
+                                   DBUS_DEVICE_IFACE, "GetProperties",
+                                   DBUS_TYPE_INVALID);
+        env->ReleaseStringUTFChars(path, c_path);
+
+        if (!reply) {
+            if (dbus_error_is_set(&err)) {
+                LOG_AND_FREE_DBUS_ERROR(&err);
+            } else
+                LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+            return NULL;
+        }
+        DBusMessageIter iter;
+        jobjectArray str_array = NULL;
+        if (dbus_message_iter_init(reply, &iter))
+           str_array =  parse_remote_device_properties(env, &iter);
+        dbus_message_unref(reply);
+        return str_array;
+    }
+#endif
+    return NULL;
+}
+
+static jobjectArray getAdapterPropertiesNative(JNIEnv *env, jobject object) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+    native_data_t *nat = get_native_data(env, object);
+    if (nat) {
+        DBusMessage *msg, *reply;
+        DBusError err;
+        dbus_error_init(&err);
+
+        reply = dbus_func_args_timeout(env,
+                                   nat->conn, -1, get_adapter_path(env, object),
+                                   DBUS_ADAPTER_IFACE, "GetProperties",
+                                   DBUS_TYPE_INVALID);
+        if (!reply) {
+            if (dbus_error_is_set(&err)) {
+                LOG_AND_FREE_DBUS_ERROR(&err);
+            } else
+                LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+            return NULL;
+        }
+        DBusMessageIter iter;
+        jobjectArray str_array = NULL;
+        if (dbus_message_iter_init(reply, &iter))
+            str_array = parse_adapter_properties(env, &iter);
+        dbus_message_unref(reply);
+        return str_array;
+    }
+#endif
+    return NULL;
+}
+
+static jboolean setAdapterPropertyNative(JNIEnv *env, jobject object, jstring key,
+                                         void *value, jint type) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+    native_data_t *nat = get_native_data(env, object);
+    if (nat) {
+        DBusMessage *reply, *msg;
+        DBusMessageIter iter;
+        DBusError err;
+        const char *c_key = env->GetStringUTFChars(key, NULL);
+        dbus_error_init(&err);
+
+        msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
+                                           get_adapter_path(env, object),
+                                           DBUS_ADAPTER_IFACE, "SetProperty");
+        if (!msg) {
+            LOGE("%s: Can't allocate new method call for GetProperties!",
+                  __FUNCTION__);
+            return JNI_FALSE;
+        }
+
+        dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID);
+        dbus_message_iter_init_append(msg, &iter);
+        append_variant(&iter, type, value);
+
+        reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
+        dbus_message_unref(msg);
+
+        env->ReleaseStringUTFChars(key, c_key);
+
+        if (!reply) {
+            if (dbus_error_is_set(&err)) {
+                LOG_AND_FREE_DBUS_ERROR(&err);
+            } else
+                LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+            return JNI_FALSE;
+        }
+        return JNI_TRUE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
+static jboolean setAdapterPropertyStringNative(JNIEnv *env, jobject object, jstring key,
+                                               jstring value) {
+#ifdef HAVE_BLUETOOTH
+    const char *c_value = env->GetStringUTFChars(value, NULL);
+    jboolean ret =  setAdapterPropertyNative(env, object, key, (void *)&c_value, DBUS_TYPE_STRING);
+    env->ReleaseStringUTFChars(value, (char *)c_value);
+    return ret;
+#else
+    return JNI_FALSE;
+#endif
+}
+
+static jboolean setAdapterPropertyIntegerNative(JNIEnv *env, jobject object, jstring key,
+                                               jint value) {
+#ifdef HAVE_BLUETOOTH
+    return setAdapterPropertyNative(env, object, key, (void *)&value, DBUS_TYPE_UINT32);
+#else
+    return JNI_FALSE;
+#endif
+}
+
+static jboolean setAdapterPropertyBooleanNative(JNIEnv *env, jobject object, jstring key,
+                                               jint value) {
+#ifdef HAVE_BLUETOOTH
+    return setAdapterPropertyNative(env, object, key, (void *)&value, DBUS_TYPE_BOOLEAN);
+#else
+    return JNI_FALSE;
+#endif
+}
+
+
 static JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     {"classInitNative", "()V", (void*)classInitNative},
     {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
+    {"setupNativeDataNative", "()Z", (void *)setupNativeDataNative},
+    {"tearDownNativeDataNative", "()Z", (void *)tearDownNativeDataNative},
     {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
     {"getAdapterPathNative", "()Ljava/lang/String;", (void*)getAdapterPathNative},
 
@@ -983,46 +646,25 @@
     {"enableNative", "()I", (void *)enableNative},
     {"disableNative", "()I", (void *)disableNative},
 
-    {"getAddressNative", "()Ljava/lang/String;", (void *)getAddressNative},
-    {"getNameNative", "()Ljava/lang/String;", (void*)getNameNative},
-    {"setNameNative", "(Ljava/lang/String;)Z", (void *)setNameNative},
-    {"getVersionNative", "()Ljava/lang/String;", (void *)getVersionNative},
-    {"getRevisionNative", "()Ljava/lang/String;", (void *)getRevisionNative},
-    {"getManufacturerNative", "()Ljava/lang/String;", (void *)getManufacturerNative},
-    {"getCompanyNative", "()Ljava/lang/String;", (void *)getCompanyNative},
+    {"getAdapterPropertiesNative", "()[Ljava/lang/Object;", (void *)getAdapterPropertiesNative},
+    {"getDevicePropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
+      (void *)getDevicePropertiesNative},
+    {"setAdapterPropertyStringNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
+      (void *)setAdapterPropertyStringNative},
+    {"setAdapterPropertyBooleanNative", "(Ljava/lang/String;I)Z",
+      (void *)setAdapterPropertyBooleanNative},
+    {"setAdapterPropertyIntegerNative", "(Ljava/lang/String;I)Z",
+      (void *)setAdapterPropertyIntegerNative},
 
-    {"getModeNative", "()Ljava/lang/String;", (void *)getModeNative},
-    {"setModeNative", "(Ljava/lang/String;)Z", (void *)setModeNative},
+    {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
+    {"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},
 
-    {"getDiscoverableTimeoutNative", "()I", (void *)getDiscoverableTimeoutNative},
-    {"setDiscoverableTimeoutNative", "(I)Z", (void *)setDiscoverableTimeoutNative},
+    {"createPairedDeviceNative", "(Ljava/lang/String;I)Z", (void *)createPairedDeviceNative},
+    {"cancelDeviceCreationNative", "(Ljava/lang/String;)Z", (void *)cancelDeviceCreationNative},
+    {"removeDeviceNative", "(Ljava/lang/String;)Z", (void *)removeDeviceNative},
+    {"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
+      (void *)getDeviceServiceChannelNative},
 
-    {"startDiscoveryNative", "(Z)Z", (void*)startDiscoveryNative},
-    {"cancelDiscoveryNative", "()Z", (void *)cancelDiscoveryNative},
-    {"startPeriodicDiscoveryNative", "()Z", (void *)startPeriodicDiscoveryNative},
-    {"stopPeriodicDiscoveryNative", "()Z", (void *)stopPeriodicDiscoveryNative},
-    {"isPeriodicDiscoveryNative", "()Z", (void *)isPeriodicDiscoveryNative},
-    {"listRemoteDevicesNative", "()[Ljava/lang/String;", (void *)listRemoteDevicesNative},
-
-    {"listConnectionsNative", "()[Ljava/lang/String;", (void *)listConnectionsNative},
-    {"isConnectedNative", "(Ljava/lang/String;)Z", (void *)isConnectedNative},
-    {"disconnectRemoteDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectRemoteDeviceNative},
-
-    {"createBondingNative", "(Ljava/lang/String;I)Z", (void *)createBondingNative},
-    {"cancelBondingProcessNative", "(Ljava/lang/String;)Z", (void *)cancelBondingProcessNative},
-    {"listBondingsNative", "()[Ljava/lang/String;", (void *)listBondingsNative},
-    {"removeBondingNative", "(Ljava/lang/String;)Z", (void *)removeBondingNative},
-
-    {"getRemoteNameNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteNameNative},
-    {"getRemoteVersionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteVersionNative},
-    {"getRemoteRevisionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteRevisionNative},
-    {"getRemoteClassNative", "(Ljava/lang/String;)I", (void *)getRemoteClassNative},
-    {"getRemoteManufacturerNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteManufacturerNative},
-    {"getRemoteCompanyNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteCompanyNative},
-    {"getRemoteServiceChannelNative", "(Ljava/lang/String;S)Z", (void *)getRemoteServiceChannelNative},
-    {"getRemoteFeaturesNative", "(Ljava/lang/String;)[B", (void *)getRemoteFeaturesNative},
-    {"lastSeenNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastSeenNative},
-    {"lastUsedNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastUsedNative},
     {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
     {"cancelPinNative", "(Ljava/lang/String;I)Z", (void *)cancelPinNative},
 };
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index ad24136..0857cb3 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -39,30 +39,19 @@
 #ifdef HAVE_BLUETOOTH
 static jfieldID field_mNativeData;
 
-static jmethodID method_onModeChanged;
-static jmethodID method_onNameChanged;
-static jmethodID method_onDiscoveryStarted;
-static jmethodID method_onDiscoveryCompleted;
-static jmethodID method_onRemoteDeviceFound;
-static jmethodID method_onRemoteDeviceDisappeared;
-static jmethodID method_onRemoteClassUpdated;
-static jmethodID method_onRemoteNameUpdated;
-static jmethodID method_onRemoteNameFailed;
-static jmethodID method_onRemoteDeviceConnected;
-static jmethodID method_onRemoteDeviceDisconnectRequested;
-static jmethodID method_onRemoteDeviceDisconnected;
-static jmethodID method_onBondingCreated;
-static jmethodID method_onBondingRemoved;
+static jmethodID method_onPropertyChanged;
+static jmethodID method_onDevicePropertyChanged;
+static jmethodID method_onDeviceFound;
+static jmethodID method_onDeviceDisappeared;
+static jmethodID method_onDeviceCreated;
+static jmethodID method_onDeviceRemoved;
 
-static jmethodID method_onCreateBondingResult;
-static jmethodID method_onGetRemoteServiceChannelResult;
+static jmethodID method_onCreatePairedDeviceResult;
+static jmethodID method_onGetDeviceServiceChannelResult;
 
-static jmethodID method_onPasskeyAgentRequest;
-static jmethodID method_onPasskeyAgentCancel;
-static jmethodID method_onAuthAgentAuthorize;
-static jmethodID method_onAuthAgentCancel;
-
-static jmethodID method_onRestartRequired;
+static jmethodID method_onRequestPinCode;
+static jmethodID method_onAgentAuthorize;
+static jmethodID method_onAgentCancel;
 
 typedef event_loop_native_data_t native_data_t;
 
@@ -80,30 +69,26 @@
     LOGV(__FUNCTION__);
 
 #ifdef HAVE_BLUETOOTH
-    method_onModeChanged = env->GetMethodID(clazz, "onModeChanged", "(Ljava/lang/String;)V");
-    method_onNameChanged = env->GetMethodID(clazz, "onNameChanged", "(Ljava/lang/String;)V");
-    method_onDiscoveryStarted = env->GetMethodID(clazz, "onDiscoveryStarted", "()V");
-    method_onDiscoveryCompleted = env->GetMethodID(clazz, "onDiscoveryCompleted", "()V");
-    method_onRemoteDeviceFound = env->GetMethodID(clazz, "onRemoteDeviceFound", "(Ljava/lang/String;IS)V");
-    method_onRemoteDeviceDisappeared = env->GetMethodID(clazz, "onRemoteDeviceDisappeared", "(Ljava/lang/String;)V");
-    method_onRemoteClassUpdated = env->GetMethodID(clazz, "onRemoteClassUpdated", "(Ljava/lang/String;I)V");
-    method_onRemoteNameUpdated = env->GetMethodID(clazz, "onRemoteNameUpdated", "(Ljava/lang/String;Ljava/lang/String;)V");
-    method_onRemoteNameFailed = env->GetMethodID(clazz, "onRemoteNameFailed", "(Ljava/lang/String;)V");
-    method_onRemoteDeviceConnected = env->GetMethodID(clazz, "onRemoteDeviceConnected", "(Ljava/lang/String;)V");
-    method_onRemoteDeviceDisconnectRequested = env->GetMethodID(clazz, "onRemoteDeviceDisconnectRequested", "(Ljava/lang/String;)V");
-    method_onRemoteDeviceDisconnected = env->GetMethodID(clazz, "onRemoteDeviceDisconnected", "(Ljava/lang/String;)V");
-    method_onBondingCreated = env->GetMethodID(clazz, "onBondingCreated", "(Ljava/lang/String;)V");
-    method_onBondingRemoved = env->GetMethodID(clazz, "onBondingRemoved", "(Ljava/lang/String;)V");
+    method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged",
+                                                "([Ljava/lang/String;)V");
+    method_onDevicePropertyChanged = env->GetMethodID(clazz,
+                                                      "onDevicePropertyChanged",
+                                                      "(Ljava/lang/String;[Ljava/lang/String;)V");
+    method_onDeviceFound = env->GetMethodID(clazz, "onDeviceFound",
+                                            "(Ljava/lang/String;[Ljava/lang/String;)V");
+    method_onDeviceDisappeared = env->GetMethodID(clazz, "onDeviceDisappeared",
+                                                  "(Ljava/lang/String;)V");
+    method_onDeviceCreated = env->GetMethodID(clazz, "onDeviceCreated", "(Ljava/lang/String;)V");
+    method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V");
 
-    method_onCreateBondingResult = env->GetMethodID(clazz, "onCreateBondingResult", "(Ljava/lang/String;I)V");
+    method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
+                                                         "(Ljava/lang/String;I)V");
 
-    method_onPasskeyAgentRequest = env->GetMethodID(clazz, "onPasskeyAgentRequest", "(Ljava/lang/String;I)V");
-    method_onPasskeyAgentCancel = env->GetMethodID(clazz, "onPasskeyAgentCancel", "(Ljava/lang/String;)V");
-    method_onAuthAgentAuthorize = env->GetMethodID(clazz, "onAuthAgentAuthorize", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
-    method_onAuthAgentCancel = env->GetMethodID(clazz, "onAuthAgentCancel", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
-    method_onGetRemoteServiceChannelResult = env->GetMethodID(clazz, "onGetRemoteServiceChannelResult", "(Ljava/lang/String;I)V");
-
-    method_onRestartRequired = env->GetMethodID(clazz, "onRestartRequired", "()V");
+    method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
+                                               "(Ljava/lang/String;Ljava/lang/String;)Z");
+    method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
+    method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
+                                               "(Ljava/lang/String;I)V");
 
     field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
 #endif
@@ -154,9 +139,11 @@
 #ifdef HAVE_BLUETOOTH
 static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
                                       void *data);
-static DBusHandlerResult agent_event_filter(DBusConnection *conn,
-                                            DBusMessage *msg,
-                                            void *data);
+DBusHandlerResult agent_event_filter(DBusConnection *conn,
+                                     DBusMessage *msg,
+                                     void *data);
+static int register_agent(native_data_t *nat,
+                          const char *agent_path, const char *capabilities);
 
 static const DBusObjectPathVTable agent_vtable = {
     NULL, agent_event_filter, NULL, NULL, NULL, NULL
@@ -178,11 +165,12 @@
 
 static jboolean setUpEventLoop(native_data_t *nat) {
     LOGV(__FUNCTION__);
-    dbus_threads_init_default();
-    DBusError err;
-    dbus_error_init(&err);
 
     if (nat != NULL && nat->conn != NULL) {
+        dbus_threads_init_default();
+        DBusError err;
+        dbus_error_init(&err);
+
         // Add a filter for all incoming messages
         if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){
             return JNI_FALSE;
@@ -204,108 +192,143 @@
             return JNI_FALSE;
         }
         dbus_bus_add_match(nat->conn,
-                "type='signal',interface='org.bluez.audio.Manager'",
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'",
                 &err);
         if (dbus_error_is_set(&err)) {
             LOG_AND_FREE_DBUS_ERROR(&err);
             return JNI_FALSE;
         }
         dbus_bus_add_match(nat->conn,
-                "type='signal',interface='org.bluez.audio.Device'",
-                &err);
-        if (dbus_error_is_set(&err)) {
-            LOG_AND_FREE_DBUS_ERROR(&err);
-            return JNI_FALSE;
-        }
-        dbus_bus_add_match(nat->conn,
-                "type='signal',interface='org.bluez.audio.Sink'",
+                "type='signal',interface='org.bluez.AudioSink'",
                 &err);
         if (dbus_error_is_set(&err)) {
             LOG_AND_FREE_DBUS_ERROR(&err);
             return JNI_FALSE;
         }
 
-        // Add an object handler for passkey agent method calls
-        const char *path = "/android/bluetooth/Agent";
-        if (!dbus_connection_register_object_path(nat->conn, path,
-                &agent_vtable, nat)) {
-            LOGE("%s: Can't register object path %s for agent!",
-                 __FUNCTION__, path);
+        const char *agent_path = "/android/bluetooth/agent";
+        const char *capabilities = "DisplayYesNo";
+        if (register_agent(nat, agent_path, capabilities) < 0) {
+            dbus_connection_unregister_object_path (nat->conn, agent_path);
             return JNI_FALSE;
         }
-
-        // RegisterDefaultPasskeyAgent() will fail until hcid is up, so keep
-        // trying for 10 seconds.
-        int attempt;
-        for (attempt = 0; attempt < 1000; attempt++) {
-            DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err,
-                    BLUEZ_DBUS_BASE_PATH,
-                    "org.bluez.Security", "RegisterDefaultPasskeyAgent",
-                    DBUS_TYPE_STRING, &path,
-                    DBUS_TYPE_INVALID);
-            if (reply) {
-                // Success
-                dbus_message_unref(reply);
-                LOGV("Registered agent on attempt %d of 1000\n", attempt);
-                break;
-            } else if (dbus_error_has_name(&err,
-                    "org.freedesktop.DBus.Error.ServiceUnknown")) {
-                // hcid is still down, retry
-                dbus_error_free(&err);
-                usleep(10000);  // 10 ms
-            } else {
-                // Some other error we weren't expecting
-                LOG_AND_FREE_DBUS_ERROR(&err);
-                return JNI_FALSE;
-            }
-        }
-        if (attempt == 1000) {
-            LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), "
-                 "is hcid running?");
-            return JNI_FALSE;
-        }
-
-        // Now register the Auth agent
-        DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err,
-                BLUEZ_DBUS_BASE_PATH,
-                "org.bluez.Security", "RegisterDefaultAuthorizationAgent",
-                DBUS_TYPE_STRING, &path,
-                DBUS_TYPE_INVALID);
-        if (!reply) {
-            LOG_AND_FREE_DBUS_ERROR(&err);
-            return JNI_FALSE;
-        }
-
-        dbus_message_unref(reply);
         return JNI_TRUE;
     }
-
     return JNI_FALSE;
 }
 
+
+const char * get_adapter_path(DBusConnection *conn) {
+    DBusMessage *msg, *reply;
+    DBusError err;
+    const char *device_path = NULL;
+    msg = dbus_message_new_method_call("org.bluez", "/",
+          "org.bluez.Manager", "DefaultAdapter");
+    if (!msg) {
+        LOGE("%s: Can't allocate new method call for GetProperties!",
+              __FUNCTION__);
+        return NULL;
+    }
+    dbus_message_append_args(msg, DBUS_TYPE_INVALID);
+
+    dbus_error_init(&err);
+    reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+    dbus_message_unref(msg);
+
+    if (!reply) {
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+        }
+        return NULL;
+    }
+    if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
+                               &device_path, DBUS_TYPE_INVALID)
+                               || !device_path){
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+        }
+        return NULL;
+    }
+    return device_path;
+}
+
+static int register_agent(native_data_t *nat,
+                          const char * agent_path, const char * capabilities)
+{
+    DBusMessage *msg, *reply;
+    DBusError err;
+
+    if (!dbus_connection_register_object_path(nat->conn, agent_path,
+            &agent_vtable, nat)) {
+        LOGE("%s: Can't register object path %s for agent!",
+              __FUNCTION__, agent_path);
+        return -1;
+    }
+
+    nat->adapter = get_adapter_path(nat->conn);
+    msg = dbus_message_new_method_call("org.bluez", nat->adapter,
+          "org.bluez.Adapter", "RegisterAgent");
+    if (!msg) {
+        LOGE("%s: Can't allocate new method call for agent!",
+              __FUNCTION__);
+        return -1;
+    }
+    dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
+                             DBUS_TYPE_STRING, &capabilities,
+                             DBUS_TYPE_INVALID);
+
+    dbus_error_init(&err);
+    reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
+    dbus_message_unref(msg);
+
+    if (!reply) {
+        LOGE("%s: Can't register agent!", __FUNCTION__);
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+        }
+        return -1;
+    }
+
+    dbus_message_unref(reply);
+    dbus_connection_flush(nat->conn);
+
+    return 0;
+}
+
 static void tearDownEventLoop(native_data_t *nat) {
     LOGV(__FUNCTION__);
     if (nat != NULL && nat->conn != NULL) {
 
+        DBusMessage *msg, *reply;
         DBusError err;
         dbus_error_init(&err);
+        const char * agent_path = "/android/bluetooth/agent";
 
-        const char *path = "/android/bluetooth/Agent";
-        DBusMessage *reply =
-            dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH,
-                    "org.bluez.Security", "UnregisterDefaultPasskeyAgent",
-                    DBUS_TYPE_STRING, &path,
-                    DBUS_TYPE_INVALID);
-        if (reply) dbus_message_unref(reply);
+        msg = dbus_message_new_method_call("org.bluez",
+                                           nat->adapter,
+                                           "org.bluez.Adapter",
+                                           "UnregisterAgent");
+        if (msg != NULL) {
+            dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                     DBUS_TYPE_INVALID);
+            reply = dbus_connection_send_with_reply_and_block(nat->conn,
+                                                              msg, -1, &err);
 
-        reply =
-            dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH,
-                    "org.bluez.Security", "UnregisterDefaultAuthorizationAgent",
-                    DBUS_TYPE_STRING, &path,
-                    DBUS_TYPE_INVALID);
-        if (reply) dbus_message_unref(reply);
+            if (!reply) {
+                if (dbus_error_is_set(&err)) {
+                    LOG_AND_FREE_DBUS_ERROR(&err);
+                    dbus_error_free(&err);
+                }
+            } else {
+                dbus_message_unref(reply);
+            }
+            dbus_message_unref(msg);
+        } else {
+             LOGE("%s: Can't create new method call!", __FUNCTION__);
+        }
 
-        dbus_connection_unregister_object_path(nat->conn, path);
+        dbus_connection_flush(nat->conn);
+        dbus_connection_unregister_object_path(nat->conn, agent_path);
 
         dbus_bus_remove_match(nat->conn,
                 "type='signal',interface='org.bluez.audio.Sink'",
@@ -660,267 +683,129 @@
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     }
 
-    LOGV("%s: Received signal %s:%s from %s", __FUNCTION__,
-         dbus_message_get_interface(msg), dbus_message_get_member(msg),
-         dbus_message_get_path(msg));
+    // STOPSHIP: Change to LOGV
+    LOGE("%s: Received signal %s:%s from %s", __FUNCTION__,
+        dbus_message_get_interface(msg), dbus_message_get_member(msg),
+        dbus_message_get_path(msg));
 
     if (dbus_message_is_signal(msg,
                                "org.bluez.Adapter",
-                               "RemoteDeviceFound")) {
+                               "DeviceFound")) {
         char *c_address;
-        int n_class;
-        short n_rssi;
-        if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_address,
-                                  DBUS_TYPE_UINT32, &n_class,
-                                  DBUS_TYPE_INT16, &n_rssi,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... address = %s class = %#X rssi = %hd", c_address, n_class,
-                 n_rssi);
+        DBusMessageIter iter;
+        jobjectArray str_array = NULL;
+        if (dbus_message_iter_init(msg, &iter)) {
+            dbus_message_iter_get_basic(&iter, &c_address);
+            if (dbus_message_iter_next(&iter))
+                str_array =
+                    parse_remote_device_properties(env, &iter);
+        }
+        if (str_array != NULL) {
             env->CallVoidMethod(nat->me,
-                                method_onRemoteDeviceFound,
+                                method_onDeviceFound,
                                 env->NewStringUTF(c_address),
-                                (jint)n_class,
-                                (jshort)n_rssi);
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+                                str_array);
+        } else
+            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
         return DBUS_HANDLER_RESULT_HANDLED;
     } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.Adapter",
-                                      "DiscoveryStarted")) {
-        LOGI("DiscoveryStarted signal received");
-        env->CallVoidMethod(nat->me, method_onDiscoveryStarted);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                    "org.bluez.Adapter",
-                                    "DiscoveryCompleted")) {
-        LOGI("DiscoveryCompleted signal received");
-        env->CallVoidMethod(nat->me, method_onDiscoveryCompleted);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.Adapter",
-                                      "RemoteDeviceDisappeared")) {
+                                     "org.bluez.Adapter",
+                                     "DeviceDisappeared")) {
         char *c_address;
         if (dbus_message_get_args(msg, &err,
                                   DBUS_TYPE_STRING, &c_address,
                                   DBUS_TYPE_INVALID)) {
             LOGV("... address = %s", c_address);
-            env->CallVoidMethod(nat->me, method_onRemoteDeviceDisappeared,
+            env->CallVoidMethod(nat->me, method_onDeviceDisappeared,
                                 env->NewStringUTF(c_address));
         } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
         return DBUS_HANDLER_RESULT_HANDLED;
     } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.Adapter",
-                                      "RemoteClassUpdated")) {
-        char *c_address;
-        int n_class;
+                                     "org.bluez.Adapter",
+                                     "DeviceCreated")) {
+        char *c_object_path;
         if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_address,
-                                  DBUS_TYPE_UINT32, &n_class,
+                                  DBUS_TYPE_OBJECT_PATH, &c_object_path,
                                   DBUS_TYPE_INVALID)) {
-            LOGV("... address = %s", c_address);
-            env->CallVoidMethod(nat->me, method_onRemoteClassUpdated,
-                                env->NewStringUTF(c_address), (jint)n_class);
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.Adapter",
-                                      "RemoteNameUpdated")) {
-        char *c_address;
-        char *c_name;
-        if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_address,
-                                  DBUS_TYPE_STRING, &c_name,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... address = %s, name = %s", c_address, c_name);
+            LOGV("... address = %s", c_object_path);
             env->CallVoidMethod(nat->me,
-                                method_onRemoteNameUpdated,
-                                env->NewStringUTF(c_address),
-                                env->NewStringUTF(c_name));
+                                method_onDeviceCreated,
+                                env->NewStringUTF(c_object_path));
         } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
         return DBUS_HANDLER_RESULT_HANDLED;
     } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.Adapter",
-                                      "RemoteNameFailed")) {
-        char *c_address;
+                                     "org.bluez.Adapter",
+                                     "DeviceRemoved")) {
+        char *c_object_path;
         if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_address,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... address = %s", c_address);
-            env->CallVoidMethod(nat->me,
-                                method_onRemoteNameFailed,
-                                env->NewStringUTF(c_address));
+                                 DBUS_TYPE_OBJECT_PATH, &c_object_path,
+                                 DBUS_TYPE_INVALID)) {
+           LOGV("... Object Path = %s", c_object_path);
+           env->CallVoidMethod(nat->me,
+                               method_onDeviceRemoved,
+                               env->NewStringUTF(c_object_path));
         } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.Adapter",
-                                      "RemoteDeviceConnected")) {
-        char *c_address;
-        if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_address,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... address = %s", c_address);
-            env->CallVoidMethod(nat->me,
-                                method_onRemoteDeviceConnected,
-                                env->NewStringUTF(c_address));
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.Adapter",
-                                      "RemoteDeviceDisconnectRequested")) {
-        char *c_address;
-        if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_address,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... address = %s", c_address);
-            env->CallVoidMethod(nat->me,
-                                method_onRemoteDeviceDisconnectRequested,
-                                env->NewStringUTF(c_address));
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.Adapter",
-                                      "RemoteDeviceDisconnected")) {
-        char *c_address;
-        if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_address,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... address = %s", c_address);
-            env->CallVoidMethod(nat->me,
-                                method_onRemoteDeviceDisconnected,
-                                env->NewStringUTF(c_address));
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.Adapter",
-                                      "BondingCreated")) {
-        char *c_address;
-        if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_address,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... address = %s", c_address);
-            env->CallVoidMethod(nat->me,
-                                method_onBondingCreated,
-                                env->NewStringUTF(c_address));
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.Adapter",
-                                      "BondingRemoved")) {
-        char *c_address;
-        if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_address,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... address = %s", c_address);
-            env->CallVoidMethod(nat->me,
-                                method_onBondingRemoved,
-                                env->NewStringUTF(c_address));
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.Adapter",
-                                      "ModeChanged")) {
-        char *c_mode;
-        if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_mode,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... mode = %s", c_mode);
-            env->CallVoidMethod(nat->me,
-                                method_onModeChanged,
-                                env->NewStringUTF(c_mode));
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.bluez.Adapter",
-                                      "NameChanged")) {
-        char *c_name;
-        if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_name,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... name = %s", c_name);
-            env->CallVoidMethod(nat->me,
-                                method_onNameChanged,
-                                env->NewStringUTF(c_name));
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_signal(msg,
-                                      "org.freedesktop.DBus",
-                                      "NameOwnerChanged")) {
-        char *c_name;
-        char *c_old_owner;
-        char *c_new_owner;
-        if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_name,
-                                  DBUS_TYPE_STRING, &c_old_owner,
-                                  DBUS_TYPE_STRING, &c_new_owner,
-                                  DBUS_TYPE_INVALID)) {
-            LOGV("... name = %s", c_name);
-            LOGV("... old_owner = %s", c_old_owner);
-            LOGV("... new_owner = %s", c_new_owner);
-            if (!strcmp(c_name, "org.bluez") && c_new_owner[0] != '\0') {
-                // New owner of org.bluez. This can only happen when hcid
-                // restarts. Need to restart framework BT services to recover.
-                LOGE("Looks like hcid restarted");
-                env->CallVoidMethod(nat->me, method_onRestartRequired);
-            }
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    }
 
+        return DBUS_HANDLER_RESULT_HANDLED;
+    } else if (dbus_message_is_signal(msg,
+                                      "org.bluez.Adapter",
+                                      "PropertyChanged")) {
+        jobjectArray str_array = parse_adapter_property_change(env, msg);
+        if (str_array != NULL) {
+            /* Check if bluetoothd has (re)started, if so update the path. */
+            jstring property =(jstring) env->GetObjectArrayElement(str_array, 0);
+            const char *c_property = env->GetStringUTFChars(property, NULL);
+            if (!strncmp(c_property, "Powered", strlen("Powered"))) {
+                jstring value =
+                    (jstring) env->GetObjectArrayElement(str_array, 1);
+                const char *c_value = env->GetStringUTFChars(value, NULL);
+                if (!strncmp(c_value, "true", strlen("true")))
+                    nat->adapter = get_adapter_path(nat->conn);
+                env->ReleaseStringUTFChars(value, c_value);
+            }
+            env->ReleaseStringUTFChars(property, c_property);
+
+            env->CallVoidMethod(nat->me,
+                              method_onPropertyChanged,
+                              str_array);
+        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+        return DBUS_HANDLER_RESULT_HANDLED;
+    } else if (dbus_message_is_signal(msg,
+                                      "org.bluez.Device",
+                                      "PropertyChanged")) {
+        jobjectArray str_array = parse_remote_device_property_change(env, msg);
+        if (str_array != NULL) {
+            const char *remote_device_path = dbus_message_get_path(msg);
+            env->CallVoidMethod(nat->me,
+                            method_onDevicePropertyChanged,
+                            env->NewStringUTF(remote_device_path),
+                            str_array);
+        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+        return DBUS_HANDLER_RESULT_HANDLED;
+
+    }
     return a2dp_event_filter(msg, env);
 }
 
 // Called by dbus during WaitForAndDispatchEventNative()
-static DBusHandlerResult agent_event_filter(DBusConnection *conn,
-                                            DBusMessage *msg, void *data) {
+DBusHandlerResult agent_event_filter(DBusConnection *conn,
+                                     DBusMessage *msg, void *data) {
     native_data_t *nat = (native_data_t *)data;
     JNIEnv *env;
-    nat->vm->GetEnv((void**)&env, nat->envVer);
     if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
         LOGV("%s: not interested (not a method call).", __FUNCTION__);
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     }
-    LOGV("%s: Received method %s:%s", __FUNCTION__,
+    LOGI("%s: Received method %s:%s", __FUNCTION__,
          dbus_message_get_interface(msg), dbus_message_get_member(msg));
 
+    if (nat == NULL) return DBUS_HANDLER_RESULT_HANDLED;
+
+    nat->vm->GetEnv((void**)&env, nat->envVer);
     if (dbus_message_is_method_call(msg,
-            "org.bluez.PasskeyAgent", "Request")) {
+            "org.bluez.Agent", "Cancel")) {
 
-        const char *adapter;
-        const char *address;
-        if (!dbus_message_get_args(msg, NULL,
-                                   DBUS_TYPE_STRING, &adapter,
-                                   DBUS_TYPE_STRING, &address,
-                                   DBUS_TYPE_INVALID)) {
-            LOGE("%s: Invalid arguments for Request() method", __FUNCTION__);
-            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-        }
-
-        LOGV("... address = %s", address);
-
-        dbus_message_ref(msg);  // increment refcount because we pass to java
-
-        env->CallVoidMethod(nat->me, method_onPasskeyAgentRequest,
-                            env->NewStringUTF(address), (int)msg);
-
-        return DBUS_HANDLER_RESULT_HANDLED;
-
-    } else if (dbus_message_is_method_call(msg,
-            "org.bluez.PasskeyAgent", "Cancel")) {
-
-        const char *adapter;
-        const char *address;
-        if (!dbus_message_get_args(msg, NULL,
-                                   DBUS_TYPE_STRING, &adapter,
-                                   DBUS_TYPE_STRING, &address,
-                                   DBUS_TYPE_INVALID)) {
-            LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__);
-            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-        }
-
-        LOGV("... address = %s", address);
-
-        env->CallVoidMethod(nat->me, method_onPasskeyAgentCancel,
-                            env->NewStringUTF(address));
+        env->CallVoidMethod(nat->me, method_onAgentCancel);
 
         // reply
         DBusMessage *reply = dbus_message_new_method_return(msg);
@@ -933,41 +818,23 @@
         return DBUS_HANDLER_RESULT_HANDLED;
 
     } else if (dbus_message_is_method_call(msg,
-            "org.bluez.PasskeyAgent", "Release")) {
-        LOGW("We are no longer the passkey agent!");
-
-        // reply
-        DBusMessage *reply = dbus_message_new_method_return(msg);
-        if (!reply) {
-            LOGE("%s: Cannot create message reply\n", __FUNCTION__);
-            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-        }
-        dbus_connection_send(nat->conn, reply, NULL);
-        dbus_message_unref(reply);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    } else if (dbus_message_is_method_call(msg,
-            "org.bluez.AuthorizationAgent", "Authorize")) {
-        const char *adapter;
-        const char *address;
-        const char *service;
+            "org.bluez.Agent", "Authorize")) {
+        char *object_path;
         const char *uuid;
         if (!dbus_message_get_args(msg, NULL,
-                                   DBUS_TYPE_STRING, &adapter,
-                                   DBUS_TYPE_STRING, &address,
-                                   DBUS_TYPE_STRING, &service,
+                                   DBUS_TYPE_OBJECT_PATH, &object_path,
                                    DBUS_TYPE_STRING, &uuid,
                                    DBUS_TYPE_INVALID)) {
             LOGE("%s: Invalid arguments for Authorize() method", __FUNCTION__);
             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
         }
 
-        LOGV("... address = %s", address);
-        LOGV("... service = %s", service);
+        LOGV("... object_path = %s", object_path);
         LOGV("... uuid = %s", uuid);
 
-        bool auth_granted = env->CallBooleanMethod(nat->me,
-                method_onAuthAgentAuthorize, env->NewStringUTF(address),
-                env->NewStringUTF(service), env->NewStringUTF(uuid));
+        bool auth_granted =
+            env->CallBooleanMethod(nat->me, method_onAgentAuthorize,
+                env->NewStringUTF(object_path), env->NewStringUTF(uuid));
 
         // reply
         if (auth_granted) {
@@ -990,43 +857,22 @@
         }
         return DBUS_HANDLER_RESULT_HANDLED;
     } else if (dbus_message_is_method_call(msg,
-            "org.bluez.AuthorizationAgent", "Cancel")) {
-        const char *adapter;
-        const char *address;
-        const char *service;
-        const char *uuid;
+            "org.bluez.Agent", "RequestPinCode")) {
+        char *object_path;
         if (!dbus_message_get_args(msg, NULL,
-                                   DBUS_TYPE_STRING, &adapter,
-                                   DBUS_TYPE_STRING, &address,
-                                   DBUS_TYPE_STRING, &service,
-                                   DBUS_TYPE_STRING, &uuid,
+                                   DBUS_TYPE_OBJECT_PATH, &object_path,
                                    DBUS_TYPE_INVALID)) {
-            LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__);
+            LOGE("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__);
             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
         }
 
-        LOGV("... address = %s", address);
-        LOGV("... service = %s", service);
-        LOGV("... uuid = %s", uuid);
-
-        env->CallVoidMethod(nat->me,
-                method_onAuthAgentCancel, env->NewStringUTF(address),
-                env->NewStringUTF(service), env->NewStringUTF(uuid));
-
-        // reply
-        DBusMessage *reply = dbus_message_new_method_return(msg);
-        if (!reply) {
-            LOGE("%s: Cannot create message reply\n", __FUNCTION__);
-            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-        }
-        dbus_connection_send(nat->conn, reply, NULL);
-        dbus_message_unref(reply);
+        dbus_message_ref(msg);  // increment refcount because we pass to java
+        env->CallVoidMethod(nat->me, method_onRequestPinCode,
+                                       env->NewStringUTF(object_path),
+                                       int(msg));
         return DBUS_HANDLER_RESULT_HANDLED;
-
     } else if (dbus_message_is_method_call(msg,
-            "org.bluez.AuthorizationAgent", "Release")) {
-        LOGW("We are no longer the auth agent!");
-
+                  "org.bluez.Agent", "Release")) {
         // reply
         DBusMessage *reply = dbus_message_new_method_return(msg);
         if (!reply) {
@@ -1037,7 +883,7 @@
         dbus_message_unref(reply);
         return DBUS_HANDLER_RESULT_HANDLED;
     } else {
-        LOGV("... ignored");
+        LOGV("%s:%s is ignored", dbus_message_get_interface(msg), dbus_message_get_member(msg));
     }
 
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -1055,7 +901,7 @@
 #define BOND_RESULT_REMOTE_DEVICE_DOWN 4
 #define BOND_RESULT_DISCOVERY_IN_PROGRESS 5
 
-void onCreateBondingResult(DBusMessage *msg, void *user, void *n) {
+void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) {
     LOGV(__FUNCTION__);
 
     native_data_t *nat = (native_data_t *)n;
@@ -1106,7 +952,7 @@
     }
 
     env->CallVoidMethod(nat->me,
-                        method_onCreateBondingResult,
+                        method_onCreatePairedDeviceResult,
                         env->NewStringUTF(address),
                         result);
 done:
@@ -1114,7 +960,7 @@
     free(user);
 }
 
-void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *n) {
+void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) {
     LOGV(__FUNCTION__);
 
     const char *address = (const char *) user;
@@ -1133,14 +979,13 @@
         !dbus_message_get_args(msg, &err,
                                DBUS_TYPE_INT32, &channel,
                                DBUS_TYPE_INVALID)) {
-        /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */
         LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
         dbus_error_free(&err);
     }
 
 done:
     env->CallVoidMethod(nat->me,
-                        method_onGetRemoteServiceChannelResult,
+                        method_onGetDeviceServiceChannelResult,
                         env->NewStringUTF(address),
                         channel);
     free(user);
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 7325432..f0885fd 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -25,12 +25,12 @@
 #include <stdio.h>
 
 #include <utils/Atomic.h>
-#include <utils/IInterface.h>
-#include <utils/IPCThreadState.h>
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
 #include <utils/Log.h>
-#include <utils/Parcel.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
 
 #include <android_runtime/AndroidRuntime.h>
 
diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h
index 16d993d..495e76a 100644
--- a/core/jni/android_util_Binder.h
+++ b/core/jni/android_util_Binder.h
@@ -15,7 +15,7 @@
 ** limitations under the License.
 */
 
-#include <utils/IBinder.h>
+#include <binder/IBinder.h>
 
 #include "jni.h"
 
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index aee0ed7..770c755 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -18,9 +18,9 @@
 #define LOG_TAG "Process"
 
 #include <utils/Log.h>
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 599360f..31b6519 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -25,7 +25,7 @@
     <!-- Permissions for things that cost money -->
     <!-- ====================================== -->
     <eat-comment />
-    
+
     <!-- Used for permissions that can be used to make the user spend money
          without their direct involvement.  For example, this is the group
          for permissions that allow you to directly place phone calls,
@@ -54,7 +54,7 @@
     <!-- Permissions for accessing messages -->
     <!-- ================================== -->
     <eat-comment />
-    
+
     <!-- Used for permissions that allow an application to send messages
          on behalf of the user or intercept messages being received by the
          user.  This is primarily intended for SMS/MMS messaging, such as
@@ -104,7 +104,7 @@
     <!-- Permissions for accessing personal info (contacts and calendar) -->
     <!-- =============================================================== -->
     <eat-comment />
-    
+
     <!-- Used for permissions that provide access to the user's private data,
          such as contacts, calendar events, e-mail messages, etc.  This includes
          both reading and writing of this data (which should generally be
@@ -158,8 +158,8 @@
         android:description="@string/permdesc_writeCalendar" />
 
     <!-- Allows an application to read the user dictionary. This should
-         really only be required by an IME, or a dictionary editor like 
-         the Settings app. 
+         really only be required by an IME, or a dictionary editor like
+         the Settings app.
          @hide Pending API council approval -->
     <permission android:name="android.permission.READ_USER_DICTIONARY"
         android:permissionGroup="android.permission-group.PERSONAL_INFO"
@@ -195,7 +195,7 @@
     <!-- Permissions for accessing location info -->
     <!-- ======================================= -->
     <eat-comment />
-    
+
     <!-- Used for permissions that allow access to the user's current
          location. -->
     <permission-group android:name="android.permission-group.LOCATION"
@@ -240,7 +240,7 @@
     <!-- Permissions for accessing networks -->
     <!-- ======================================= -->
     <eat-comment />
-    
+
     <!-- Used for permissions that provide access to networking services.  The
          main permission here is internet access, but this is also an
          appropriate group for accessing or modifying any network configuration
@@ -281,7 +281,7 @@
     <!-- Permissions for accessing accounts -->
     <!-- ================================== -->
     <eat-comment />
-    
+
     <!-- Permissions for direct access to Google accounts.
          Note that while right now this is only used for Google accounts,
          we expect in the future to have a more general account management
@@ -302,7 +302,7 @@
     <!-- Permissions for accessing hardware -->
     <!-- ================================== -->
     <eat-comment />
-    
+
     <!-- Used for permissions that provide direct access to the hardware on
          the device.  This includes audio, the camera, vibrator, etc. -->
     <permission-group android:name="android.permission-group.HARDWARE_CONTROLS"
@@ -355,7 +355,7 @@
     <!-- Permissions associated with telephony state -->
     <!-- =========================================== -->
     <eat-comment />
-    
+
     <!-- Used for permissions that are associated with accessing and modifyign
          telephony state: intercepting outgoing calls, reading
          and modifying the phone state.  Note that
@@ -409,7 +409,7 @@
     <!-- Permissions for low-level system interaction -->
     <!-- ============================================ -->
     <eat-comment />
-    
+
     <!-- Group of permissions that are related to system APIs.  Many
          of these are not permissions the user will be expected to understand,
          and such permissions should generally be marked as "normal" protection
@@ -633,7 +633,7 @@
                 android:description="@string/permdesc_writeApnSettings"
                 android:label="@string/permlab_writeApnSettings" />
 
-    <!-- Allows an application to allow access the subscribed feeds 
+    <!-- Allows an application to allow access the subscribed feeds
          ContentProvider. -->
     <permission android:name="android.permission.SUBSCRIBED_FEEDS_READ"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
@@ -645,7 +645,7 @@
         android:label="@string/permlab_subscribedFeedsWrite"
         android:description="@string/permdesc_subscribedFeedsWrite"
         android:protectionLevel="dangerous" />
-        
+
     <!-- Allows applications to change network connectivity state -->
     <permission android:name="android.permission.CHANGE_NETWORK_STATE"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
@@ -696,7 +696,7 @@
     <!-- Permissions for special development tools -->
     <!-- ========================================= -->
     <eat-comment />
-    
+
     <!-- Group of permissions that are related to development features.  These
          are not permissions that should appear in normal applications; they
          protect APIs that are intended only to be used for development
@@ -1033,6 +1033,11 @@
                 android:excludeFromRecents="true">
         </activity>
 
+        <activity android:name="android.accounts.ChooseAccountActivity"
+                android:excludeFromRecents="true"
+                android:exported="true">
+        </activity>
+
         <service android:name="com.android.server.LoadAverageService"
             android:exported="true" />
 
@@ -1045,7 +1050,7 @@
         <receiver android:name="com.android.server.MasterClearReceiver"
             android:permission="android.permission.MASTER_CLEAR" >
             <intent-filter>
-                <action android:name="android.intent.action.GTALK_DATA_MESSAGE_RECEIVED" />
+                <action android:name="android.intent.action.REMOTE_INTENT" />
                 <category android:name="android.intent.category.MASTER_CLEAR" />
             </intent-filter>
         </receiver>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3e93b02..4c84732 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3312,6 +3312,47 @@
         <attr name="configure" format="string" />
     </declare-styleable>
 
+    <!-- =============================== -->
+    <!-- Accounts package class attributes -->
+    <!-- =============================== -->
+
+    <!-- Use <code>account-authenticator</code> as the root tag of the XML resource that
+         describes an account authenticator.
+     -->
+    <declare-styleable name="AccountAuthenticator">
+        <!-- the account type this authenticator handles. -->
+        <attr name="accountType" format="string"/>
+        <!-- the user-visible name of the authenticator. -->
+        <attr name="label"/>
+        <!-- the icon of the authenticator. -->
+        <attr name="icon"/>
+    </declare-styleable>
+
+    <!-- =============================== -->
+    <!-- Accounts package class attributes -->
+    <!-- =============================== -->
+
+    <!-- Use <code>account-authenticator</code> as the root tag of the XML resource that
+         describes an account authenticator.
+     -->
+    <declare-styleable name="SyncAdapter">
+        <!-- the authority of a content provider. -->
+        <attr name="contentAuthority" format="string"/>
+        <attr name="accountType"/>
+    </declare-styleable>
+
+    <!-- =============================== -->
+    <!-- Contacts meta-data attributes -->
+    <!-- =============================== -->
+
+    <declare-styleable name="Icon">
+        <attr name="icon" />
+        <attr name="mimeType" />
+    </declare-styleable>
+
+    <declare-styleable name="IconDefault">
+        <attr name="icon" />
+    </declare-styleable>
 
 </resources>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7215685..f655b27 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -36,6 +36,6 @@
     <integer name="config_longAnimTime">300</integer>
 
     <!-- Flag indicating whether Last Name comes before First Name.
-         This becomes true in Japan, for example.-->
+        This becomes true in Japan, for example.-->
     <bool name="config_lastname_comes_before_firstname">false</bool>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2121669..414382d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -5,16 +5,16 @@
      ***************************************************************
      IMPORTANT NOTE FOR ANYONE MODIFYING THIS FILE
      READ THIS BEFORE YOU MAKE ANY CHANGES
-     
+
      This file defines the binary compatibility for resources.  As such,
      you must be very careful when making changes here, or you will
      completely break backwards compatibility with old applications.
-     
+
      To avoid breaking compatibility, all new resources must be placed
      at the end of the list of resources of the same type.  Placing a resource
      in the middle of type will cause all following resources to be
      assigned new resource numbers, breaking compatibility.
-     
+
      ***************************************************************
      *************************************************************** -->
 <resources>
@@ -22,7 +22,7 @@
   <!-- We don't want to publish private symbols in android.R as part of the
        SDK.  Instead, put them here. -->
   <private-symbols package="com.android.internal" />
-    
+
   <!-- AndroidManifest.xml attributes. -->
   <eat-comment />
 
@@ -569,10 +569,10 @@
   <public type="attr" name="lineSpacingExtra" id="0x01010217" />
   <public type="attr" name="lineSpacingMultiplier" id="0x01010218" />
   <public type="attr" name="listChoiceIndicatorSingle" id="0x01010219" />
-  <public type="attr" name="listChoiceIndicatorMultiple" id="0x0101021a" />    
+  <public type="attr" name="listChoiceIndicatorMultiple" id="0x0101021a" />
   <public type="attr" name="versionCode" id="0x0101021b" />
   <public type="attr" name="versionName" id="0x0101021c" />
-  
+
   <public type="id" name="background" id="0x01020000" />
   <public type="id" name="checkbox" id="0x01020001" />
   <public type="id" name="content" id="0x01020002" />
@@ -601,7 +601,7 @@
   <public type="id" name="button1" id="0x01020019" />
   <public type="id" name="button2" id="0x0102001a" />
   <public type="id" name="button3" id="0x0102001b" />
-  
+
   <public type="style" name="Animation" id="0x01030000" />
   <public type="style" name="Animation.Activity" id="0x01030001" />
   <public type="style" name="Animation.Dialog" id="0x01030002" />
@@ -748,7 +748,7 @@
   <public type="drawable" name="btn_plus" id="0x01080008" />
   <public type="drawable" name="btn_radio" id="0x01080009" />
   <public type="drawable" name="btn_star" id="0x0108000a" />
-  <public type="drawable" name="btn_star_big_off" id="0x0108000b" /> 
+  <public type="drawable" name="btn_star_big_off" id="0x0108000b" />
   <public type="drawable" name="btn_star_big_on" id="0x0108000c" />
   <public type="drawable" name="button_onoff_indicator_on" id="0x0108000d" />
   <public type="drawable" name="button_onoff_indicator_off" id="0x0108000e" />
@@ -916,7 +916,7 @@
   <public type="layout" name="select_dialog_item" id="0x01090011" />
   <public type="layout" name="select_dialog_singlechoice" id="0x01090012" />
   <public type="layout" name="select_dialog_multichoice" id="0x01090013" />
-  
+
   <public type="anim" name="fade_in" id="0x010a0000" />
   <public type="anim" name="fade_out" id="0x010a0001" />
   <public type="anim" name="slide_in_left" id="0x010a0002" />
@@ -929,9 +929,9 @@
      Resources added in version 2 of the platform.
      =============================================================== -->
   <eat-comment />
-  
+
   <public type="attr" name="marqueeRepeatLimit" id="0x0101021d" />
-  
+
 <!-- ===============================================================
      Resources added in version 3 of the platform.
      =============================================================== -->
@@ -1062,7 +1062,7 @@
   <public type="id" name="stopSelectingText" id="0x01020029" />
   <!-- Menu ID to perform a "add to dictionary" operation. -->
   <public type="id" name="addToDictionary" id="0x0102002a" />
-  
+
   <public type="style" name="Theme.InputMethod" id="0x01030054" />
   <public type="style" name="Theme.NoDisplay" id="0x01030055" />
   <public type="style" name="Animation.InputMethod" id="0x01030056" />
@@ -1070,7 +1070,7 @@
   <public type="style" name="ButtonBar" id="0x01030058" />
   <public type="style" name="Theme.Panel" id="0x01030059" />
   <public type="style" name="Theme.Light.Panel" id="0x0103005a" />
-  
+
   <public type="string" name="dialog_alert_title" id="0x01040014" />
   <public type="string" name="VideoView_error_text_invalid_progressive_playback" id="0x01040015" />
 
@@ -1081,7 +1081,7 @@
 
   <!--  Drawable to use as a background for a taller version of the titlebar -->
   <public type="drawable" name="title_bar_tall" id="0x010800a6" />
-  
+
   <public type="integer" name="config_shortAnimTime" id="0x010e0000" />
   <public type="integer" name="config_mediumAnimTime" id="0x010e0001" />
   <public type="integer" name="config_longAnimTime" id="0x010e0002" />
@@ -1141,21 +1141,31 @@
 
   <public-padding type="array" name="donut_resource_pad" end="0x01070010" />
 
-  <public type="drawable" name="stat_sys_vp_phone_call" />
-  <public type="drawable" name="stat_sys_vp_phone_call_on_hold" />
+  <public-padding type="anim" name="donut_resource_pad" end="0x010a0020" />
   
-  <public-padding type="drawable" name="donut_resource_pad" end="0x010800d0" />
-  
-  <public-padding type="layout" name="donut_resource_pad" end="0x01090020" />
+  <public-padding type="integer" name="donut_resource_pad" end="0x010e0010" />
   
   <public type="anim" name="anticipate_interpolator" />
   <public type="anim" name="overshoot_interpolator" />
   <public type="anim" name="anticipate_overshoot_interpolator" />
   <public type="anim" name="bounce_interpolator" />
   <public type="anim" name="linear_interpolator" />
+  
+  <public-padding type="drawable" name="donut_resource_pad" end="0x010800d0" />
+  
+  <public-padding type="layout" name="donut_resource_pad" end="0x01090020" />
+  
+  
+<!-- ===============================================================
+     Resources added in Eclair.
+     =============================================================== -->
+  <eat-comment />
 
-  <public-padding type="anim" name="donut_resource_pad" end="0x010a0020" />
+  <public type="attr" name="accountType" />
+  <public type="attr" name="contentAuthority" />
+
+  <public type="drawable" name="stat_sys_vp_phone_call" />
+  <public type="drawable" name="stat_sys_vp_phone_call_on_hold" />
   
-  <public-padding type="integer" name="donut_resource_pad" end="0x010e0010" />
-  
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1ce9c76..eeafbe6 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -134,7 +134,7 @@
     <string name="RestrictedOnNormal">Voice/SMS service is blocked.</string>
     <!-- Displayed to tell the user that all voice service is blocked by access control. -->
     <string name="RestrictedOnAll">All voice/SMS services are blocked.</string>
-    
+
     <!-- Mappings between TS 27.007 +CFCC/+CLCK "service classes" and human-readable strings--> <skip />
     <!-- Example: Service was enabled for: Voice, Data -->
     <string name="serviceClassVoice">Voice</string>
@@ -231,6 +231,13 @@
     <!-- Displayed a toast that a certificate is saved in the keystore -->
     <string name="certificateSaved">The certificate is saved in the system\'s key store.</string>
 
+    <!-- Account notifications --> <skip />
+    <!-- A notification is shown when the AccountManager is unable to
+    supply an auth token without prompting the user to re-enter the
+    password.  This is the text that will scroll through the
+    notification bar (will be seen by the user as he uses another application). -->
+    <string name="notification_title">Sign-in error</string>
+
     <!-- Sync notifications --> <skip />
     <!-- A notification is shown when there is a sync error.  This is the text that will scroll through the notification bar (will be seen by the user as he uses another application). -->
     <string name="contentServiceSync">Sync</string>
@@ -301,7 +308,7 @@
 
     <!-- Label for the Android system components when they are shown to the user. -->
     <string name="android_system_label">Android System</string>
-    
+
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_costMoney">Services that cost you money</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
@@ -802,7 +809,7 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readFrameBuffer">read frame buffer</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readFrameBuffer">Allows application to use
+    <string name="permdesc_readFrameBuffer">Allows application to 
         read the content of the frame buffer.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -1111,9 +1118,6 @@
         <item>Custom</item>
     </string-array>
 
-    <!-- String which means the type "mobile phone". -->
-    <string name="mobileEmailTypeName">Mobile</string>
-
     <!-- The order of these is important, don't reorder without changing Contacts.java --> <skip />
     <!-- Postal address types from android.provider.Contacts. This could be used when adding a new address for a contact, for example. -->
     <string-array name="postalAddressTypes">
@@ -1321,7 +1325,7 @@
 
     <!-- Do not translate.  WebView User Agent string -->
     <string name="web_user_agent"><xliff:g id="x">Mozilla/5.0 (Linux; U; Android %s)
-        AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1</xliff:g></string>
+        AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17</xliff:g></string>
 
     <!-- Title for a JavaScript dialog. "The page at <url of current page> says:" -->
     <string name="js_dialog_title">The page at \'<xliff:g id="title">%s</xliff:g>\' says:</string>
@@ -1377,8 +1381,8 @@
     <string name="menu_delete_shortcut_label">delete</string>
 
     <!-- Strings used for search bar --><skip />
-    
-    <!-- This is the default button label in the system-wide search UI. 
+
+    <!-- This is the default button label in the system-wide search UI.
          It is also used by the home screen's search "widget". It should be short -->
     <string name="search_go">Search</string>
 
@@ -1434,7 +1438,7 @@
         <item quantity="one">tomorrow</item>
         <item quantity="other">in <xliff:g id="count">%d</xliff:g> days</item>
     </plurals>
-    
+
     <!-- This is used to express that something occurred some number of abbreviated seconds in the past (e.g., 5 secs ago). -->
     <plurals name="abbrev_num_seconds_ago">
         <item quantity="one">1 sec ago</item>
@@ -1753,7 +1757,7 @@
     <string name="usb_storage_button_mount">Mount</string>
     <!-- See USB_STORAGE.   This is the button text to ignore the plugging in of the phone.. -->
     <string name="usb_storage_button_unmount">Don\'t mount</string>
-    <!-- See USB_STORAGE_DIALOG.  If there was an error mounting, this is the text. --> 
+    <!-- See USB_STORAGE_DIALOG.  If there was an error mounting, this is the text. -->
     <string name="usb_storage_error_message">There is a problem using your SD card for USB storage.</string>
     <!-- USB_STORAGE: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across.  This is the title -->
     <string name="usb_storage_notification_title">USB connected</string>
@@ -1774,7 +1778,7 @@
     <string name="usb_storage_stop_button_mount">Turn Off</string>
     <!-- See USB_STORAGE_STOP.   This is the button text to cancel stoping usb storage. -->
     <string name="usb_storage_stop_button_unmount">Cancel</string>
-    <!-- See USB_STORAGE_STOP_DIALOG.  If there was an error stopping, this is the text. --> 
+    <!-- See USB_STORAGE_STOP_DIALOG.  If there was an error stopping, this is the text. -->
     <string name="usb_storage_stop_error_message">We've encountered a problem turning off USB storage. Check to make sure you have unmounted the USB host, then try again.</string>
 
     <!-- External media format dialog strings -->
@@ -1800,10 +1804,10 @@
 
     <!-- Title of the pop-up dialog in which the user switches input method components. -->
     <string name="select_input_method">Select Input Method</string>
-    
+
     <string name="fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
     <string name="fast_scroll_numeric_alphabet">\u00200123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
-    
+
     <string name="candidates_style"><u>candidates</u></string>
 
     <!-- External media notification strings -->
@@ -1850,23 +1854,23 @@
 
     <!-- Long label for a button on a full-screen input method for the "Go" action. -->
     <string name="ime_action_go">Go</string>
-    
+
     <!-- Long label for a button on a full-screen input method for the "Search" action. -->
     <string name="ime_action_search">Search</string>
-    
+
     <!-- Long label for a button on a full-screen input method for the "Send" action. -->
     <string name="ime_action_send">Send</string>
-    
+
     <!-- Long label for a button on a full-screen input method for the "Next" action. -->
     <string name="ime_action_next">Next</string>
-    
+
     <!-- Long label for a button on a full-screen input method for the "Done" action. -->
     <string name="ime_action_done">Done</string>
-    
+
     <!-- Long label for a button on a full-screen input method for an unknown action. -->
     <string name="ime_action_default">Execute</string>
-    
-    <!-- Strings for search suggestions. These are going here because they are referenced by both 
+
+    <!-- Strings for search suggestions. These are going here because they are referenced by both
          ContactsProvider and GoogleContactsProvider -->
     <skip />
 
@@ -1877,8 +1881,11 @@
     <!-- This string appears (on two lines) when you type a number into contacts search, to let you create a contact whose phone number is the number you typed.  The first line will be in bigger type than the second. -->
     <string name="create_contact_using">Create contact\nusing <xliff:g id="number" example="555">%s</xliff:g></string>
 
-    <!-- This string array should be overridden by the manufacture to present a list of carrier-id,locale pairs.  This is used at startup to set a default locale by checking the system property ro.carrier for the carrier-id and searching through this array -->
-    <string-array translatable="false" name="carrier_locales">
+    <!-- This string array should be overridden by the manufacture to present a list of carrier-id,locale,wifi-channel sets.  This is used at startup to set system defaults by checking the system property ro.carrier for the carrier-id and searching through this array -->
+    <!-- An Array of [[Carrier-ID]                     -->
+    <!--              [default-locale]                 -->
+    <!--              [default-wifi-allowed-channels]] -->
+    <string-array translatable="false" name="carrier_properties">
     </string-array>
 
      <!-- Title for the selected state of a CompoundButton. -->
diff --git a/docs/html/guide/developing/tools/ddms.jd b/docs/html/guide/developing/tools/ddms.jd
index fa04216..f55940d 100644
--- a/docs/html/guide/developing/tools/ddms.jd
+++ b/docs/html/guide/developing/tools/ddms.jd
@@ -1,7 +1,7 @@
-page.title=Using Dalvik Debug Monitor Service (DDMS)
+page.title=Using the Dalvik Debug Monitor
 @jd:body
 
-<p>Android ships with a debugging tool called the Dalvik Debug Monitor Service (DDMS),
+<p>Android ships with a debugging tool called the Dalvik Debug Monitor Server (DDMS),
     which provides port-forwarding services, screen capture on the device, thread
     and heap information on the device, logcat, process, and radio state information,
     incoming call and SMS spoofing, location data spoofing, and more. This page
@@ -106,7 +106,7 @@
         </ul>
     </li>
     <li> <strong>utime</strong> - cumulative time spent executing user code, in &quot;jiffies&quot; (usually
-        10ms). Only available under Linux. </li>
+        10ms). </li>
     <li> <strong>stime</strong> - cumulative time spent executing system code, in &quot;jiffies&quot;  (usually
         10ms). </li>
     <li> <strong>Name</strong> - the name of the thread</li>
@@ -214,14 +214,15 @@
 
 <h2 id="screen-capture">Screen Capture</h2>
 <p>You can capture screen images on the device or emulator by selecting <strong>Device</strong>
-    &gt; <strong>Screen capture...</strong> in the menu bar, or press CTRL-S.</p>
+    &gt; <strong>Screen capture...</strong> in the menu bar, or press CTRL-S.
+	Be sure to select a device first.</p>
 
 <h2 id="exploring-processes">Exploring Processes</h2>
 <p>You can see the output of <code>ps -x</code> for a specific VM by selecting <strong>Device</strong>
     &gt; <strong>Show process status</strong>... in the menu bar.</p>
 
 <h2 id="cause-a-gc-to-occur">Cause a GC to Occur</h2>
-<p>Cause garbage collection to occury by pressing the trash can button on the toolbar. </p>
+<p>Cause garbage collection to occur in the selected application by pressing the trash can button on the toolbar. </p>
 
 <h2 id="running-dumpsys-and-dumpstate">Running Dumpsys and Dumpstate on the Device (logcat)<a name="logcat" id="logcat"></a> </h2>
 <ul>
@@ -239,7 +240,7 @@
 
 <h2 id="stop-a-vitrual-machine">Stop a Virtual Machine </h2>
 <p>You can stop a virtual machine by selecting <strong>Actions</strong> &gt; <strong>Halt
-VM</strong>. Pressing this  button causes the VM to call <code>System.exit(1)</code>.</p>
+VM</strong>. Pressing this button causes the VM to call <code>Runtime.halt(1)</code>.</p>
 
 <h2 id="known-issues" style="color:#FF0000">Known issues with DDMS </h2>
 <p>DDMS has the following known limitations:</p>
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index e5a9aab..2a39987 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -18,6 +18,7 @@
 
 import android.content.res.AssetManager;
 import android.content.res.Resources;
+import android.os.MemoryFile;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 
@@ -435,6 +436,17 @@
      * @return the decoded bitmap, or null
      */
     public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
+        try {
+            if (MemoryFile.isMemoryFile(fd)) {
+                int mappedlength = MemoryFile.getMappedSize(fd);
+                MemoryFile file = new MemoryFile(fd, mappedlength, "r");
+                InputStream is = file.getInputStream();
+                return decodeStream(is, outPadding, opts);
+            }
+        } catch (IOException ex) {
+            // invalid filedescriptor, no need to call nativeDecodeFileDescriptor()
+            return null;
+        }
         return nativeDecodeFileDescriptor(fd, outPadding, opts);
     }
 
@@ -447,7 +459,7 @@
      * @return the decoded bitmap, or null
      */
     public static Bitmap decodeFileDescriptor(FileDescriptor fd) {
-        return nativeDecodeFileDescriptor(fd, null, null);
+        return decodeFileDescriptor(fd, null, null);
     }
 
     private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h
index 78bef91..99ab2f0 100644
--- a/include/android_runtime/AndroidRuntime.h
+++ b/include/android_runtime/AndroidRuntime.h
@@ -20,7 +20,7 @@
 #define _RUNTIME_ANDROID_RUNTIME_H
 
 #include <utils/Errors.h>
-#include <utils/IBinder.h>
+#include <binder/IBinder.h>
 #include <utils/String8.h>
 #include <utils/String16.h>
 #include <utils/Vector.h>
@@ -98,6 +98,7 @@
 
 private:
     static int startReg(JNIEnv* env);
+    int startVm(JavaVM** pJavaVM, JNIEnv** pEnv);
 
     Vector<JavaVMOption> mOptions;
 
diff --git a/include/utils/Binder.h b/include/binder/Binder.h
similarity index 96%
rename from include/utils/Binder.h
rename to include/binder/Binder.h
index b5b8d98..47b2bb9 100644
--- a/include/utils/Binder.h
+++ b/include/binder/Binder.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_BINDER_H
 #define ANDROID_BINDER_H
 
-#include <utils/IBinder.h>
+#include <binder/IBinder.h>
 
 // ---------------------------------------------------------------------------
 namespace android {
@@ -27,7 +27,7 @@
 public:
                         BBinder();
 
-    virtual String16    getInterfaceDescriptor() const;
+    virtual const String16& getInterfaceDescriptor() const;
     virtual bool        isBinderAlive() const;
     virtual status_t    pingBinder();
     virtual status_t    dump(int fd, const Vector<String16>& args);
@@ -71,6 +71,7 @@
 
             Extras*     mExtras;
             void*       mReserved0;
+    static  String16    sEmptyDescriptor;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/include/utils/BpBinder.h b/include/binder/BpBinder.h
similarity index 94%
rename from include/utils/BpBinder.h
rename to include/binder/BpBinder.h
index 7b96e29..7ef93aa 100644
--- a/include/utils/BpBinder.h
+++ b/include/binder/BpBinder.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_BPBINDER_H
 #define ANDROID_BPBINDER_H
 
-#include <utils/IBinder.h>
+#include <binder/IBinder.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
 
@@ -31,7 +31,7 @@
 
     inline  int32_t     handle() const { return mHandle; }
 
-    virtual String16    getInterfaceDescriptor() const;
+    virtual const String16&    getInterfaceDescriptor() const;
     virtual bool        isBinderAlive() const;
     virtual status_t    pingBinder();
     virtual status_t    dump(int fd, const Vector<String16>& args);
@@ -106,6 +106,7 @@
     };
 
             void                reportOneDeath(const Obituary& obit);
+            bool                isDescriptorCached() const;
 
     mutable Mutex               mLock;
             volatile int32_t    mAlive;
@@ -113,6 +114,7 @@
             Vector<Obituary>*   mObituaries;
             ObjectManager       mObjects;
             Parcel*             mConstantData;
+    mutable String16            mDescriptorCache;
 };
 
 }; // namespace android
diff --git a/include/utils/IBinder.h b/include/binder/IBinder.h
similarity index 97%
rename from include/utils/IBinder.h
rename to include/binder/IBinder.h
index 7370330..884b5c1 100644
--- a/include/utils/IBinder.h
+++ b/include/binder/IBinder.h
@@ -56,7 +56,7 @@
         FLAG_ONEWAY             = 0x00000001
     };
 
-    inline                  IBinder() { }
+                          IBinder();
 
     /**
      * Check if this IBinder implements the interface named by
@@ -69,7 +69,7 @@
      * Return the canonical name of the interface provided by this IBinder
      * object.
      */
-    virtual String16        getInterfaceDescriptor() const = 0;
+    virtual const String16& getInterfaceDescriptor() const = 0;
 
     virtual bool            isBinderAlive() const = 0;
     virtual status_t        pingBinder() = 0;
@@ -147,7 +147,7 @@
     virtual BpBinder*       remoteBinder();
 
 protected:
-    inline virtual          ~IBinder() { }
+    virtual          ~IBinder();
 
 private:
 };
diff --git a/include/utils/IInterface.h b/include/binder/IInterface.h
similarity index 82%
rename from include/utils/IInterface.h
rename to include/binder/IInterface.h
index 959722a..273d922 100644
--- a/include/utils/IInterface.h
+++ b/include/binder/IInterface.h
@@ -18,7 +18,7 @@
 #ifndef ANDROID_IINTERFACE_H
 #define ANDROID_IINTERFACE_H
 
-#include <utils/Binder.h>
+#include <binder/Binder.h>
 
 namespace android {
 
@@ -27,10 +27,12 @@
 class IInterface : public virtual RefBase
 {
 public:
+            IInterface();
             sp<IBinder>         asBinder();
             sp<const IBinder>   asBinder() const;
-
+            
 protected:
+    virtual                     ~IInterface();
     virtual IBinder*            onAsBinder() = 0;
 };
 
@@ -49,7 +51,7 @@
 {
 public:
     virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);
-    virtual String16            getInterfaceDescriptor() const;
+    virtual const String16&     getInterfaceDescriptor() const;
 
 protected:
     virtual IBinder*            onAsBinder();
@@ -72,11 +74,14 @@
 #define DECLARE_META_INTERFACE(INTERFACE)                               \
     static const String16 descriptor;                                   \
     static sp<I##INTERFACE> asInterface(const sp<IBinder>& obj);        \
-    virtual String16 getInterfaceDescriptor() const;                    \
+    virtual const String16& getInterfaceDescriptor() const;             \
+    I##INTERFACE();                                                     \
+    virtual ~I##INTERFACE();                                            \
+
 
 #define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
     const String16 I##INTERFACE::descriptor(NAME);                      \
-    String16 I##INTERFACE::getInterfaceDescriptor() const {             \
+    const String16& I##INTERFACE::getInterfaceDescriptor() const {      \
         return I##INTERFACE::descriptor;                                \
     }                                                                   \
     sp<I##INTERFACE> I##INTERFACE::asInterface(const sp<IBinder>& obj)  \
@@ -92,9 +97,16 @@
         }                                                               \
         return intr;                                                    \
     }                                                                   \
+    I##INTERFACE::I##INTERFACE() { }                                    \
+    I##INTERFACE::~I##INTERFACE() { }                                   \
+
+
+#define CHECK_INTERFACE(interface, data, reply)                         \
+    if (!data.checkInterface(this)) { return PERMISSION_DENIED; }       \
+
 
 // ----------------------------------------------------------------------
-// No user-servicable parts after this...
+// No user-serviceable parts after this...
 
 template<typename INTERFACE>
 inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
@@ -105,7 +117,7 @@
 }
 
 template<typename INTERFACE>
-inline String16 BnInterface<INTERFACE>::getInterfaceDescriptor() const
+inline const String16& BnInterface<INTERFACE>::getInterfaceDescriptor() const
 {
     return INTERFACE::getInterfaceDescriptor();
 }
diff --git a/include/utils/IMemory.h b/include/binder/IMemory.h
similarity index 94%
rename from include/utils/IMemory.h
rename to include/binder/IMemory.h
index 35a3fd7..ae042cb 100644
--- a/include/utils/IMemory.h
+++ b/include/binder/IMemory.h
@@ -23,7 +23,7 @@
 
 #include <utils/RefBase.h>
 #include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
 
 namespace android {
 
@@ -59,6 +59,10 @@
             const Parcel& data,
             Parcel* reply,
             uint32_t flags = 0);
+    
+    BnMemoryHeap();
+protected:
+    virtual ~BnMemoryHeap();
 };
 
 // ----------------------------------------------------------------------------
@@ -85,6 +89,10 @@
             const Parcel& data,
             Parcel* reply,
             uint32_t flags = 0);
+
+    BnMemory();
+protected:
+    virtual ~BnMemory();
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/utils/IPCThreadState.h b/include/binder/IPCThreadState.h
similarity index 98%
rename from include/utils/IPCThreadState.h
rename to include/binder/IPCThreadState.h
index 0490fd3..78306b2 100644
--- a/include/utils/IPCThreadState.h
+++ b/include/binder/IPCThreadState.h
@@ -18,8 +18,8 @@
 #define ANDROID_IPC_THREAD_STATE_H
 
 #include <utils/Errors.h>
-#include <utils/Parcel.h>
-#include <utils/ProcessState.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
 #include <utils/Vector.h>
 
 #ifdef HAVE_WIN32_PROC
diff --git a/include/utils/IPermissionController.h b/include/binder/IPermissionController.h
similarity index 97%
rename from include/utils/IPermissionController.h
rename to include/binder/IPermissionController.h
index cb1dd34..f9d371b 100644
--- a/include/utils/IPermissionController.h
+++ b/include/binder/IPermissionController.h
@@ -18,7 +18,7 @@
 #ifndef ANDROID_IPERMISSION_CONTROLLER_H
 #define ANDROID_IPERMISSION_CONTROLLER_H
 
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
 
 namespace android {
 
diff --git a/include/utils/IServiceManager.h b/include/binder/IServiceManager.h
similarity index 97%
rename from include/utils/IServiceManager.h
rename to include/binder/IServiceManager.h
index e3d99fe..ea149dd 100644
--- a/include/utils/IServiceManager.h
+++ b/include/binder/IServiceManager.h
@@ -18,8 +18,8 @@
 #ifndef ANDROID_ISERVICE_MANAGER_H
 #define ANDROID_ISERVICE_MANAGER_H
 
-#include <utils/IInterface.h>
-#include <utils/IPermissionController.h>
+#include <binder/IInterface.h>
+#include <binder/IPermissionController.h>
 #include <utils/Vector.h>
 #include <utils/String16.h>
 
diff --git a/include/utils/MemoryBase.h b/include/binder/MemoryBase.h
similarity index 97%
rename from include/utils/MemoryBase.h
rename to include/binder/MemoryBase.h
index eb5a9d2..463e26d 100644
--- a/include/utils/MemoryBase.h
+++ b/include/binder/MemoryBase.h
@@ -20,7 +20,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 
 
 namespace android {
diff --git a/include/utils/MemoryDealer.h b/include/binder/MemoryDealer.h
similarity index 96%
rename from include/utils/MemoryDealer.h
rename to include/binder/MemoryDealer.h
index 454b627..d057556 100644
--- a/include/utils/MemoryDealer.h
+++ b/include/binder/MemoryDealer.h
@@ -21,9 +21,9 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 #include <utils/threads.h>
-#include <utils/MemoryHeapBase.h>
+#include <binder/MemoryHeapBase.h>
 
 namespace android {
 // ----------------------------------------------------------------------------
@@ -39,6 +39,10 @@
 public:
     // all values must be page-aligned
     virtual sp<IMemory> mapMemory(size_t offset, size_t size) = 0;
+
+    HeapInterface();
+protected:
+    virtual ~HeapInterface();
 };
 
 // ----------------------------------------------------------------------------
@@ -61,6 +65,10 @@
     virtual void        dump(const char* what, uint32_t flags = 0) const = 0;
     virtual void        dump(String8& res,
             const char* what, uint32_t flags = 0) const = 0;
+
+    AllocatorInterface();
+protected:
+    virtual ~AllocatorInterface();
 };
 
 // ----------------------------------------------------------------------------
@@ -71,6 +79,7 @@
 class SharedHeap : public HeapInterface, public MemoryHeapBase
 {
 public:
+                        SharedHeap();
                         SharedHeap(size_t size, uint32_t flags = 0, char const * name = NULL);
     virtual             ~SharedHeap();
     virtual sp<IMemory> mapMemory(size_t offset, size_t size);
diff --git a/include/utils/MemoryHeapBase.h b/include/binder/MemoryHeapBase.h
similarity index 98%
rename from include/utils/MemoryHeapBase.h
rename to include/binder/MemoryHeapBase.h
index 574acf4..83c7283 100644
--- a/include/utils/MemoryHeapBase.h
+++ b/include/binder/MemoryHeapBase.h
@@ -20,7 +20,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 
 
 namespace android {
diff --git a/include/utils/MemoryHeapPmem.h b/include/binder/MemoryHeapPmem.h
similarity index 95%
rename from include/utils/MemoryHeapPmem.h
rename to include/binder/MemoryHeapPmem.h
index 60335ad..dbf26ff 100644
--- a/include/utils/MemoryHeapPmem.h
+++ b/include/binder/MemoryHeapPmem.h
@@ -20,9 +20,9 @@
 #include <stdlib.h>
 #include <stdint.h>
 
-#include <utils/MemoryDealer.h>
-#include <utils/MemoryHeapBase.h>
-#include <utils/IMemory.h>
+#include <binder/MemoryDealer.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/IMemory.h>
 #include <utils/SortedVector.h>
 
 namespace android {
diff --git a/include/utils/Parcel.h b/include/binder/Parcel.h
similarity index 98%
rename from include/utils/Parcel.h
rename to include/binder/Parcel.h
index af1490a..58c2d9a 100644
--- a/include/utils/Parcel.h
+++ b/include/binder/Parcel.h
@@ -57,7 +57,8 @@
 
     status_t            writeInterfaceToken(const String16& interface);
     bool                enforceInterface(const String16& interface) const;
-            
+    bool                checkInterface(IBinder*) const;    
+
     void                freeData();
 
     const size_t*       objects() const;
@@ -147,7 +148,7 @@
                                             release_func relFunc, void* relCookie);
     
     void                print(TextOutput& to, uint32_t flags = 0) const;
-    
+        
 private:
                         Parcel(const Parcel& o);
     Parcel&             operator=(const Parcel& o);
diff --git a/include/utils/ProcessState.h b/include/binder/ProcessState.h
similarity index 98%
rename from include/utils/ProcessState.h
rename to include/binder/ProcessState.h
index 39584f4..feeb3c3 100644
--- a/include/utils/ProcessState.h
+++ b/include/binder/ProcessState.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_PROCESS_STATE_H
 #define ANDROID_PROCESS_STATE_H
 
-#include <utils/IBinder.h>
+#include <binder/IBinder.h>
 #include <utils/KeyedVector.h>
 #include <utils/String8.h>
 #include <utils/String16.h>
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 3694803..106807e 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -26,8 +26,8 @@
 
 #include <utils/RefBase.h>
 #include <utils/Errors.h>
-#include <utils/IInterface.h>
-#include <utils/IMemory.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
 #include <utils/threads.h>
 
 
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index ba0467c..0955819 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -26,8 +26,8 @@
 
 #include <utils/RefBase.h>
 #include <utils/Errors.h>
-#include <utils/IInterface.h>
-#include <utils/IMemory.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
 #include <utils/threads.h>
 
 
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 3e59d85..bac3d29 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -23,7 +23,7 @@
 
 #include <utils/RefBase.h>
 #include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
 #include <media/IAudioTrack.h>
 #include <media/IAudioRecord.h>
 #include <media/IAudioFlingerClient.h>
diff --git a/include/media/IAudioFlingerClient.h b/include/media/IAudioFlingerClient.h
index c3deb0b..383ec0c 100644
--- a/include/media/IAudioFlingerClient.h
+++ b/include/media/IAudioFlingerClient.h
@@ -19,7 +19,7 @@
 
 
 #include <utils/RefBase.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
 
 
 namespace android {
diff --git a/include/media/IAudioRecord.h b/include/media/IAudioRecord.h
index 9d45d2d9..46735de 100644
--- a/include/media/IAudioRecord.h
+++ b/include/media/IAudioRecord.h
@@ -22,8 +22,8 @@
 
 #include <utils/RefBase.h>
 #include <utils/Errors.h>
-#include <utils/IInterface.h>
-#include <utils/IMemory.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
 
 
 namespace android {
diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h
index 12f2111..de6426a 100644
--- a/include/media/IAudioTrack.h
+++ b/include/media/IAudioTrack.h
@@ -22,8 +22,8 @@
 
 #include <utils/RefBase.h>
 #include <utils/Errors.h>
-#include <utils/IInterface.h>
-#include <utils/IMemory.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
 
 
 namespace android {
diff --git a/include/media/IMediaMetadataRetriever.h b/include/media/IMediaMetadataRetriever.h
index c677e83..9baba8e 100644
--- a/include/media/IMediaMetadataRetriever.h
+++ b/include/media/IMediaMetadataRetriever.h
@@ -19,9 +19,9 @@
 #define ANDROID_IMEDIAMETADATARETRIEVER_H
 
 #include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
-#include <utils/IMemory.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
 
 namespace android {
 
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index a683e74..85aeb30 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -18,11 +18,12 @@
 #define ANDROID_IMEDIAPLAYER_H
 
 #include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
 
 namespace android {
 
+class Parcel;
 class ISurface;
 
 class IMediaPlayer: public IInterface
@@ -45,6 +46,14 @@
     virtual status_t        setAudioStreamType(int type) = 0;
     virtual status_t        setLooping(int loop) = 0;
     virtual status_t        setVolume(float leftVolume, float rightVolume) = 0;
+
+    // Invoke a generic method on the player by using opaque parcels
+    // for the request and reply.
+    // @param request Parcel that must start with the media player
+    // interface token.
+    // @param[out] reply Parcel to hold the reply data. Cannot be null.
+    // @return OK if the invocation was made. PERMISSION_DENIED otherwise.
+    virtual status_t        invoke(const Parcel& request, Parcel *reply) = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -61,4 +70,3 @@
 }; // namespace android
 
 #endif // ANDROID_IMEDIAPLAYER_H
-
diff --git a/include/media/IMediaPlayerClient.h b/include/media/IMediaPlayerClient.h
index 5d32811..eee6c97 100644
--- a/include/media/IMediaPlayerClient.h
+++ b/include/media/IMediaPlayerClient.h
@@ -18,8 +18,8 @@
 #define ANDROID_IMEDIAPLAYERCLIENT_H
 
 #include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
 
 namespace android {
 
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index d1d96b1..f6faf14 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -17,9 +17,10 @@
 #ifndef ANDROID_IMEDIAPLAYERSERVICE_H
 #define ANDROID_IMEDIAPLAYERSERVICE_H
 
+#include <utils/Errors.h>  // for status_t
 #include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
 
 #include <media/IMediaPlayerClient.h>
 #include <media/IMediaPlayer.h>
@@ -36,7 +37,6 @@
 
     virtual sp<IMediaRecorder>  createMediaRecorder(pid_t pid) = 0;
     virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid) = 0;
-
     virtual sp<IMediaPlayer>    create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) = 0;
     virtual sp<IMediaPlayer>    create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;
     virtual sp<IMemory>         decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h
index 64d3a40..24ac82b 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -18,7 +18,7 @@
 #ifndef ANDROID_IMEDIARECORDER_H
 #define ANDROID_IMEDIARECORDER_H
 
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
 
 namespace android {
 
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 7bf555a..21600b2 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -27,12 +27,15 @@
 
 namespace android {
 
+class Parcel;
+
 enum player_type {
     PV_PLAYER = 1,
     SONIVOX_PLAYER = 2,
     VORBIS_PLAYER = 3
 };
 
+
 #define DEFAULT_AUDIOSINK_BUFFERCOUNT 4
 #define DEFAULT_AUDIOSINK_BUFFERSIZE 1200
 #define DEFAULT_AUDIOSINK_SAMPLERATE 44100
@@ -45,7 +48,6 @@
 class MediaPlayerBase : public RefBase
 {
 public:
-
     // AudioSink: abstraction layer for audio output
     class AudioSink : public RefBase {
     public:
@@ -88,7 +90,14 @@
     virtual player_type playerType() = 0;
     virtual void        setNotifyCallback(void* cookie, notify_callback_f notifyFunc) {
                             mCookie = cookie; mNotify = notifyFunc; }
-
+    // Invoke a generic method on the player by using opaque parcels
+    // for the request and reply.
+    // @param request Parcel that is positioned at the start of the
+    //                data sent by the java layer.
+    // @param[out] reply Parcel to hold the reply data. Cannot be null.
+    // @return OK if the invocation was made successfully. A player
+    // not supporting the direct API should return INVALID_OPERATION.
+    virtual status_t    invoke(const Parcel& request, Parcel *reply) = 0;
 protected:
     virtual void        sendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify) mNotify(mCookie, msg, ext1, ext2); }
 
diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h
index 8122df6..eb4595b 100644
--- a/include/media/PVPlayer.h
+++ b/include/media/PVPlayer.h
@@ -52,6 +52,7 @@
     virtual status_t    reset();
     virtual status_t    setLooping(int loop);
     virtual player_type playerType() { return PV_PLAYER; }
+    virtual status_t    invoke(const Parcel& request, Parcel *reply);
 
     // make available to PlayerDriver
     void        sendEvent(int msg, int ext1=0, int ext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2); }
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index f2719d3..3db8a0f 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -20,7 +20,7 @@
 
 #include <utils/Errors.h>  // for status_t
 #include <utils/threads.h>
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 #include <media/IMediaMetadataRetriever.h>
 
 namespace android {
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 513ffe1..dd8ea19 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_MEDIAPLAYER_H
 #define ANDROID_MEDIAPLAYER_H
 
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 #include <ui/Surface.h>
 #include <media/IMediaPlayerClient.h>
 #include <media/IMediaPlayer.h>
@@ -151,7 +151,7 @@
             void            notify(int msg, int ext1, int ext2);
     static  sp<IMemory>     decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
     static  sp<IMemory>     decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
-
+            status_t        invoke(const Parcel& request, Parcel *reply);
 private:
             void            clear_l();
             status_t        seekTo_l(int msec);
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 9b54ca9..ad27903 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -18,7 +18,10 @@
 #ifndef ANDROID_MEDIARECORDER_H
 #define ANDROID_MEDIARECORDER_H
 
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
 #include <media/IMediaPlayerClient.h>
 
 namespace android {
diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h
index fbef1db..7749566 100644
--- a/include/media/mediascanner.h
+++ b/include/media/mediascanner.h
@@ -17,7 +17,10 @@
 #ifndef MEDIASCANNER_H
 #define MEDIASCANNER_H
 
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
 #include <pthread.h>
 
 namespace android {
diff --git a/include/private/binder/Static.h b/include/private/binder/Static.h
new file mode 100644
index 0000000..5b0f9fc
--- /dev/null
+++ b/include/private/binder/Static.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// All static variables go here, to control initialization and
+// destruction order in the library.
+
+#include <utils/threads.h>
+
+#include <binder/IBinder.h>
+#include <binder/IMemory.h>
+#include <binder/ProcessState.h>
+#include <binder/IPermissionController.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+// For ProcessState.cpp
+extern Mutex gProcessMutex;
+extern sp<ProcessState> gProcess;
+
+// For ServiceManager.cpp
+extern Mutex gDefaultServiceManagerLock;
+extern sp<IServiceManager> gDefaultServiceManager;
+extern sp<IPermissionController> gPermissionController;
+
+}   // namespace android
diff --git a/include/private/utils/binder_module.h b/include/private/binder/binder_module.h
similarity index 100%
rename from include/private/utils/binder_module.h
rename to include/private/binder/binder_module.h
diff --git a/include/private/utils/Static.h b/include/private/utils/Static.h
index f1439b7..d95ae0d 100644
--- a/include/private/utils/Static.h
+++ b/include/private/utils/Static.h
@@ -20,14 +20,6 @@
 #include <utils/threads.h>
 #include <utils/KeyedVector.h>
 
-#ifndef LIBUTILS_NATIVE
-#include <utils/IBinder.h>
-#include <utils/IMemory.h>
-#include <utils/ProcessState.h>
-#include <utils/IPermissionController.h>
-#include <utils/IServiceManager.h>
-#endif
-
 namespace android {
 // For TextStream.cpp
 extern Vector<int32_t> gTextBuffers;
@@ -40,19 +32,4 @@
 extern void initialize_string16();
 extern void terminate_string16();
 
-
-
-#ifndef LIBUTILS_NATIVE
-
-// For ProcessState.cpp
-extern Mutex gProcessMutex;
-extern sp<ProcessState> gProcess;
-
-// For ServiceManager.cpp
-extern Mutex gDefaultServiceManagerLock;
-extern sp<IServiceManager> gDefaultServiceManager;
-extern sp<IPermissionController> gPermissionController;
-
-#endif
-
 }   // namespace android
diff --git a/include/ui/CameraHardwareInterface.h b/include/ui/CameraHardwareInterface.h
index 73036f0..2cdcc06 100644
--- a/include/ui/CameraHardwareInterface.h
+++ b/include/ui/CameraHardwareInterface.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
 #define ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
 
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 #include <utils/RefBase.h>
 #include <ui/CameraParameters.h>
 #include <ui/Overlay.h>
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index 3848d8c..e12c4f1 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -20,7 +20,10 @@
 
 #include <utils/String8.h>
 #include <utils/threads.h>
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
 
 #include <linux/input.h>
 
diff --git a/include/ui/ICamera.h b/include/ui/ICamera.h
index 241fb63..1df7914 100644
--- a/include/ui/ICamera.h
+++ b/include/ui/ICamera.h
@@ -18,10 +18,10 @@
 #define ANDROID_HARDWARE_ICAMERA_H
 
 #include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
 #include <ui/ISurface.h>
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 #include <utils/String8.h>
 #include <ui/Camera.h>
 
diff --git a/include/ui/ICameraClient.h b/include/ui/ICameraClient.h
index c4bdd07..f38a6aa 100644
--- a/include/ui/ICameraClient.h
+++ b/include/ui/ICameraClient.h
@@ -18,9 +18,9 @@
 #define ANDROID_HARDWARE_ICAMERA_APP_H
 
 #include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
-#include <utils/IMemory.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
 
 namespace android {
 
diff --git a/include/ui/ICameraService.h b/include/ui/ICameraService.h
index c652c51..061681a 100644
--- a/include/ui/ICameraService.h
+++ b/include/ui/ICameraService.h
@@ -18,8 +18,8 @@
 #define ANDROID_HARDWARE_ICAMERASERVICE_H
 
 #include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
 
 #include <ui/ICameraClient.h>
 #include <ui/ICamera.h>
diff --git a/include/ui/IOverlay.h b/include/ui/IOverlay.h
index 699b1b0..af3add1 100644
--- a/include/ui/IOverlay.h
+++ b/include/ui/IOverlay.h
@@ -21,7 +21,7 @@
 #include <sys/types.h>
 
 #include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
 #include <utils/RefBase.h>
 #include <ui/PixelFormat.h>
 
diff --git a/include/ui/ISurface.h b/include/ui/ISurface.h
index 87b320f..136e801 100644
--- a/include/ui/ISurface.h
+++ b/include/ui/ISurface.h
@@ -21,7 +21,7 @@
 #include <sys/types.h>
 
 #include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
 #include <utils/RefBase.h>
 #include <ui/PixelFormat.h>
 
diff --git a/include/ui/ISurfaceComposer.h b/include/ui/ISurfaceComposer.h
index 5c64b22..5b062a8 100644
--- a/include/ui/ISurfaceComposer.h
+++ b/include/ui/ISurfaceComposer.h
@@ -22,7 +22,7 @@
 
 #include <utils/RefBase.h>
 #include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
 
 #include <ui/PixelFormat.h>
 #include <ui/ISurfaceFlingerClient.h>
diff --git a/include/ui/ISurfaceFlingerClient.h b/include/ui/ISurfaceFlingerClient.h
index 5b9361d..b6cbb0b 100644
--- a/include/ui/ISurfaceFlingerClient.h
+++ b/include/ui/ISurfaceFlingerClient.h
@@ -21,7 +21,7 @@
 #include <sys/types.h>
 
 #include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
 #include <utils/RefBase.h>
 
 #include <ui/ISurface.h>
diff --git a/include/ui/Overlay.h b/include/ui/Overlay.h
index 66514b4..9ba2f7b 100644
--- a/include/ui/Overlay.h
+++ b/include/ui/Overlay.h
@@ -21,7 +21,7 @@
 #include <sys/types.h>
 
 #include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
 #include <utils/RefBase.h>
 #include <utils/threads.h>
 
diff --git a/include/ui/Region.h b/include/ui/Region.h
index 7689673..68cb52e 100644
--- a/include/ui/Region.h
+++ b/include/ui/Region.h
@@ -21,7 +21,7 @@
 #include <sys/types.h>
 
 #include <utils/Vector.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 
 #include <ui/Rect.h>
 
diff --git a/include/utils.h b/include/utils.h
deleted file mode 100644
index 30648b1..0000000
--- a/include/utils.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Handy utility functions and portability code.  This file includes all
-// of the generally-useful headers in the "utils" directory.
-//
-#ifndef _LIBS_UTILS_H
-#define _LIBS_UTILS_H
-
-#include <utils/ported.h>
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <utils/Timers.h>
-#include <utils/List.h>
-#include <utils/string_array.h>
-#include <utils/misc.h>
-#include <utils/Errors.h>
-
-#endif // _LIBS_UTILS_H
diff --git a/include/utils/LogSocket.h b/include/utils/LogSocket.h
deleted file mode 100644
index 01fbfb5..0000000
--- a/include/utils/LogSocket.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* utils/LogSocket.h
-** 
-** Copyright 2008, The Android Open Source Project
-**
-** This file is dual licensed.  It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
-
-#ifndef _UTILS_LOGSOCKET_H
-#define _UTILS_LOGSOCKET_H
-
-#define SOCKET_CLOSE_LOCAL 0
-
-void add_send_stats(int fd, int send);
-void add_recv_stats(int fd, int recv);
-void log_socket_close(int fd, short reason);
-void log_socket_connect(int fd, unsigned int ip, unsigned short port);
-
-#endif /* _UTILS_LOGSOCKET_H */
diff --git a/include/utils/Pipe.h b/include/utils/Pipe.h
deleted file mode 100644
index 6404168..0000000
--- a/include/utils/Pipe.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// FIFO I/O.
-//
-#ifndef _LIBS_UTILS_PIPE_H
-#define _LIBS_UTILS_PIPE_H
-
-#ifdef HAVE_ANDROID_OS
-#error DO NOT USE THIS FILE IN THE DEVICE BUILD
-#endif
-
-namespace android {
-
-/*
- * Simple anonymous unidirectional pipe.
- *
- * The primary goal is to create an implementation with minimal overhead
- * under Linux.  Making Windows, Mac OS X, and Linux all work the same way
- * is a secondary goal.  Part of this goal is to have something that can
- * be fed to a select() call, so that the application can sleep in the
- * kernel until something interesting happens.
- */
-class Pipe {
-public:
-    Pipe(void);
-    virtual ~Pipe(void);
-
-    /* Create the pipe */
-    bool create(void);
-
-    /* Create a read-only pipe, using the supplied handle as read handle */
-    bool createReader(unsigned long handle);
-    /* Create a write-only pipe, using the supplied handle as write handle */
-    bool createWriter(unsigned long handle);
-
-    /* Is this object ready to go? */
-    bool isCreated(void);
-
-    /*
-     * Read "count" bytes from the pipe.  Returns the amount of data read,
-     * or 0 if no data available and we're non-blocking.
-     * Returns -1 on error.
-     */
-    int read(void* buf, int count);
-
-    /*
-     * Write "count" bytes into the pipe.  Returns number of bytes written,
-     * or 0 if there's no room for more data and we're non-blocking.
-     * Returns -1 on error.
-     */
-    int write(const void* buf, int count);
-
-    /* Returns "true" if data is available to read */
-    bool readReady(void);
-
-    /* Enable or disable non-blocking I/O for reads */
-    bool setReadNonBlocking(bool val);
-    /* Enable or disable non-blocking I/O for writes.  Only works on Linux. */
-    bool setWriteNonBlocking(bool val);
-
-    /*
-     * Get the handle.  Only useful in some platform-specific situations.
-     */
-    unsigned long getReadHandle(void);
-    unsigned long getWriteHandle(void);
-
-    /*
-     * Modify inheritance, i.e. whether or not a child process will get
-     * copies of the descriptors.  Systems with fork+exec allow us to close
-     * the descriptors before launching the child process, but Win32
-     * doesn't allow it.
-     */
-    bool disallowReadInherit(void);
-    bool disallowWriteInherit(void);
-
-    /*
-     * Close one side or the other.  Useful in the parent after launching
-     * a child process.
-     */
-    bool closeRead(void);
-    bool closeWrite(void);
-
-private:
-    bool    mReadNonBlocking;
-    bool    mWriteNonBlocking;
-
-    unsigned long mReadHandle;
-    unsigned long mWriteHandle;
-};
-
-}; // android
-
-#endif // _LIBS_UTILS_PIPE_H
diff --git a/include/utils/Socket.h b/include/utils/Socket.h
deleted file mode 100644
index 8b7f4061..0000000
--- a/include/utils/Socket.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Socket class.  Modeled after Java classes.
-//
-#ifndef _RUNTIME_SOCKET_H
-#define _RUNTIME_SOCKET_H
-
-#include <utils/inet_address.h>
-#include <sys/types.h>
-
-namespace android {
-
-/*
- * Basic socket class, needed to abstract away the differences between
- * BSD sockets and WinSock.  This establishes a streaming network
- * connection (TCP/IP) to somebody.
- */
-class Socket {
-public:
-    Socket(void);
-    ~Socket(void);
-
-    // Create a connection to somewhere.
-    // Return 0 on success.
-    int connect(const char* host, int port);
-    int connect(const InetAddress* addr, int port);
-
-
-    // Close the socket.  Don't try to use this object again after
-    // calling this.  Returns false on failure.
-    bool close(void);
-
-    // If we created the socket without an address, we can use these
-    // to finish the connection.  Returns 0 on success.
-    int bind(const SocketAddress& bindPoint);
-    int connect(const SocketAddress& endPoint);
-
-    // Here we deviate from the traditional object-oriented fanciness
-    // and just provide read/write operators instead of getters for
-    // objects that abstract a stream.
-    //
-    // Standard read/write semantics.
-    int read(void* buf, ssize_t len) const;
-    int write(const void* buf, ssize_t len) const;
-
-    // This must be called once, at program startup.
-    static bool bootInit(void);
-    static void finalShutdown(void);
-
-private:
-    // Internal function that establishes a connection.
-    int doConnect(const InetSocketAddress& addr);
-
-    unsigned long   mSock;      // holds SOCKET or int
-
-    static bool     mBootInitialized;
-};
-
-
-// debug -- unit tests
-void TestSockets(void);
-
-}; // namespace android
-
-#endif // _RUNTIME_SOCKET_H
diff --git a/include/utils/StringArray.h b/include/utils/StringArray.h
new file mode 100644
index 0000000..c244587
--- /dev/null
+++ b/include/utils/StringArray.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Sortable array of strings.  STL-ish, but STL-free.
+//  
+#ifndef _LIBS_UTILS_STRING_ARRAY_H
+#define _LIBS_UTILS_STRING_ARRAY_H
+
+#include <stdlib.h>
+#include <string.h>
+
+namespace android {
+
+//
+// An expanding array of strings.  Add, get, sort, delete.
+//
+class StringArray {
+public:
+    StringArray();
+    virtual ~StringArray();
+
+    //
+    // Add a string.  A copy of the string is made.
+    //
+    bool push_back(const char* str);
+
+    //
+    // Delete an entry.
+    //
+    void erase(int idx);
+
+    //
+    // Sort the array.
+    //
+    void sort(int (*compare)(const void*, const void*));
+    
+    //
+    // Pass this to the sort routine to do an ascending alphabetical sort.
+    //
+    static int cmpAscendingAlpha(const void* pstr1, const void* pstr2);
+    
+    //
+    // Get the #of items in the array.
+    //
+    inline int size(void) const { return mCurrent; }
+
+    //
+    // Return entry N.
+    // [should use operator[] here]
+    //
+    const char* getEntry(int idx) const {
+        return (unsigned(idx) >= unsigned(mCurrent)) ? NULL : mArray[idx];
+    }
+
+    //
+    // Set entry N to specified string.
+    // [should use operator[] here]
+    //
+    void setEntry(int idx, const char* str);
+
+private:
+    int     mMax;
+    int     mCurrent;
+    char**  mArray;
+};
+
+}; // namespace android
+
+#endif // _LIBS_UTILS_STRING_ARRAY_H
diff --git a/include/utils/TextOutput.h b/include/utils/TextOutput.h
index d8d86ba..de2fbbe 100644
--- a/include/utils/TextOutput.h
+++ b/include/utils/TextOutput.h
@@ -28,8 +28,8 @@
 class TextOutput
 {
 public:
-                        TextOutput() { }
-    virtual             ~TextOutput() { }
+                        TextOutput();
+    virtual             ~TextOutput();
     
     virtual status_t    print(const char* txt, size_t len) = 0;
     virtual void        moveIndent(int delta) = 0;
diff --git a/include/utils/TimerProbe.h b/include/utils/TimerProbe.h
deleted file mode 100644
index f2e32b2..0000000
--- a/include/utils/TimerProbe.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_TIMER_PROBE_H
-#define ANDROID_TIMER_PROBE_H
-
-#if 0 && defined(HAVE_POSIX_CLOCKS)
-#define ENABLE_TIMER_PROBE 1
-#else
-#define ENABLE_TIMER_PROBE 0
-#endif
-
-#if ENABLE_TIMER_PROBE
-
-#include <time.h>
-#include <sys/time.h>
-#include <utils/Vector.h>
-
-#define TIMER_PROBE(tag) \
-    static int _timer_slot_; \
-    android::TimerProbe probe(tag, &_timer_slot_)
-#define TIMER_PROBE_END() probe.end()
-#else
-#define TIMER_PROBE(tag)
-#define TIMER_PROBE_END()
-#endif
-
-#if ENABLE_TIMER_PROBE
-namespace android {
-
-class TimerProbe {
-public:
-    TimerProbe(const char tag[], int* slot);
-    void end();
-    ~TimerProbe();
-private:
-    struct Bucket {
-        int mStart, mReal, mProcess, mThread, mCount;
-        const char* mTag;
-        int* mSlotPtr;
-        int mIndent;
-    };
-    static Vector<Bucket> gBuckets;
-    static TimerProbe* gExecuteChain;
-    static int gIndent;
-    static timespec gRealBase;
-    TimerProbe* mNext;
-    static uint32_t ElapsedTime(const timespec& start, const timespec& end);
-    void print(const timespec& r, const timespec& p, const timespec& t) const;
-    timespec mRealStart, mPStart, mTStart;
-    const char* mTag;
-    int mIndent;
-    int mBucket;
-};
-
-}; // namespace android
-
-#endif
-#endif
diff --git a/include/utils/Timers.h b/include/utils/Timers.h
index 9610399..9a9e07c 100644
--- a/include/utils/Timers.h
+++ b/include/utils/Timers.h
@@ -88,9 +88,6 @@
 nsecs_t systemTime(int clock);
 #endif // def __cplusplus
 
-// return the system-time according to the specified clock
-int sleepForInterval(long interval, struct timeval* pNextTick);
-
 #ifdef __cplusplus
 } // extern "C"
 #endif
@@ -108,15 +105,15 @@
  */
 class DurationTimer {
 public:
-    DurationTimer(void) {}
-    ~DurationTimer(void) {}
+    DurationTimer() {}
+    ~DurationTimer() {}
 
     // Start the timer.
-    void start(void);
+    void start();
     // Stop the timer.
-    void stop(void);
+    void stop();
     // Get the duration in microseconds.
-    long long durationUsecs(void) const;
+    long long durationUsecs() const;
 
     // Subtract two timevals.  Returns the difference (ptv1-ptv2) in
     // microseconds.
diff --git a/include/utils/inet_address.h b/include/utils/inet_address.h
deleted file mode 100644
index dbd8672..0000000
--- a/include/utils/inet_address.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Internet address classes.  Modeled after Java classes.
-//
-#ifndef _RUNTIME_INET_ADDRESS_H
-#define _RUNTIME_INET_ADDRESS_H
-
-#ifdef HAVE_ANDROID_OS
-#error DO NOT USE THIS FILE IN THE DEVICE BUILD
-#endif
-
-
-namespace android {
-
-/*
- * This class holds Internet addresses.  Perhaps more useful is its
- * ability to look up addresses by name.
- *
- * Invoke one of the static factory methods to create a new object.
- */
-class InetAddress {
-public:
-    virtual ~InetAddress(void);
-
-    // create from w.x.y.z or foo.bar.com notation
-    static InetAddress* getByName(const char* host);
-
-    // copy-construction
-    InetAddress(const InetAddress& orig);
-
-    const void* getAddress(void) const { return mAddress; }
-    int getAddressLength(void) const { return mLength; }
-    const char* getHostName(void) const { return mName; }
-
-private:
-    InetAddress(void);
-    // assignment (private)
-    InetAddress& operator=(const InetAddress& addr);
-
-    // use a void* here so we don't have to expose actual socket headers
-    void*       mAddress;   // this is really a ptr to sockaddr_in
-    int         mLength;
-    char*       mName;
-};
-
-
-/*
- * Base class for socket addresses.
- */
-class SocketAddress {
-public:
-    SocketAddress() {}
-    virtual ~SocketAddress() {}
-};
-
-
-/*
- * Internet address class.  This combines an InetAddress with a port.
- */
-class InetSocketAddress : public SocketAddress {
-public:
-    InetSocketAddress() :
-        mAddress(0), mPort(-1)
-        {}
-    ~InetSocketAddress(void) {
-        delete mAddress;
-    }
-
-    // Create an address with a host wildcard (useful for servers).
-    bool create(int port);
-    // Create an address with the specified host and port.
-    bool create(const InetAddress* addr, int port);
-    // Create an address with the specified host and port.  Does the
-    // hostname lookup.
-    bool create(const char* host, int port);
-
-    const InetAddress* getAddress(void) const { return mAddress; }
-    const int getPort(void) const { return mPort; }
-    const char* getHostName(void) const { return mAddress->getHostName(); }
-
-private:
-    InetAddress* mAddress;
-    int         mPort;
-};
-
-}; // namespace android
-
-#endif // _RUNTIME_INET_ADDRESS_H
diff --git a/include/utils/misc.h b/include/utils/misc.h
index 62e84b4..23f2a4c 100644
--- a/include/utils/misc.h
+++ b/include/utils/misc.h
@@ -21,7 +21,7 @@
 #define _LIBS_UTILS_MISC_H
 
 #include <sys/time.h>
-#include "utils/Endian.h"
+#include <utils/Endian.h>
 
 namespace android {
 
diff --git a/include/utils/ported.h b/include/utils/ported.h
deleted file mode 100644
index eb3be01..0000000
--- a/include/utils/ported.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Standard functions ported to the current platform.  Note these are NOT
-// in the "android" namespace.
-//
-#ifndef _LIBS_UTILS_PORTED_H
-#define _LIBS_UTILS_PORTED_H
-
-#include <sys/time.h>       // for timeval
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* library replacement functions */
-#if defined(NEED_GETTIMEOFDAY)
-int gettimeofday(struct timeval* tv, struct timezone* tz);
-#endif
-#if defined(NEED_USLEEP)
-void usleep(unsigned long usec);
-#endif
-#if defined(NEED_PIPE)
-int pipe(int filedes[2]);
-#endif
-#if defined(NEED_SETENV)
-int setenv(const char* name, const char* value, int overwrite);
-void unsetenv(const char* name);
-char* getenv(const char* name);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // _LIBS_UTILS_PORTED_H
diff --git a/include/utils/string_array.h b/include/utils/string_array.h
deleted file mode 100644
index 064dda2..0000000
--- a/include/utils/string_array.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Sortable array of strings.  STL-ish, but STL-free.
-//  
-#ifndef _LIBS_UTILS_STRING_ARRAY_H
-#define _LIBS_UTILS_STRING_ARRAY_H
-
-#include <stdlib.h>
-#include <string.h>
-
-namespace android {
-
-//
-// An expanding array of strings.  Add, get, sort, delete.
-//
-class StringArray {
-public:
-    StringArray()
-        : mMax(0), mCurrent(0), mArray(NULL)
-        {}
-    virtual ~StringArray() {
-        for (int i = 0; i < mCurrent; i++)
-            delete[] mArray[i];
-        delete[] mArray;
-    }
-
-    //
-    // Add a string.  A copy of the string is made.
-    //
-    bool push_back(const char* str) {
-        if (mCurrent >= mMax) {
-            char** tmp;
-
-            if (mMax == 0)
-                mMax = 16;      // initial storage
-            else
-                mMax *= 2;
-
-            tmp = new char*[mMax];
-            if (tmp == NULL)
-                return false;
-
-            memcpy(tmp, mArray, mCurrent * sizeof(char*));
-            delete[] mArray;
-            mArray = tmp;
-        }
-
-        int len = strlen(str);
-        mArray[mCurrent] = new char[len+1];
-        memcpy(mArray[mCurrent], str, len+1);
-        mCurrent++;
-
-        return true;
-    }
-
-    //
-    // Delete an entry.
-    //
-    void erase(int idx) {
-        if (idx < 0 || idx >= mCurrent)
-            return;
-        delete[] mArray[idx];
-        if (idx < mCurrent-1) {
-            memmove(&mArray[idx], &mArray[idx+1],
-                (mCurrent-1 - idx) * sizeof(char*));
-        }
-        mCurrent--;
-    }
-
-    //
-    // Sort the array.
-    //
-    void sort(int (*compare)(const void*, const void*)) {
-        qsort(mArray, mCurrent, sizeof(char*), compare);
-    }
-
-    //
-    // Pass this to the sort routine to do an ascending alphabetical sort.
-    //
-    static int cmpAscendingAlpha(const void* pstr1, const void* pstr2) {
-        return strcmp(*(const char**)pstr1, *(const char**)pstr2);
-    }
-
-    //
-    // Get the #of items in the array.
-    //
-    inline int size(void) const { return mCurrent; }
-
-    //
-    // Return entry N.
-    // [should use operator[] here]
-    //
-    const char* getEntry(int idx) const {
-        if (idx < 0 || idx >= mCurrent)
-            return NULL;
-        return mArray[idx];
-    }
-
-    //
-    // Set entry N to specified string.
-    // [should use operator[] here]
-    //
-    void setEntry(int idx, const char* str) {
-        if (idx < 0 || idx >= mCurrent)
-            return;
-        delete[] mArray[idx];
-        int len = strlen(str);
-        mArray[idx] = new char[len+1];
-        memcpy(mArray[idx], str, len+1);
-    }
-
-private:
-    int     mMax;
-    int     mCurrent;
-    char**  mArray;
-};
-
-}; // namespace android
-
-#endif // _LIBS_UTILS_STRING_ARRAY_H
diff --git a/include/utils/threads.h b/include/utils/threads.h
index b320915..e0cb664 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -199,11 +199,11 @@
     // constructed and released when Autolock goes out of scope.
     class Autolock {
     public:
-        inline Autolock(Mutex& mutex) : mpMutex(&mutex) { mutex.lock(); }
-        inline Autolock(Mutex* mutex) : mpMutex(mutex) { mutex->lock(); }
-        inline ~Autolock() { mpMutex->unlock(); }
+        inline Autolock(Mutex& mutex) : mLock(mutex)  { mLock.lock(); }
+        inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
+        inline ~Autolock() { mLock.unlock(); }
     private:
-        Mutex*  mpMutex;
+        Mutex& mLock;
     };
 
 private:
@@ -291,7 +291,7 @@
             bool        exitPending() const;
     
 private:
-    // Derived class must implemtent threadLoop(). The thread starts its life
+    // Derived class must implement threadLoop(). The thread starts its life
     // here. There are two ways of using the Thread object:
     // 1) loop: if threadLoop() returns true, it will be called again if
     //          requestExit() wasn't called.
diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk
index 50d516b..ea7d6c2 100644
--- a/libs/audioflinger/Android.mk
+++ b/libs/audioflinger/Android.mk
@@ -11,6 +11,7 @@
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
     libutils \
+	libbinder \
     libmedia \
     libhardware_legacy
 
@@ -34,6 +35,7 @@
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
     libutils \
+	libbinder \
     libmedia \
     libhardware_legacy
 
@@ -49,8 +51,7 @@
   LOCAL_SRC_FILES += A2dpAudioInterface.cpp
   LOCAL_SHARED_LIBRARIES += liba2dp
   LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
-  LOCAL_C_INCLUDES += $(call include-path-for, bluez-libs)
-  LOCAL_C_INCLUDES += $(call include-path-for, bluez-utils)
+  LOCAL_C_INCLUDES += $(call include-path-for, bluez)
 endif
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 324111b..9783e54 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -24,10 +24,10 @@
 #include <sys/time.h>
 #include <sys/resource.h>
 
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
 #include <utils/Log.h>
-#include <utils/Parcel.h>
-#include <utils/IPCThreadState.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
 #include <utils/String16.h>
 #include <utils/threads.h>
 
@@ -652,6 +652,7 @@
     }
 
     status_t ret = NO_ERROR;
+    
     if (stream == AudioSystem::VOICE_CALL ||
         stream == AudioSystem::BLUETOOTH_SCO) {
         float hwValue;
@@ -668,7 +669,13 @@
         mHardwareStatus = AUDIO_SET_VOICE_VOLUME;
         ret = mAudioHardware->setVoiceVolume(hwValue);
         mHardwareStatus = AUDIO_HW_IDLE;
+        
     }
+    
+    mHardwareMixerThread->setStreamVolume(stream, value);
+#ifdef WITH_A2DP
+    mA2dpMixerThread->setStreamVolume(stream, value);
+#endif
 
     mHardwareMixerThread->setStreamVolume(stream, value);
 #ifdef WITH_A2DP
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index 8e47b29..cc3d6c2 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -30,7 +30,7 @@
 #include <utils/Atomic.h>
 #include <utils/Errors.h>
 #include <utils/threads.h>
-#include <utils/MemoryDealer.h>
+#include <binder/MemoryDealer.h>
 #include <utils/KeyedVector.h>
 #include <utils/SortedVector.h>
 #include <utils/Vector.h>
diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk
new file mode 100644
index 0000000..c4d695e
--- /dev/null
+++ b/libs/binder/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# we have the common sources, plus some device-specific stuff
+LOCAL_SRC_FILES:= \
+	Binder.cpp \
+	BpBinder.cpp \
+	IInterface.cpp \
+	IMemory.cpp \
+	IPCThreadState.cpp \
+	IPermissionController.cpp \
+	IServiceManager.cpp \
+	MemoryDealer.cpp \
+    MemoryBase.cpp \
+    MemoryHeapBase.cpp \
+    MemoryHeapPmem.cpp \
+	Parcel.cpp \
+	ProcessState.cpp \
+	Static.cpp
+
+LOCAL_LDLIBS += -lpthread
+
+LOCAL_SHARED_LIBRARIES := \
+	liblog \
+	libcutils \
+	libutils
+
+LOCAL_MODULE:= libbinder
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/utils/Binder.cpp b/libs/binder/Binder.cpp
similarity index 92%
rename from libs/utils/Binder.cpp
rename to libs/binder/Binder.cpp
index 37e4685..0dd7622 100644
--- a/libs/utils/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#include <utils/Binder.h>
+#include <binder/Binder.h>
 
 #include <utils/Atomic.h>
-#include <utils/BpBinder.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/BpBinder.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
 
 #include <stdio.h>
 
@@ -27,6 +27,17 @@
 
 // ---------------------------------------------------------------------------
 
+IBinder::IBinder()
+    : RefBase()
+{
+}
+
+IBinder::~IBinder()
+{
+}
+
+// ---------------------------------------------------------------------------
+
 sp<IInterface>  IBinder::queryLocalInterface(const String16& descriptor)
 {
     return NULL;
@@ -58,6 +69,8 @@
 
 // ---------------------------------------------------------------------------
 
+String16 BBinder::sEmptyDescriptor;
+
 BBinder::BBinder()
     : mExtras(NULL)
 {
@@ -73,10 +86,10 @@
     return NO_ERROR;
 }
 
-String16 BBinder::getInterfaceDescriptor() const
+const String16& BBinder::getInterfaceDescriptor() const
 {
     LOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this);
-    return String16();
+    return sEmptyDescriptor;
 }
 
 status_t BBinder::transact(
diff --git a/libs/utils/BpBinder.cpp b/libs/binder/BpBinder.cpp
similarity index 89%
rename from libs/utils/BpBinder.cpp
rename to libs/binder/BpBinder.cpp
index 69ab195..5de87ec 100644
--- a/libs/utils/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -17,9 +17,9 @@
 #define LOG_TAG "BpBinder"
 //#define LOG_NDEBUG 0
 
-#include <utils/BpBinder.h>
+#include <binder/BpBinder.h>
 
-#include <utils/IPCThreadState.h>
+#include <binder/IPCThreadState.h>
 #include <utils/Log.h>
 
 #include <stdio.h>
@@ -98,16 +98,33 @@
     IPCThreadState::self()->incWeakHandle(handle);
 }
 
-String16 BpBinder::getInterfaceDescriptor() const
+bool BpBinder::isDescriptorCached() const {
+    Mutex::Autolock _l(mLock);
+    return mDescriptorCache.size() ? true : false;
+}
+
+const String16& BpBinder::getInterfaceDescriptor() const
 {
-    String16 res;
-    Parcel send, reply;
-    status_t err = const_cast<BpBinder*>(this)->transact(
-            INTERFACE_TRANSACTION, send, &reply);
-    if (err == NO_ERROR) {
-        res = reply.readString16();
+    if (isDescriptorCached() == false) {
+        Parcel send, reply;
+        // do the IPC without a lock held.
+        status_t err = const_cast<BpBinder*>(this)->transact(
+                INTERFACE_TRANSACTION, send, &reply);
+        if (err == NO_ERROR) {
+            String16 res(reply.readString16());
+            Mutex::Autolock _l(mLock);
+            // mDescriptorCache could have been assigned while the lock was
+            // released.
+            if (mDescriptorCache.size() == 0)
+                mDescriptorCache = res;
+        }
     }
-    return res;
+    
+    // we're returning a reference to a non-static object here. Usually this
+    // is not something smart to do, however, with binder objects it is 
+    // (usually) safe because they are reference-counted.
+    
+    return mDescriptorCache;
 }
 
 bool BpBinder::isBinderAlive() const
diff --git a/libs/utils/IInterface.cpp b/libs/binder/IInterface.cpp
similarity index 90%
rename from libs/utils/IInterface.cpp
rename to libs/binder/IInterface.cpp
index 6ea8178..29acf5d 100644
--- a/libs/utils/IInterface.cpp
+++ b/libs/binder/IInterface.cpp
@@ -14,12 +14,19 @@
  * limitations under the License.
  */
 
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
 
 namespace android {
 
 // ---------------------------------------------------------------------------
 
+IInterface::IInterface() 
+    : RefBase() {
+}
+
+IInterface::~IInterface() {
+}
+
 sp<IBinder> IInterface::asBinder()
 {
     return this ? onAsBinder() : NULL;
diff --git a/libs/utils/IMemory.cpp b/libs/binder/IMemory.cpp
similarity index 96%
rename from libs/utils/IMemory.cpp
rename to libs/binder/IMemory.cpp
index 429bc2b..6c1d225 100644
--- a/libs/utils/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -25,11 +25,11 @@
 #include <sys/types.h>
 #include <sys/mman.h>
 
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
 #include <utils/Atomic.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 #include <utils/CallStack.h>
 
 #define VERBOSE   0
@@ -205,11 +205,11 @@
 
 IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory");
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
+BnMemory::BnMemory() {
+}
+
+BnMemory::~BnMemory() { 
+}
 
 status_t BnMemory::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
@@ -299,11 +299,11 @@
         ssize_t size = reply.readInt32();
         uint32_t flags = reply.readInt32();
 
-        LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%d, err=%d (%s)",
+        LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%ld, err=%d (%s)",
                 asBinder().get(), parcel_fd, size, err, strerror(-err));
 
         int fd = dup( parcel_fd );
-        LOGE_IF(fd==-1, "cannot dup fd=%d, size=%d, err=%d (%s)",
+        LOGE_IF(fd==-1, "cannot dup fd=%d, size=%ld, err=%d (%s)",
                 parcel_fd, size, err, strerror(errno));
 
         int access = PROT_READ;
@@ -316,7 +316,7 @@
             mRealHeap = true;
             mBase = mmap(0, size, access, MAP_SHARED, fd, 0);
             if (mBase == MAP_FAILED) {
-                LOGE("cannot map BpMemoryHeap (binder=%p), size=%d, fd=%d (%s)",
+                LOGE("cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d (%s)",
                         asBinder().get(), size, fd, strerror(errno));
                 close(fd);
             } else {
@@ -357,8 +357,14 @@
 
 IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap");
 
+BnMemoryHeap::BnMemoryHeap() { 
+}
+
+BnMemoryHeap::~BnMemoryHeap() { 
+}
+
 status_t BnMemoryHeap::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
     switch(code) {
        case HEAP_ID: {
diff --git a/libs/utils/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
similarity index 99%
rename from libs/utils/IPCThreadState.cpp
rename to libs/binder/IPCThreadState.cpp
index 04ae142..c3889e9 100644
--- a/libs/utils/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-#include <utils/IPCThreadState.h>
+#include <binder/IPCThreadState.h>
 
-#include <utils/Binder.h>
-#include <utils/BpBinder.h>
+#include <binder/Binder.h>
+#include <binder/BpBinder.h>
 #include <utils/Debug.h>
 #include <utils/Log.h>
 #include <utils/TextOutput.h>
 #include <utils/threads.h>
 
-#include <private/utils/binder_module.h>
-#include <private/utils/Static.h>
+#include <private/binder/binder_module.h>
+#include <private/binder/Static.h>
 
 #include <sys/ioctl.h>
 #include <signal.h>
diff --git a/libs/utils/IPermissionController.cpp b/libs/binder/IPermissionController.cpp
similarity index 87%
rename from libs/utils/IPermissionController.cpp
rename to libs/binder/IPermissionController.cpp
index f01d38f..bff4c9b 100644
--- a/libs/utils/IPermissionController.cpp
+++ b/libs/binder/IPermissionController.cpp
@@ -16,14 +16,14 @@
 
 #define LOG_TAG "PermissionController"
 
-#include <utils/IPermissionController.h>
+#include <binder/IPermissionController.h>
 
 #include <utils/Debug.h>
 #include <utils/Log.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 #include <utils/String8.h>
 
-#include <private/utils/Static.h>
+#include <private/binder/Static.h>
 
 namespace android {
 
@@ -55,12 +55,6 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnPermissionController::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/libs/utils/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
similarity index 94%
rename from libs/utils/IServiceManager.cpp
rename to libs/binder/IServiceManager.cpp
index 9beeadd..88774e7 100644
--- a/libs/utils/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -16,16 +16,16 @@
 
 #define LOG_TAG "ServiceManager"
 
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
 
 #include <utils/Debug.h>
-#include <utils/IPCThreadState.h>
+#include <binder/IPCThreadState.h>
 #include <utils/Log.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 #include <utils/String8.h>
 #include <utils/SystemClock.h>
 
-#include <private/utils/Static.h>
+#include <private/binder/Static.h>
 
 #include <unistd.h>
 
@@ -178,12 +178,6 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnServiceManager::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/libs/utils/MemoryBase.cpp b/libs/binder/MemoryBase.cpp
similarity index 97%
rename from libs/utils/MemoryBase.cpp
rename to libs/binder/MemoryBase.cpp
index f25e11c..033066b 100644
--- a/libs/utils/MemoryBase.cpp
+++ b/libs/binder/MemoryBase.cpp
@@ -18,7 +18,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 
-#include <utils/MemoryBase.h>
+#include <binder/MemoryBase.h>
 
 
 namespace android {
diff --git a/libs/utils/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
similarity index 94%
rename from libs/utils/MemoryDealer.cpp
rename to libs/binder/MemoryDealer.cpp
index cf8201b..d5ffe7f 100644
--- a/libs/utils/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -16,13 +16,13 @@
 
 #define LOG_TAG "MemoryDealer"
 
-#include <utils/MemoryDealer.h>
+#include <binder/MemoryDealer.h>
 
 #include <utils/Log.h>
-#include <utils/IPCThreadState.h>
+#include <binder/IPCThreadState.h>
 #include <utils/SortedVector.h>
 #include <utils/String8.h>
-#include <utils/MemoryBase.h>
+#include <binder/MemoryBase.h>
 
 #include <stdint.h>
 #include <stdio.h>
@@ -38,7 +38,15 @@
 #include <sys/file.h>
 
 namespace android {
+// ----------------------------------------------------------------------------
 
+HeapInterface::HeapInterface() { }
+HeapInterface::~HeapInterface() { }
+
+// ----------------------------------------------------------------------------
+
+AllocatorInterface::AllocatorInterface() { }
+AllocatorInterface::~AllocatorInterface() { }
 
 // ----------------------------------------------------------------------------
 
@@ -107,7 +115,7 @@
         if (new_memory != 0) {
             memory = new Allocation(this, offset, size, new_memory);
         } else {
-            LOGE("couldn't map [%8x, %d]", offset, size);
+            LOGE("couldn't map [%8lx, %u]", offset, size);
             if (size) {
                 /* NOTE: it's VERY important to not free allocations of size 0
                  * because they're special as they don't have any record in the 
@@ -339,6 +347,10 @@
         
 // ----------------------------------------------------------------------------
 
+SharedHeap::SharedHeap() 
+    : HeapInterface(), MemoryHeapBase() 
+{ 
+}
 
 SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name)
     : MemoryHeapBase(size, flags, name)
diff --git a/libs/utils/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
similarity index 98%
rename from libs/utils/MemoryHeapBase.cpp
rename to libs/binder/MemoryHeapBase.cpp
index 8251728..ac38f51 100644
--- a/libs/utils/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -29,7 +29,7 @@
 #include <cutils/ashmem.h>
 #include <cutils/atomic.h>
 
-#include <utils/MemoryHeapBase.h>
+#include <binder/MemoryHeapBase.h>
 
 #if HAVE_ANDROID_OS
 #include <linux/android_pmem.h>
diff --git a/libs/utils/MemoryHeapPmem.cpp b/libs/binder/MemoryHeapPmem.cpp
similarity index 98%
rename from libs/utils/MemoryHeapPmem.cpp
rename to libs/binder/MemoryHeapPmem.cpp
index eba2b30..3806a42 100644
--- a/libs/utils/MemoryHeapPmem.cpp
+++ b/libs/binder/MemoryHeapPmem.cpp
@@ -27,8 +27,8 @@
 
 #include <cutils/log.h>
 
-#include <utils/MemoryHeapPmem.h>
-#include <utils/MemoryHeapBase.h>
+#include <binder/MemoryHeapPmem.h>
+#include <binder/MemoryHeapBase.h>
 
 #if HAVE_ANDROID_OS
 #include <linux/android_pmem.h>
@@ -108,7 +108,7 @@
     // promote() it.
     
 #if HAVE_ANDROID_OS
-    if (mSize != NULL) {
+    if (mSize != 0) {
         const sp<MemoryHeapPmem>& heap(getHeap());
         int our_fd = heap->heapID();
         struct pmem_region sub;
diff --git a/libs/utils/Parcel.cpp b/libs/binder/Parcel.cpp
similarity index 98%
rename from libs/utils/Parcel.cpp
rename to libs/binder/Parcel.cpp
index b0e3750..f40e4bd 100644
--- a/libs/utils/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -17,19 +17,19 @@
 #define LOG_TAG "Parcel"
 //#define LOG_NDEBUG 0
 
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 
-#include <utils/Binder.h>
-#include <utils/BpBinder.h>
+#include <binder/Binder.h>
+#include <binder/BpBinder.h>
 #include <utils/Debug.h>
-#include <utils/ProcessState.h>
+#include <binder/ProcessState.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
 #include <utils/String16.h>
 #include <utils/TextOutput.h>
 #include <utils/misc.h>
 
-#include <private/utils/binder_module.h>
+#include <private/binder/binder_module.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -441,9 +441,14 @@
     return writeString16(interface);
 }
 
+bool Parcel::checkInterface(IBinder* binder) const
+{
+    return enforceInterface(binder->getInterfaceDescriptor()); 
+}
+
 bool Parcel::enforceInterface(const String16& interface) const
 {
-    String16 str = readString16();
+    const String16 str(readString16());
     if (str == interface) {
         return true;
     } else {
diff --git a/libs/utils/ProcessState.cpp b/libs/binder/ProcessState.cpp
similarity index 97%
rename from libs/utils/ProcessState.cpp
rename to libs/binder/ProcessState.cpp
index 4567df6..d7daf73 100644
--- a/libs/utils/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -18,19 +18,19 @@
 
 #include <cutils/process_name.h>
 
-#include <utils/ProcessState.h>
+#include <binder/ProcessState.h>
 
 #include <utils/Atomic.h>
-#include <utils/BpBinder.h>
-#include <utils/IPCThreadState.h>
+#include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
 #include <utils/String8.h>
 #include <utils/threads.h>
 
-#include <private/utils/binder_module.h>
-#include <private/utils/Static.h>
+#include <private/binder/binder_module.h>
+#include <private/binder/Static.h>
 
 #include <errno.h>
 #include <fcntl.h>
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
new file mode 100644
index 0000000..12b0308
--- /dev/null
+++ b/libs/binder/Static.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// All static variables go here, to control initialization and
+// destruction order in the library.
+
+#include <private/binder/Static.h>
+
+#include <binder/IPCThreadState.h>
+#include <utils/Log.h>
+
+namespace android {
+
+// ------------ ProcessState.cpp
+
+Mutex gProcessMutex;
+sp<ProcessState> gProcess;
+
+class LibUtilsIPCtStatics
+{
+public:
+    LibUtilsIPCtStatics()
+    {
+    }
+    
+    ~LibUtilsIPCtStatics()
+    {
+        IPCThreadState::shutdown();
+    }
+};
+
+static LibUtilsIPCtStatics gIPCStatics;
+
+// ------------ ServiceManager.cpp
+
+Mutex gDefaultServiceManagerLock;
+sp<IServiceManager> gDefaultServiceManager;
+sp<IPermissionController> gPermissionController;
+
+}   // namespace android
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
new file mode 100644
index 0000000..f5297f8
--- /dev/null
+++ b/libs/rs/Android.mk
@@ -0,0 +1,118 @@
+# Only build if BUILD_RENDERSCRIPT is defined to true in the environment.
+ifeq ($(BUILD_RENDERSCRIPT),true)
+
+LOCAL_PATH:=$(call my-dir)
+
+
+# Build rsg-generator ====================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := rsg-generator
+
+# These symbols are normally defined by BUILD_XXX, but we need to define them
+# here so that local-intermediates-dir works.
+
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+intermediates := $(local-intermediates-dir)
+
+GEN := $(addprefix $(intermediates)/, \
+            lex.yy.c \
+        )
+$(GEN):	PRIVATE_CUSTOM_TOOL = flex -o $@ $<
+
+$(intermediates)/lex.yy.c : $(LOCAL_PATH)/spec.lex
+	$(transform-generated-source)
+
+$(LOCAL_PATH)/rsg_generator.c : $(intermediates)/lex.yy.c
+
+LOCAL_SRC_FILES:= \
+    rsg_generator.c
+
+include $(BUILD_HOST_EXECUTABLE)
+
+# TODO: This should go into build/core/config.mk
+RSG_GENERATOR:=$(LOCAL_BUILT_MODULE)
+
+
+
+# Build render script lib ====================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libRS
+
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+intermediates:= $(local-intermediates-dir)
+
+# Generate custom headers
+
+GEN := $(addprefix $(intermediates)/, \
+            rsgApiStructs.h \
+            rsgApiFuncDecl.h \
+        )
+
+$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN) : PRIVATE_CUSTOM_TOOL = $(RSG_GENERATOR) $< $@ <$(PRIVATE_PATH)/rs.spec
+$(GEN) : $(RSG_GENERATOR) $(LOCAL_PATH)/rs.spec
+$(GEN): $(intermediates)/%.h : $(LOCAL_PATH)/%.h.rsg
+	$(transform-generated-source)
+
+# used in jni/Android.mk
+rs_generated_source += $(GEN)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+# Generate custom source files
+
+GEN := $(addprefix $(intermediates)/, \
+            rsgApi.cpp \
+            rsgApiReplay.cpp \
+        )
+
+$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN) : PRIVATE_CUSTOM_TOOL = $(RSG_GENERATOR) $< $@ <$(PRIVATE_PATH)/rs.spec
+$(GEN) : $(RSG_GENERATOR) $(LOCAL_PATH)/rs.spec
+$(GEN): $(intermediates)/%.cpp : $(LOCAL_PATH)/%.cpp.rsg
+	$(transform-generated-source)
+
+# used in jni/Android.mk
+rs_generated_source += $(GEN)
+
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+LOCAL_SRC_FILES:= \
+	rsAdapter.cpp \
+	rsAllocation.cpp \
+	rsComponent.cpp \
+	rsContext.cpp \
+	rsDevice.cpp \
+	rsElement.cpp \
+	rsLight.cpp \
+	rsLocklessFifo.cpp \
+	rsObjectBase.cpp \
+	rsMatrix.cpp \
+	rsProgram.cpp \
+	rsProgramFragment.cpp \
+	rsProgramFragmentStore.cpp \
+	rsProgramVertex.cpp \
+	rsSampler.cpp \
+	rsScript.cpp \
+	rsScriptC.cpp \
+	rsThreadIO.cpp \
+	rsType.cpp \
+	rsTriangleMesh.cpp
+
+LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libui libacc
+LOCAL_LDLIBS := -lpthread -ldl
+LOCAL_MODULE:= libRS
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Include the subdirectories ====================
+include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk,\
+            jni \
+            java \
+    	))
+
+endif # BUILD_RENDERSCRIPT
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
new file mode 100644
index 0000000..5f551df
--- /dev/null
+++ b/libs/rs/RenderScript.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RENDER_SCRIPT_H
+#define RENDER_SCRIPT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//////////////////////////////////////////////////////
+// 
+
+typedef void * RsAdapter1D;
+typedef void * RsAdapter2D;
+typedef void * RsAllocation;
+typedef void * RsContext;
+typedef void * RsDevice;
+typedef void * RsElement;
+typedef void * RsSampler;
+typedef void * RsScript;
+typedef void * RsScriptBasicTemp;
+typedef void * RsTriangleMesh;
+typedef void * RsType;
+typedef void * RsLight;
+
+typedef void * RsProgramVertex;
+typedef void * RsProgramFragment;
+typedef void * RsProgramFragmentStore;
+
+RsDevice rsDeviceCreate();
+void rsDeviceDestroy(RsDevice);
+
+RsContext rsContextCreate(RsDevice, void *, uint32_t version);
+void rsContextDestroy(RsContext);
+
+enum RsDataType {
+    RS_TYPE_FLOAT,
+    RS_TYPE_UNSIGNED,
+    RS_TYPE_SIGNED
+};
+
+enum RsDataKind {
+    RS_KIND_USER,
+    RS_KIND_RED, 
+    RS_KIND_GREEN, 
+    RS_KIND_BLUE, 
+    RS_KIND_ALPHA, 
+    RS_KIND_LUMINANCE, 
+    RS_KIND_INTENSITY,
+    RS_KIND_X, 
+    RS_KIND_Y, 
+    RS_KIND_Z, 
+    RS_KIND_W,
+    RS_KIND_S, 
+    RS_KIND_T, 
+    RS_KIND_Q, 
+    RS_KIND_R,
+    RS_KIND_NX, 
+    RS_KIND_NY, 
+    RS_KIND_NZ,
+    RS_KIND_INDEX
+};
+
+enum RsElementPredefined {
+    RS_ELEMENT_USER_U8,
+    RS_ELEMENT_USER_I8,
+    RS_ELEMENT_USER_U16,
+    RS_ELEMENT_USER_I16,
+    RS_ELEMENT_USER_U32,
+    RS_ELEMENT_USER_I32,
+    RS_ELEMENT_USER_FLOAT, 
+
+    RS_ELEMENT_A_8,          // 7
+    RS_ELEMENT_RGB_565,      // 8
+    RS_ELEMENT_RGBA_5551,    // 9
+    RS_ELEMENT_RGBA_4444,    // 10
+    RS_ELEMENT_RGB_888,      // 11
+    RS_ELEMENT_RGBA_8888,    // 12
+
+    RS_ELEMENT_INDEX_16, //13
+    RS_ELEMENT_INDEX_32, 
+    RS_ELEMENT_XY_F32, 
+    RS_ELEMENT_XYZ_F32, 
+    RS_ELEMENT_ST_XY_F32, 
+    RS_ELEMENT_ST_XYZ_F32, 
+    RS_ELEMENT_NORM_XYZ_F32,
+    RS_ELEMENT_NORM_ST_XYZ_F32,
+};  
+
+enum RsSamplerParam {
+    RS_SAMPLER_MIN_FILTER,
+    RS_SAMPLER_MAG_FILTER,
+    RS_SAMPLER_WRAP_S,
+    RS_SAMPLER_WRAP_T,
+    RS_SAMPLER_WRAP_R
+};  
+
+enum RsSamplerValue {
+    RS_SAMPLER_NEAREST,
+    RS_SAMPLER_LINEAR,
+    RS_SAMPLER_LINEAR_MIP_LINEAR,
+    RS_SAMPLER_WRAP,
+    RS_SAMPLER_CLAMP
+};  
+
+enum RsDimension {
+    RS_DIMENSION_X,
+    RS_DIMENSION_Y,
+    RS_DIMENSION_Z,
+    RS_DIMENSION_LOD,
+    RS_DIMENSION_FACE,
+
+    RS_DIMENSION_ARRAY_0 = 100, 
+    RS_DIMENSION_ARRAY_1, 
+    RS_DIMENSION_ARRAY_2, 
+    RS_DIMENSION_ARRAY_3,
+    RS_DIMENSION_MAX = RS_DIMENSION_ARRAY_3
+};
+
+enum RsDepthFunc {
+    RS_DEPTH_FUNC_ALWAYS,
+    RS_DEPTH_FUNC_LESS,
+    RS_DEPTH_FUNC_LEQUAL,
+    RS_DEPTH_FUNC_GREATER,
+    RS_DEPTH_FUNC_GEQUAL,
+    RS_DEPTH_FUNC_EQUAL,
+    RS_DEPTH_FUNC_NOTEQUAL
+};
+
+enum RsBlendSrcFunc {
+    RS_BLEND_SRC_ZERO, 
+    RS_BLEND_SRC_ONE, 
+    RS_BLEND_SRC_DST_COLOR, 
+    RS_BLEND_SRC_ONE_MINUS_DST_COLOR, 
+    RS_BLEND_SRC_SRC_ALPHA, 
+    RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA, 
+    RS_BLEND_SRC_DST_ALPHA, 
+    RS_BLEND_SRC_ONE_MINUS_DST_ALPHA, 
+    RS_BLEND_SRC_SRC_ALPHA_SATURATE
+};
+
+enum RsBlendDstFunc {
+    RS_BLEND_DST_ZERO, 
+    RS_BLEND_DST_ONE, 
+    RS_BLEND_DST_SRC_COLOR, 
+    RS_BLEND_DST_ONE_MINUS_SRC_COLOR, 
+    RS_BLEND_DST_SRC_ALPHA, 
+    RS_BLEND_DST_ONE_MINUS_SRC_ALPHA, 
+    RS_BLEND_DST_DST_ALPHA, 
+    RS_BLEND_DST_ONE_MINUS_DST_ALPHA
+};
+
+enum RsTexEnvMode {
+    RS_TEX_ENV_MODE_REPLACE,
+    RS_TEX_ENV_MODE_MODULATE,
+    RS_TEX_ENV_MODE_DECAL
+};
+
+enum RsPrimitive {
+    RS_PRIMITIVE_POINT,
+    RS_PRIMITIVE_LINE,
+    RS_PRIMITIVE_LINE_STRIP,
+    RS_PRIMITIVE_TRIANGLE,
+    RS_PRIMITIVE_TRIANGLE_STRIP,
+    RS_PRIMITIVE_TRIANGLE_FAN
+};
+
+
+#include "rsgApiFuncDecl.h"
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // RENDER_SCRIPT_H
+
+
+
diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h
new file mode 100644
index 0000000..53de1f1
--- /dev/null
+++ b/libs/rs/RenderScriptEnv.h
@@ -0,0 +1,98 @@
+#include <stdint.h>
+
+
+typedef void * RsAdapter1D;
+typedef void * RsAdapter2D;
+typedef void * RsAllocation;
+typedef void * RsContext;
+typedef void * RsDevice;
+typedef void * RsElement;
+typedef void * RsSampler;
+typedef void * RsScript;
+typedef void * RsScriptBasicTemp;
+typedef void * RsTriangleMesh;
+typedef void * RsType;
+typedef void * RsProgramFragment;
+typedef void * RsProgramFragmentStore;
+typedef void * RsLight;
+
+
+typedef struct {
+    float m[16];
+} rsc_Matrix;
+
+
+typedef struct {
+    float v[4];
+} rsc_Vector4;
+
+#define RS_PROGRAM_VERTEX_MODELVIEW_OFFSET 0
+#define RS_PROGRAM_VERTEX_PROJECTION_OFFSET 16
+#define RS_PROGRAM_VERTEX_TEXTURE_OFFSET 32
+
+typedef struct {
+    const void * (*loadEnvVp)(uint32_t bank, uint32_t offset);
+
+    float (*loadEnvF)(uint32_t bank, uint32_t offset);
+    int32_t (*loadEnvI32)(uint32_t bank, uint32_t offset);
+    uint32_t (*loadEnvU32)(uint32_t bank, uint32_t offset);
+    void (*loadEnvVec4)(uint32_t bank, uint32_t offset, rsc_Vector4 *);
+    void (*loadEnvMatrix)(uint32_t bank, uint32_t offset, rsc_Matrix *);
+
+    void (*storeEnvF)(uint32_t bank, uint32_t offset, float);
+    void (*storeEnvI32)(uint32_t bank, uint32_t offset, int32_t);
+    void (*storeEnvU32)(uint32_t bank, uint32_t offset, uint32_t);
+    void (*storeEnvVec4)(uint32_t bank, uint32_t offset, const rsc_Vector4 *);
+    void (*storeEnvMatrix)(uint32_t bank, uint32_t offset, const rsc_Matrix *);
+
+    void (*matrixLoadIdentity)(rsc_Matrix *);
+    void (*matrixLoadFloat)(rsc_Matrix *, const float *);
+    void (*matrixLoadMat)(rsc_Matrix *, const rsc_Matrix *);
+    void (*matrixLoadRotate)(rsc_Matrix *, float rot, float x, float y, float z);
+    void (*matrixLoadScale)(rsc_Matrix *, float x, float y, float z);
+    void (*matrixLoadTranslate)(rsc_Matrix *, float x, float y, float z);
+    void (*matrixLoadMultiply)(rsc_Matrix *, const rsc_Matrix *lhs, const rsc_Matrix *rhs);
+    void (*matrixMultiply)(rsc_Matrix *, const rsc_Matrix *rhs);
+    void (*matrixRotate)(rsc_Matrix *, float rot, float x, float y, float z);
+    void (*matrixScale)(rsc_Matrix *, float x, float y, float z);
+    void (*matrixTranslate)(rsc_Matrix *, float x, float y, float z);
+
+    void (*color)(float r, float g, float b, float a);
+
+    void (*programFragmentBindTexture)(RsProgramFragment, uint32_t slot, RsAllocation);
+    void (*programFragmentBindSampler)(RsProgramFragment, uint32_t slot, RsAllocation);
+
+    void (*materialDiffuse)(float r, float g, float b, float a);
+    void (*materialSpecular)(float r, float g, float b, float a);
+    void (*lightPosition)(float x, float y, float z, float w);
+    void (*materialShininess)(float s);
+
+    void (*uploadToTexture)(RsAllocation va, uint32_t baseMipLevel);
+
+    void (*enable)(uint32_t);
+    void (*disable)(uint32_t);
+
+    uint32_t (*rand)(uint32_t max);
+
+    void (*contextBindProgramFragment)(RsProgramFragment pf);
+    void (*contextBindProgramFragmentStore)(RsProgramFragmentStore pfs);
+
+
+    // Drawing funcs
+    void (*renderTriangleMesh)(RsTriangleMesh);
+    void (*renderTriangleMeshRange)(RsTriangleMesh, uint32_t start, uint32_t count);
+
+    // Assumes (GL_FIXED) x,y,z (GL_UNSIGNED_BYTE)r,g,b,a
+    void (*drawTriangleArray)(RsAllocation alloc, uint32_t count);
+
+    void (*drawRect)(int32_t x1, int32_t x2, int32_t y1, int32_t y2);
+} rsc_FunctionTable;
+
+typedef int (*rsc_RunScript)(uint32_t launchIndex, const rsc_FunctionTable *);
+
+
+/* EnableCap */
+#define GL_LIGHTING                       0x0B50
+
+/* LightName */
+#define GL_LIGHT0                         0x4000
diff --git a/libs/rs/java/Android.mk b/libs/rs/java/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/libs/rs/java/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/libs/rs/java/Film/Android.mk b/libs/rs/java/Film/Android.mk
new file mode 100644
index 0000000..2e9c243
--- /dev/null
+++ b/libs/rs/java/Film/Android.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+
+LOCAL_PACKAGE_NAME := Film
+
+include $(BUILD_PACKAGE)
diff --git a/libs/rs/java/Film/AndroidManifest.xml b/libs/rs/java/Film/AndroidManifest.xml
new file mode 100644
index 0000000..a5ce8a1
--- /dev/null
+++ b/libs/rs/java/Film/AndroidManifest.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.film">
+    <application android:label="Film">
+        <activity android:name="Film"
+                  android:screenOrientation="portrait"
+                  android:theme="@android:style/Theme.Black.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/libs/rs/java/Film/res/raw/filmimage.c b/libs/rs/java/Film/res/raw/filmimage.c
new file mode 100644
index 0000000..3bd9496
--- /dev/null
+++ b/libs/rs/java/Film/res/raw/filmimage.c
@@ -0,0 +1,110 @@
+// Fountain test script
+
+#pragma version(1)
+#pragma stateVertex(orthoWindow)
+#pragma stateRaster(flat)
+#pragma stateFragment(PgmFragBackground)
+#pragma stateFragmentStore(MyBlend)
+
+
+int main(void* con, int ft, int launchID) {
+    int count, touch, x, y, rate, maxLife, lifeShift;
+    int life;
+    int ct, ct2;
+    int newPart;
+    int drawCount;
+    int dx, dy, idx;
+    int posx,posy;
+    int c;
+    int srcIdx;
+    int dstIdx;
+
+    count = loadI32(con, 0, 1);
+    touch = loadI32(con, 0, 2);
+    x = loadI32(con, 0, 3);
+    y = loadI32(con, 0, 4);
+
+    rate = 4;
+    maxLife = (count / rate) - 1;
+    lifeShift = 0;
+    {
+        life = maxLife;
+        while (life > 255) {
+            life = life >> 1;
+            lifeShift ++;
+        }
+    }
+
+    drawRect(con, 0, 256, 0, 512);
+    contextBindProgramFragment(con, NAMED_PgmFragParts);
+
+    if (touch) {
+        newPart = loadI32(con, 2, 0);
+        for (ct2=0; ct2<rate; ct2++) {
+            dx = scriptRand(con, 0x10000) - 0x8000;
+            dy = scriptRand(con, 0x10000) - 0x8000;
+
+            idx = newPart * 5 + 1;
+            storeI32(con, 2, idx, dx);
+            storeI32(con, 2, idx + 1, dy);
+            storeI32(con, 2, idx + 2, maxLife);
+            storeI32(con, 2, idx + 3, x << 16);
+            storeI32(con, 2, idx + 4, y << 16);
+
+            newPart++;
+            if (newPart >= count) {
+                newPart = 0;
+            }
+        }
+        storeI32(con, 2, 0, newPart);
+    }
+
+    drawCount = 0;
+    for (ct=0; ct < count; ct++) {
+        srcIdx = ct * 5 + 1;
+
+        dx = loadI32(con, 2, srcIdx);
+        dy = loadI32(con, 2, srcIdx + 1);
+        life = loadI32(con, 2, srcIdx + 2);
+        posx = loadI32(con, 2, srcIdx + 3);
+        posy = loadI32(con, 2, srcIdx + 4);
+
+        if (life) {
+            if (posy < (480 << 16)) {
+                dstIdx = drawCount * 9;
+                c = 0xffafcf | ((life >> lifeShift) << 24);
+
+                storeU32(con, 1, dstIdx, c);
+                storeI32(con, 1, dstIdx + 1, posx);
+                storeI32(con, 1, dstIdx + 2, posy);
+
+                storeU32(con, 1, dstIdx + 3, c);
+                storeI32(con, 1, dstIdx + 4, posx + 0x10000);
+                storeI32(con, 1, dstIdx + 5, posy + dy * 4);
+
+                storeU32(con, 1, dstIdx + 6, c);
+                storeI32(con, 1, dstIdx + 7, posx - 0x10000);
+                storeI32(con, 1, dstIdx + 8, posy + dy * 4);
+                drawCount ++;
+            } else {
+                if (dy > 0) {
+                    dy = (-dy) >> 1;
+                }
+            }
+
+            posx = posx + dx;
+            posy = posy + dy;
+            dy = dy + 0x400;
+            life --;
+
+            //storeI32(con, 2, srcIdx, dx);
+            storeI32(con, 2, srcIdx + 1, dy);
+            storeI32(con, 2, srcIdx + 2, life);
+            storeI32(con, 2, srcIdx + 3, posx);
+            storeI32(con, 2, srcIdx + 4, posy);
+        }
+    }
+
+    drawTriangleArray(con, NAMED_PartBuffer, drawCount);
+    return 1;
+}
diff --git a/libs/rs/java/Film/res/raw/filmstrip.c b/libs/rs/java/Film/res/raw/filmstrip.c
new file mode 100644
index 0000000..1687a31
--- /dev/null
+++ b/libs/rs/java/Film/res/raw/filmstrip.c
@@ -0,0 +1,126 @@
+// Fountain test script
+
+#pragma version(1)
+#pragma stateVertex(PV)
+#pragma stateFragment(PFBackground)
+#pragma stateFragmentStore(PFSBackground)
+
+/*
+typedef struct FilmScriptUserEnvRec {
+    RsAllocation tex[13];
+    int32_t triangleOffsets[64];
+    float triangleOffsetsTex[64];
+    int32_t triangleOffsetsCount;
+} FilmScriptUserEnv; 
+*/ 
+
+// The script enviroment has 3 env allocations.
+// bank0: (r) The enviroment structure
+// bank1: (r) The position information
+// bank2: (rw) The temporary texture state
+
+int main(int index) 
+{
+    int f1,f2,f3,f4, f5,f6,f7,f8, f9,f10,f11,f12, f13,f14,f15,f16;
+    int g1,g2,g3,g4, g5,g6,g7,g8, g9,g10,g11,g12, g13,g14,g15,g16;
+    int float_1;
+    int float_0;
+    int float_2;
+    int float_90;
+    int float_0_5;
+    int trans;  // float
+    int rot;   // float
+    int x;
+    float focusPos;  // float
+    int focusID;
+    int lastFocusID;
+    int imgCount;
+
+    float_2 = intToFloat(2);
+    float_1 = intToFloat(1);
+    float_0 = intToFloat(0);
+    float_90= intToFloat(90);
+    float_0_5 = fixedtoFloat(0x8000);
+
+    trans = loadF(1, 0);
+    rot = loadF(1, 1);
+
+    matrixLoadScale(&f16, float_2, float_2, float_2);
+    matrixTranslate(&f16, 0, 0, trans);
+    matrixRotate(&f16, float_90, 0, 0, float_1);
+    matrixRotate(&f16, rot, float_1, 0, 0);
+    storeEnvMatrix(3, 0, &f16);
+
+    //materialDiffuse(con, 0.0f, 0.0f, 0.0f, 1.0f);
+    //materialSpecular(con, 0.5f, 0.5f, 0.5f, 0.5f);
+    //materialShininess(intToFloat(20));
+    //lightPosition(con, 0.2f, -0.2f, -2.0f, 0.0f);
+    //enable(con, GL_LIGHTING);
+    renderTriangleMesh(NAMED_mesh);
+
+
+
+    //int imgId = 0;
+
+    contextBindProgramFragmentStore(NAMED_PFImages);
+    contextBindProgramFragment(NAMED_PFSImages);
+
+    //focusPos = loadF(1, 2);
+    //focusID = 0;
+    //lastFocusID = loadI32(2, 0);
+    //imgCount = 13;
+
+    /*
+    disable(GL_LIGHTING);
+
+
+    if (trans > (-.3)) {
+        focusID = -1.0 - focusPos;
+        if (focusID >= imgCount) {
+            focusID = -1;
+        }
+    } else {
+        focusID = -1;
+    }
+
+    if (focusID != lastFocusID) {
+        if (lastFocusID >= 0) {
+            uploadToTexture(con, env->tex[lastFocusID], 1);
+        }
+        if (focusID >= 0) {
+            uploadToTexture(con, env->tex[focusID], 0);
+        }
+    }
+    storeEnvI32(con, 2, 0, focusID);
+
+
+    for (imgId=1; imgId <= imgCount; imgId++) {
+        float pos = focusPos + imgId + .4f;
+        int offset = (int)floor(pos*2);
+        pos -= 0.75;
+    
+        offset += env->triangleOffsetsCount / 2;
+    
+        if ((offset < 0) || (offset >= env->triangleOffsetsCount)) {
+            continue;
+        }
+    
+        int start = offset -2;
+        int end = offset + 2;
+    
+        if (start < 0) {
+            start = 0;
+        }
+        if (end > env->triangleOffsetsCount) {
+            end = env->triangleOffsetsCount;
+        }
+    
+        programFragmentBindTexture(con, env->fpImages, 0, env->tex[imgId - 1]);
+        matrixLoadTranslate(con, &m, -pos - env->triangleOffsetsTex[env->triangleOffsetsCount / 2], 0, 0); 
+        storeEnvMatrix(con, 3, RS_PROGRAM_VERTEX_TEXTURE_OFFSET, &m);
+        renderTriangleMeshRange(con, env->mesh, env->triangleOffsets[start], env->triangleOffsets[end] - env->triangleOffsets[start]);
+    } 
+*/
+    return 0;
+}
+
diff --git a/libs/rs/java/Film/src/com/android/film/Film.java b/libs/rs/java/Film/src/com/android/film/Film.java
new file mode 100644
index 0000000..6e99816
--- /dev/null
+++ b/libs/rs/java/Film/src/com/android/film/Film.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.film;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class Film extends Activity {
+    //EventListener mListener = new EventListener();
+
+    private static final String LOG_TAG = "libRS_jni";
+    private static final boolean DEBUG  = false;
+    private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+
+    private FilmView mView;
+
+    // get the current looper (from your Activity UI thread for instance
+
+
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new FilmView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mView.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onPause();
+        mView.onPause();
+
+        Runtime.getRuntime().exit(0);
+    }
+
+
+    static void log(String message) {
+        if (LOG_ENABLED) {
+            Log.v(LOG_TAG, message);
+        }
+    }
+
+
+}
+
diff --git a/libs/rs/java/Film/src/com/android/film/FilmRS.java b/libs/rs/java/Film/src/com/android/film/FilmRS.java
new file mode 100644
index 0000000..2711bf0
--- /dev/null
+++ b/libs/rs/java/Film/src/com/android/film/FilmRS.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.film;
+
+import java.io.Writer;
+
+import android.renderscript.RenderScript;
+import android.renderscript.ProgramVertexAlloc;
+import android.renderscript.Matrix;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class FilmRS {
+
+    public FilmRS() {
+    }
+
+    public void init(RenderScript rs, Resources res, int width, int height) {
+        mRS = rs;
+        mRes = res;
+        initNamed();
+        initRS();
+    }
+
+    public void setFilmStripPosition(int x, int y)
+    {
+        if (x < 50) {
+            x = 50;
+        }
+        if (x > 270) {
+            x = 270;
+        }
+    
+        float anim = ((float)x-50) / 270.f;
+        mBufferPos[0] = 2f * anim + 0.5f;   // translation
+        mBufferPos[1] = (anim * 40);  // rotation
+        mBufferPos[2] = ((float)y) / 16.f - 8;  // focusPos
+        mAllocPos.data(mBufferPos);
+    }
+
+
+    private Resources mRes;
+    private RenderScript mRS;
+    private RenderScript.Script mScriptStrip;
+    private RenderScript.Script mScriptImage;
+    private RenderScript.Element mElementVertex;
+    private RenderScript.Element mElementIndex;
+    private RenderScript.Sampler mSampler;
+    private RenderScript.ProgramFragmentStore mPFSBackground;
+    private RenderScript.ProgramFragmentStore mPFSImages;
+    private RenderScript.ProgramFragment mPFBackground;
+    private RenderScript.ProgramFragment mPFImages;
+    private RenderScript.ProgramVertex mPV;
+    private ProgramVertexAlloc mPVA;
+
+    private RenderScript.Allocation mAllocEnv;
+    private RenderScript.Allocation mAllocPos;
+    private RenderScript.Allocation mAllocState;
+    private RenderScript.Allocation mAllocPV;
+    private RenderScript.TriangleMesh mMesh;
+    private RenderScript.Light mLight;
+
+    private float[] mBufferPos;
+    private float[] mBufferPV;
+
+    private void initNamed() {
+        mElementVertex = mRS.elementGetPredefined(
+            RenderScript.ElementPredefined.NORM_ST_XYZ_F32);
+        mElementIndex = mRS.elementGetPredefined(
+            RenderScript.ElementPredefined.INDEX_16);
+
+        mRS.triangleMeshBegin(mElementVertex, mElementIndex);
+        FilmStripMesh fsm = new FilmStripMesh();
+        fsm.init(mRS);
+        mMesh = mRS.triangleMeshCreate();
+        mMesh.setName("mesh");
+        Log.e("rs", "Done loading strips");
+
+
+        mRS.samplerBegin();
+        mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN,
+                       RenderScript.SamplerValue.LINEAR_MIP_LINEAR);
+        mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_S,
+                       RenderScript.SamplerValue.CLAMP);
+        mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_T,
+                       RenderScript.SamplerValue.CLAMP);
+        mSampler = mRS.samplerCreate();
+
+        mRS.programFragmentBegin(null, null);
+        mPFBackground = mRS.programFragmentCreate();
+        mPFBackground.setName("PFBackground");
+
+        mRS.programFragmentBegin(null, null);
+        mRS.programFragmentSetTexEnable(0, true);
+        //mRS.programFragmentSetEnvMode(0, RS_TEX_ENV_MODE_REPLACE);
+        //rsProgramFragmentSetType(0, gEnv.tex[0]->getType());
+        mPFImages = mRS.programFragmentCreate();
+        mPFImages.setName("PFImages");
+        mPFImages.bindSampler(mSampler, 0);
+
+        mRS.programFragmentStoreBegin(null, null);
+        mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.LESS);
+        mRS.programFragmentStoreDitherEnable(true);
+        mPFSBackground = mRS.programFragmentStoreCreate();
+        mPFSBackground.setName("PFSBackground");
+
+        mRS.programFragmentStoreBegin(null, null);
+        mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.EQUAL);
+        mRS.programFragmentStoreDitherEnable(false);
+        mRS.programFragmentStoreDepthMask(false);
+        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE, 
+                                          RenderScript.BlendDstFunc.ONE);
+        mPFSImages = mRS.programFragmentStoreCreate();
+        mPFSImages.setName("PFSImages");
+
+        mRS.programVertexBegin(null, null);
+        mRS.programVertexSetTextureMatrixEnable(true);
+        mPV = mRS.programVertexCreate();
+        mPV.setName("PV");
+
+        mRS.lightBegin();
+        mLight = mRS.lightCreate();
+        mLight.setPosition(0, -0.5f, -1.0f);
+
+        Log.e("rs", "Done loading named");
+    }
+
+
+    private Bitmap mBackground;
+
+    int mParams[] = new int[10];
+
+    private void initRS() {
+        int partCount = 1024;
+
+        mRS.scriptCBegin();
+        mRS.scriptCSetClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+        mRS.scriptCSetScript(mRes, R.raw.filmstrip);
+        mRS.scriptCSetRoot(true);
+        mScriptStrip = mRS.scriptCCreate();
+
+        mBufferPos = new float[3];
+        mAllocPos = mRS.allocationCreatePredefSized(
+            RenderScript.ElementPredefined.USER_FLOAT, 
+            mBufferPos.length);
+
+        mPVA = new ProgramVertexAlloc(mRS);
+        mPV.bindAllocation(0, mPVA.mAlloc);
+        mPVA.setupProjectionNormalized(320, 480);
+
+
+        mScriptStrip.bindAllocation(mAllocPos, 1);
+       //mScriptStrip.bindAllocation(gStateAlloc, 2);
+        mScriptStrip.bindAllocation(mPVA.mAlloc, 3);
+
+
+        //mIntAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, 10);
+        //mPartAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, partCount * 3 * 3);
+        //mPartAlloc.setName("PartBuffer");
+        //mVertAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, partCount * 5 + 1);
+/*
+        {
+            Resources res = getResources();
+            Drawable d = res.getDrawable(R.drawable.gadgets_clock_mp3);
+            BitmapDrawable bd = (BitmapDrawable)d;
+            Bitmap b = bd.getBitmap();
+            mTexture = mRS.allocationCreateFromBitmap(b,
+                                                      RenderScript.ElementPredefined.RGB_565,
+                                                      true);
+            mTexture.uploadToTexture(0);
+        }
+
+        mRS.programFragmentStoreBegin(null, null);
+        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.SRC_ALPHA, RenderScript.BlendDstFunc.ONE);
+        mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS);
+        mPFS = mRS.programFragmentStoreCreate();
+        mPFS.setName("MyBlend");
+        mRS.contextBindProgramFragmentStore(mPFS);
+
+        mRS.samplerBegin();
+        mRS.samplerSet(RenderScript.SamplerParam.FILTER_MAG, RenderScript.SamplerValue.LINEAR);
+        mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN, RenderScript.SamplerValue.LINEAR);
+        mSampler = mRS.samplerCreate();
+
+
+        mParams[0] = 0;
+        mParams[1] = partCount;
+        mParams[2] = 0;
+        mParams[3] = 0;
+        mParams[4] = 0;
+        mIntAlloc.data(mParams);
+
+        int t2[] = new int[partCount * 4*3];
+        for (int ct=0; ct < t2.length; ct++) {
+            t2[ct] = 0;
+        }
+        mPartAlloc.data(t2);
+        */
+
+        setFilmStripPosition(0, 0);
+
+        mRS.contextBindRootScript(mScriptStrip);
+    }
+}
+
+
+
diff --git a/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java b/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java
new file mode 100644
index 0000000..02bffd8
--- /dev/null
+++ b/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.film;
+
+import java.io.Writer;
+import java.lang.Math;
+import android.util.Log;
+
+import android.renderscript.RenderScript;
+
+
+class FilmStripMesh {
+
+    class Vertex {
+        float nx;
+        float ny;
+        float nz;
+        float s;
+        float t;
+        float x;
+        float y;
+        float z;
+
+        Vertex() {
+            nx = 0;
+            ny = 0;
+            nz = 0;
+            s = 0;
+            t = 0;
+            x = 0;
+            y = 0;
+            z = 0;
+        }
+
+        void xyz(float _x, float _y, float _z) {
+            x = _x;
+            y = _y;
+            z = _z;
+        }
+
+        void nxyz(float _x, float _y, float _z) {
+            nx = _x;
+            ny = _y;
+            nz = _z;
+        }
+
+        void st(float _s, float _t) {
+            s = _s;
+            t = _t;
+        }
+
+        void computeNorm(Vertex v1, Vertex v2) {
+            float dx = v1.x - v2.x;
+            float dy = v1.y - v2.y;
+            float dz = v1.z - v2.z;
+            float len = (float)java.lang.Math.sqrt(dx*dx + dy*dy + dz*dz);
+            dx /= len;
+            dy /= len;
+            dz /= len;
+        
+            nx = dx * dz;
+            ny = dy * dz;
+            nz = (float)java.lang.Math.sqrt(dx*dx + dy*dy);
+        
+            len = (float)java.lang.Math.sqrt(nx*nx + ny*ny + nz*nz);
+            nx /= len;
+            ny /= len;
+            nz /= len;
+        }
+
+        void addToRS(RenderScript rs) {
+            rs.triangleMeshAddVertex_XYZ_ST_NORM(x, y, z, s, t, nx, ny, nz);
+        }
+    }
+
+    int[] mTriangleOffsets;
+    float[] mTriangleOffsetsTex;
+    int mTriangleOffsetsCount;
+
+    void init(RenderScript rs)
+    {
+        float vtx[] = new float[] {
+            60.431003f, 124.482050f,
+            60.862074f, 120.872604f,
+            61.705303f, 117.336662f,
+            62.949505f, 113.921127f,
+            64.578177f, 110.671304f,
+            66.569716f, 107.630302f,
+            68.897703f, 104.838457f,
+            71.531259f, 102.332803f,
+            74.435452f, 100.146577f,
+            77.571757f, 98.308777f,
+            80.898574f, 96.843781f,
+            84.371773f, 95.771023f,
+            87.945283f, 95.104731f,
+            98.958994f, 95.267098f,
+            109.489523f, 98.497596f,
+            118.699582f, 104.539366f,
+            125.856872f, 112.912022f,
+            130.392311f, 122.949849f,
+            131.945283f, 133.854731f,
+            130.392311f, 144.759613f,
+            125.856872f, 154.797439f,
+            118.699582f, 163.170096f,
+            109.489523f, 169.211866f,
+            98.958994f, 172.442364f,
+            87.945283f, 172.604731f,
+            72.507313f, 172.672927f,
+            57.678920f, 168.377071f,
+            44.668135f, 160.067134f,
+            34.534908f, 148.420104f,
+            28.104767f, 134.384831f,
+            25.901557f, 119.104731f,
+            28.104767f, 103.824631f,
+            34.534908f, 89.789358f,
+            44.668135f, 78.142327f,
+            57.678920f, 69.832390f,
+            72.507313f, 65.536534f,
+            87.945283f, 65.604731f,
+            106.918117f, 65.688542f,
+            125.141795f, 60.409056f,
+            141.131686f, 50.196376f,
+            153.585137f, 35.882502f,
+            161.487600f, 18.633545f,
+            164.195283f, -0.145269f,
+            161.487600f, -18.924084f,
+            153.585137f, -36.173040f,
+            141.131686f, -50.486914f,
+            125.141795f, -60.699594f,
+            106.918117f, -65.979081f,
+            87.945283f, -65.895269f,
+            80f, -65.895269f,
+            60f, -65.895269f,
+            40f, -65.895269f,
+            20f, -65.895269f,
+            0f, -65.895269f,
+            -20f, -65.895269f,
+            -40f, -65.895269f,
+            -60f, -65.895269f,
+            -80f, -65.895269f,
+            -87.945283f, -65.895269f,
+            -106.918117f, -65.979081f,
+            -125.141795f, -60.699594f,
+            -141.131686f, -50.486914f,
+            -153.585137f, -36.173040f,
+            -161.487600f, -18.924084f,
+            -164.195283f, -0.145269f,
+            -161.487600f, 18.633545f,
+             -153.585137f, 35.882502f,
+             -141.131686f, 50.196376f,
+             -125.141795f, 60.409056f,
+             -106.918117f, 65.688542f,
+             -87.945283f, 65.604731f,
+             -72.507313f, 65.536534f,
+             -57.678920f, 69.832390f,
+             -44.668135f, 78.142327f,
+             -34.534908f, 89.789358f,
+             -28.104767f, 103.824631f,
+             -25.901557f, 119.104731f,
+             -28.104767f, 134.384831f,
+             -34.534908f, 148.420104f,
+             -44.668135f, 160.067134f,
+             -57.678920f, 168.377071f,
+             -72.507313f, 172.672927f,
+             -87.945283f, 172.604731f,
+             -98.958994f, 172.442364f,
+             -109.489523f, 169.211866f,
+             -118.699582f, 163.170096f,
+             -125.856872f, 154.797439f,
+             -130.392311f, 144.759613f,
+             -131.945283f, 133.854731f,
+             -130.392311f, 122.949849f,
+             -125.856872f, 112.912022f,
+             -118.699582f, 104.539366f,
+             -109.489523f, 98.497596f,
+             -98.958994f, 95.267098f,
+             -87.945283f, 95.104731f,
+             -84.371773f, 95.771023f,
+             -80.898574f, 96.843781f,
+             -77.571757f, 98.308777f,
+             -74.435452f, 100.146577f,
+             -71.531259f, 102.332803f,
+             -68.897703f, 104.838457f,
+             -66.569716f, 107.630302f,
+             -64.578177f, 110.671304f,
+             -62.949505f, 113.921127f,
+             -61.705303f, 117.336662f,
+             -60.862074f, 120.872604f,
+             -60.431003f, 124.482050f
+        };
+    
+    
+        mTriangleOffsets = new int[64];
+        mTriangleOffsetsTex = new float[64];
+    
+        mTriangleOffsets[0] = 0;
+        mTriangleOffsetsCount = 1;
+
+        Vertex t = new Vertex();
+        t.nxyz(1, 0, 0);
+        int count = vtx.length / 2;
+
+        float runningS = 0;
+        for (int ct=0; ct < (count-1); ct++) {
+            t.x = -vtx[ct*2] / 100.f;
+            t.z = vtx[ct*2+1] / 100.f;
+            t.s = runningS;
+            t.nx =  (vtx[ct*2+3] - vtx[ct*2 +1]);
+            t.ny =  (vtx[ct*2+2] - vtx[ct*2   ]);
+            float len = (float)java.lang.Math.sqrt(t.nx * t.nx + t.ny * t.ny);
+            runningS += len / 100;
+            t.nx /= len;
+            t.ny /= len;
+            t.y = -0.5f;
+            t.t = 0;
+            //Log.e("xx", "vtx " + t.x + "  " + t.y + "  " + t.z);
+            t.addToRS(rs);
+            t.y = .5f;
+            t.t = 1;
+            t.addToRS(rs);
+
+            //LOGE(" %f", runningS);
+            if((runningS*2) > mTriangleOffsetsCount) {
+                //LOGE("**** img %i  %i", gTriangleOffsetsCount, ct*2);
+                mTriangleOffsets[mTriangleOffsetsCount] = ct*2;
+                mTriangleOffsetsTex[mTriangleOffsetsCount] = t.s;
+                mTriangleOffsetsCount ++;
+            }
+        }
+
+        count = (count * 2 - 2);
+        for (int ct=0; ct < (count-2); ct+= 2) {
+            rs.triangleMeshAddTriangle(ct, ct+1, ct+2);
+            rs.triangleMeshAddTriangle(ct+1, ct+3, ct+2);
+        }
+    }
+
+
+}
+
diff --git a/libs/rs/java/Film/src/com/android/film/FilmView.java b/libs/rs/java/Film/src/com/android/film/FilmView.java
new file mode 100644
index 0000000..a743b1b
--- /dev/null
+++ b/libs/rs/java/Film/src/com/android/film/FilmView.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.film;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class FilmView extends RSSurfaceView {
+
+    public FilmView(Context context) {
+        super(context);
+
+        //setFocusable(true);
+    }
+
+    private RenderScript mRS;
+    private FilmRS mRender;
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+
+        mRS = createRenderScript();
+        mRender = new FilmRS();
+        mRender.init(mRS, getResources(), w, h);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event)
+    {
+        // break point at here
+        // this method doesn't work when 'extends View' include 'extends ScrollView'.
+        return super.onKeyDown(keyCode, event);
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        boolean ret = true;
+        int act = ev.getAction();
+        if (act == ev.ACTION_UP) {
+            ret = false;
+        }
+        mRender.setFilmStripPosition((int)ev.getX(), (int)ev.getY());
+        return ret;
+    }
+}
+
+
diff --git a/libs/rs/java/Fountain/Android.mk b/libs/rs/java/Fountain/Android.mk
new file mode 100644
index 0000000..af3d5fc
--- /dev/null
+++ b/libs/rs/java/Fountain/Android.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+
+LOCAL_PACKAGE_NAME := Fountain
+
+include $(BUILD_PACKAGE)
diff --git a/libs/rs/java/Fountain/AndroidManifest.xml b/libs/rs/java/Fountain/AndroidManifest.xml
new file mode 100644
index 0000000..a10938b
--- /dev/null
+++ b/libs/rs/java/Fountain/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.fountain">
+    <application android:label="Fountain">
+        <activity android:name="Fountain"
+                  android:theme="@android:style/Theme.Black.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png b/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png
new file mode 100755
index 0000000..e91bfb4
--- /dev/null
+++ b/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png
Binary files differ
diff --git a/libs/rs/java/Fountain/res/raw/fountain.c b/libs/rs/java/Fountain/res/raw/fountain.c
new file mode 100644
index 0000000..6919565
--- /dev/null
+++ b/libs/rs/java/Fountain/res/raw/fountain.c
@@ -0,0 +1,110 @@
+// Fountain test script
+
+#pragma version(1)
+#pragma stateVertex(default)
+#pragma stateFragment(PgmFragBackground)
+#pragma stateFragmentStore(PFSReplace)
+
+
+int main(int launchID) {
+    int count, touch, x, y, rate, maxLife, lifeShift;
+    int life;
+    int ct, ct2;
+    int newPart;
+    int drawCount;
+    int dx, dy, idx;
+    int posx,posy;
+    int c;
+    int srcIdx;
+    int dstIdx;
+
+    count = loadI32(0, 1);
+    touch = loadI32(0, 2);
+    x = loadI32(0, 3);
+    y = loadI32(0, 4);
+
+    rate = 4;
+    maxLife = (count / rate) - 1;
+    lifeShift = 0;
+    {
+        life = maxLife;
+        while (life > 255) {
+            life = life >> 1;
+            lifeShift ++;
+        }
+    }
+
+    drawRect(0, 256, 0, 512);
+    contextBindProgramFragment(NAMED_PgmFragParts);
+    contextBindProgramFragmentStore(NAMED_PFSBlend);
+
+    if (touch) {
+        newPart = loadI32(2, 0);
+        for (ct2=0; ct2<rate; ct2++) {
+            dx = scriptRand(0x10000) - 0x8000;
+            dy = scriptRand(0x10000) - 0x8000;
+
+            idx = newPart * 5 + 1;
+            storeI32(2, idx, dx);
+            storeI32(2, idx + 1, dy);
+            storeI32(2, idx + 2, maxLife);
+            storeI32(2, idx + 3, x << 16);
+            storeI32(2, idx + 4, y << 16);
+
+            newPart++;
+            if (newPart >= count) {
+                newPart = 0;
+            }
+        }
+        storeI32(2, 0, newPart);
+    }
+
+    drawCount = 0;
+    for (ct=0; ct < count; ct++) {
+        srcIdx = ct * 5 + 1;
+
+        dx = loadI32(2, srcIdx);
+        dy = loadI32(2, srcIdx + 1);
+        life = loadI32(2, srcIdx + 2);
+        posx = loadI32(2, srcIdx + 3);
+        posy = loadI32(2, srcIdx + 4);
+
+        if (life) {
+            if (posy < (480 << 16)) {
+                dstIdx = drawCount * 9;
+                c = 0xffafcf | ((life >> lifeShift) << 24);
+
+                storeU32(1, dstIdx, c);
+                storeI32(1, dstIdx + 1, posx);
+                storeI32(1, dstIdx + 2, posy);
+
+                storeU32(1, dstIdx + 3, c);
+                storeI32(1, dstIdx + 4, posx + 0x10000);
+                storeI32(1, dstIdx + 5, posy + dy * 4);
+
+                storeU32(1, dstIdx + 6, c);
+                storeI32(1, dstIdx + 7, posx - 0x10000);
+                storeI32(1, dstIdx + 8, posy + dy * 4);
+                drawCount ++;
+            } else {
+                if (dy > 0) {
+                    dy = (-dy) >> 1;
+                }
+            }
+
+            posx = posx + dx;
+            posy = posy + dy;
+            dy = dy + 0x400;
+            life --;
+
+            //storeI32(2, srcIdx, dx);
+            storeI32(2, srcIdx + 1, dy);
+            storeI32(2, srcIdx + 2, life);
+            storeI32(2, srcIdx + 3, posx);
+            storeI32(2, srcIdx + 4, posy);
+        }
+    }
+
+    drawTriangleArray(NAMED_PartBuffer, drawCount);
+    return 1;
+}
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java b/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java
new file mode 100644
index 0000000..58c78fa
--- /dev/null
+++ b/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.fountain;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class Fountain extends Activity {
+    //EventListener mListener = new EventListener();
+
+    private static final String LOG_TAG = "libRS_jni";
+    private static final boolean DEBUG  = false;
+    private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+
+    private FountainView mView;
+
+    // get the current looper (from your Activity UI thread for instance
+
+
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new FountainView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mView.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onPause();
+        mView.onPause();
+
+        Runtime.getRuntime().exit(0);
+    }
+
+
+    static void log(String message) {
+        if (LOG_ENABLED) {
+            Log.v(LOG_TAG, message);
+        }
+    }
+
+
+}
+
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
new file mode 100644
index 0000000..745635f
--- /dev/null
+++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.fountain;
+
+import java.io.Writer;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.renderscript.RenderScript;
+import android.renderscript.ProgramVertexAlloc;
+import android.util.Log;
+
+public class FountainRS {
+
+    public FountainRS() {
+    }
+
+    public void init(RenderScript rs, Resources res, int width, int height) {
+        mRS = rs;
+        mRes = res;
+        initRS();
+    }
+
+    public void newTouchPosition(int x, int y) {
+        mParams[0] = 1;
+        mParams[1] = x;
+        mParams[2] = y;
+        mIntAlloc.subData1D(2, 3, mParams);
+    }
+
+
+    /////////////////////////////////////////
+
+    private Resources mRes;
+
+    private RenderScript mRS;
+    private RenderScript.Allocation mIntAlloc;
+    private RenderScript.Allocation mPartAlloc;
+    private RenderScript.Allocation mVertAlloc;
+    private RenderScript.Script mScript;
+    private RenderScript.ProgramFragmentStore mPFS;
+    private RenderScript.ProgramFragmentStore mPFS2;
+    private RenderScript.ProgramFragment mPF;
+    private RenderScript.ProgramFragment mPF2;
+    private RenderScript.Allocation mTexture;
+    private RenderScript.Sampler mSampler;
+
+    private Bitmap mBackground;
+
+    int mParams[] = new int[10];
+
+    private void initRS() {
+        int partCount = 1024;
+
+        mIntAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, 10);
+        mPartAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, partCount * 3 * 3);
+        mPartAlloc.setName("PartBuffer");
+        mVertAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, partCount * 5 + 1);
+
+        {
+            Drawable d = mRes.getDrawable(R.drawable.gadgets_clock_mp3);
+            BitmapDrawable bd = (BitmapDrawable)d;
+            Bitmap b = bd.getBitmap();
+            mTexture = mRS.allocationCreateFromBitmap(b,
+                                                      RenderScript.ElementPredefined.RGB_565,
+                                                      false);
+            mTexture.uploadToTexture(0);
+        }
+
+        mRS.programFragmentStoreBegin(null, null);
+        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.SRC_ALPHA, RenderScript.BlendDstFunc.ONE);
+        mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS);
+        mRS.programFragmentStoreDepthMask(false);
+        mRS.programFragmentStoreDitherEnable(false);
+        mPFS = mRS.programFragmentStoreCreate();
+        mPFS.setName("PFSBlend");
+
+        mRS.programFragmentStoreBegin(null, null);
+        mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS);
+        mRS.programFragmentStoreDepthMask(false);
+        mRS.programFragmentStoreDitherEnable(false);
+        mPFS2 = mRS.programFragmentStoreCreate();
+        mPFS2.setName("PFSReplace");
+        mRS.contextBindProgramFragmentStore(mPFS2);
+
+        mRS.samplerBegin();
+        mRS.samplerSet(RenderScript.SamplerParam.FILTER_MAG, RenderScript.SamplerValue.NEAREST);
+        mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN, RenderScript.SamplerValue.NEAREST);
+        mSampler = mRS.samplerCreate();
+
+
+        mRS.programFragmentBegin(null, null);
+        mPF = mRS.programFragmentCreate();
+        mPF.setName("PgmFragParts");
+
+        mRS.programFragmentBegin(null, null);
+        mRS.programFragmentSetTexEnable(0, true);
+        mPF2 = mRS.programFragmentCreate();
+        mRS.contextBindProgramFragment(mPF2);
+        mPF2.bindTexture(mTexture, 0);
+        mPF2.bindSampler(mSampler, 0);
+        mPF2.setName("PgmFragBackground");
+
+
+        mParams[0] = 0;
+        mParams[1] = partCount;
+        mParams[2] = 0;
+        mParams[3] = 0;
+        mParams[4] = 0;
+        mIntAlloc.data(mParams);
+
+        int t2[] = new int[partCount * 4*3];
+        for (int ct=0; ct < t2.length; ct++) {
+            t2[ct] = 0;
+        }
+        mPartAlloc.data(t2);
+
+        mRS.scriptCBegin();
+        mRS.scriptCSetClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+        mRS.scriptCSetScript(mRes, R.raw.fountain);
+        mRS.scriptCSetRoot(true);
+        mScript = mRS.scriptCCreate();
+
+        mScript.bindAllocation(mIntAlloc, 0);
+        mScript.bindAllocation(mPartAlloc, 1);
+        mScript.bindAllocation(mVertAlloc, 2);
+        mRS.contextBindRootScript(mScript);
+    }
+
+}
+
+
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
new file mode 100644
index 0000000..be8b24e
--- /dev/null
+++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.fountain;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class FountainView extends RSSurfaceView {
+
+    public FountainView(Context context) {
+        super(context);
+
+        //setFocusable(true);
+    }
+
+    private RenderScript mRS;
+    private FountainRS mRender;
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+
+        mRS = createRenderScript();
+        mRender = new FountainRS();
+        mRender.init(mRS, getResources(), w, h);
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        boolean ret = true;
+        int act = ev.getAction();
+        if (act == ev.ACTION_UP) {
+            ret = false;
+        }
+        mRender.newTouchPosition((int)ev.getX(), (int)ev.getY());
+        return ret;
+    }
+}
+
+
diff --git a/libs/rs/java/RenderScript/Android.mk b/libs/rs/java/RenderScript/Android.mk
new file mode 100644
index 0000000..616fbd2
--- /dev/null
+++ b/libs/rs/java/RenderScript/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2008 Esmertec AG.
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+# the library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+            $(call all-subdir-java-files) 
+
+LOCAL_MODULE:= android.renderscript
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/libs/rs/java/RenderScript/android/renderscript/Matrix.java b/libs/rs/java/RenderScript/android/renderscript/Matrix.java
new file mode 100644
index 0000000..79b60d0
--- /dev/null
+++ b/libs/rs/java/RenderScript/android/renderscript/Matrix.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+public class Matrix {
+
+    public Matrix() {
+        mMat = new float[16];
+        loadIdentity();
+    }
+
+    public float get(int i, int j) {
+        return mMat[i*4 + j];
+    }
+
+    public void set(int i, int j, float v) {
+        mMat[i*4 + j] = v;
+    }
+
+    public void loadIdentity() {
+        mMat[0] = 1;
+        mMat[1] = 0;
+        mMat[2] = 0;
+        mMat[3] = 0;
+
+        mMat[4] = 0;
+        mMat[5] = 1;
+        mMat[6] = 0;
+        mMat[7] = 0;
+    
+        mMat[8] = 0;
+        mMat[9] = 0;
+        mMat[10] = 1;
+        mMat[11] = 0;
+
+        mMat[12] = 0;
+        mMat[13] = 0;
+        mMat[14] = 0;
+        mMat[15] = 1;
+    }
+
+    public void load(Matrix src) {
+        mMat = src.mMat;
+    }
+
+    public void loadRotate(float rot, float x, float y, float z) {
+        float c, s;
+        mMat[3] = 0;
+        mMat[7] = 0;
+        mMat[11]= 0;
+        mMat[12]= 0;
+        mMat[13]= 0;
+        mMat[14]= 0;
+        mMat[15]= 1;
+        rot *= (float)(java.lang.Math.PI / 180.0f);
+        c = (float)java.lang.Math.cos(rot);
+        s = (float)java.lang.Math.sin(rot);
+    
+        float len = (float)java.lang.Math.sqrt(x*x + y*y + z*z);
+        if (!(len != 1)) {
+            float recipLen = 1.f / len;
+            x *= recipLen;
+            y *= recipLen;
+            z *= recipLen;
+        }
+        float nc = 1.0f - c;
+        float xy = x * y;
+        float yz = y * z;
+        float zx = z * x;
+        float xs = x * s;
+        float ys = y * s;
+        float zs = z * s;		
+        mMat[ 0] = x*x*nc +  c;
+        mMat[ 4] =  xy*nc - zs;
+        mMat[ 8] =  zx*nc + ys;
+        mMat[ 1] =  xy*nc + zs;
+        mMat[ 5] = y*y*nc +  c;
+        mMat[ 9] =  yz*nc - xs;
+        mMat[ 2] =  zx*nc - ys;
+        mMat[ 6] =  yz*nc + xs;
+        mMat[10] = z*z*nc +  c;
+    }
+
+    public void loadScale(float x, float y, float z) {
+        loadIdentity();
+        mMat[0] = x;
+        mMat[5] = y;
+        mMat[10] = z;
+    }
+    
+    public void loadTranslate(float x, float y, float z) {
+        loadIdentity();
+        mMat[12] = x;
+        mMat[13] = y;
+        mMat[14] = z;
+    }
+
+    public void loadMultiply(Matrix lhs, Matrix rhs) {
+        for (int i=0 ; i<4 ; i++) {
+            float ri0 = 0;
+            float ri1 = 0;
+            float ri2 = 0;
+            float ri3 = 0;
+            for (int j=0 ; j<4 ; j++) {
+                float rhs_ij = rhs.get(i,j);
+                ri0 += lhs.get(j,0) * rhs_ij;
+                ri1 += lhs.get(j,1) * rhs_ij;
+                ri2 += lhs.get(j,2) * rhs_ij;
+                ri3 += lhs.get(j,3) * rhs_ij;
+            }
+            set(i,0, ri0);
+            set(i,1, ri1);
+            set(i,2, ri2);
+            set(i,3, ri3);
+        }
+    }
+
+    public void loadOrtho(float l, float r, float b, float t, float n, float f) {
+        loadIdentity();
+        mMat[0] = 2 / (r - l);
+        mMat[5] = 2 / (t - b);
+        mMat[10]= -2 / (f - n);
+        mMat[12]= -(r + l) / (r - l);
+        mMat[13]= -(t + b) / (t - b);
+        mMat[14]= -(f + n) / (f - n);
+    }
+
+    public void loadFrustum(float l, float r, float b, float t, float n, float f) {
+        loadIdentity();
+        mMat[0] = 2 * n / (r - l);
+        mMat[5] = 2 * n / (t - b);
+        mMat[8] = (r + l) / (r - l);
+        mMat[9] = (t + b) / (t - b);
+        mMat[10]= -(f + n) / (f - n);
+        mMat[11]= -1;
+        mMat[14]= -2*f*n / (f - n);
+        mMat[15]= 0;
+    }
+
+    public void multiply(Matrix rhs) {
+        Matrix tmp = new Matrix();
+        tmp.loadMultiply(this, rhs);
+        load(tmp);
+    }
+    public void rotate(float rot, float x, float y, float z) {
+        Matrix tmp = new Matrix();
+        tmp.loadRotate(rot, x, y, z);
+        multiply(tmp);
+    }
+    public void scale(float x, float y, float z) {
+        Matrix tmp = new Matrix();
+        tmp.loadScale(x, y, z);
+        multiply(tmp);
+    }
+    public void translate(float x, float y, float z) {
+        Matrix tmp = new Matrix();
+        tmp.loadTranslate(x, y, z);
+        multiply(tmp);
+    }
+
+
+
+    float[] mMat;
+
+}
+
+
+
+
+
diff --git a/libs/rs/java/RenderScript/android/renderscript/ProgramVertexAlloc.java b/libs/rs/java/RenderScript/android/renderscript/ProgramVertexAlloc.java
new file mode 100644
index 0000000..020ddb2
--- /dev/null
+++ b/libs/rs/java/RenderScript/android/renderscript/ProgramVertexAlloc.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+public class ProgramVertexAlloc {
+    public static final int MODELVIEW_OFFSET = 0;
+    public static final int PROJECTION_OFFSET = 16;
+    public static final int TEXTURE_OFFSET = 32;
+
+    Matrix mModel;
+    Matrix mProjection;
+    Matrix mTexture;
+
+    public RenderScript.Allocation mAlloc;
+
+    public ProgramVertexAlloc(RenderScript rs) {
+        mModel = new Matrix();
+        mProjection = new Matrix();
+        mTexture = new Matrix();
+
+        mAlloc = rs.allocationCreatePredefSized(
+            RenderScript.ElementPredefined.USER_FLOAT, 
+            48);
+
+        mAlloc.subData1D(MODELVIEW_OFFSET, 16, mModel.mMat);
+        mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+        mAlloc.subData1D(TEXTURE_OFFSET, 16, mTexture.mMat);
+    }
+
+    public void loadModelview(Matrix m) {
+        mModel = m;
+        mAlloc.subData1D(MODELVIEW_OFFSET, 16, m.mMat);
+    }
+
+    public void loadProjection(Matrix m) {
+        mProjection = m;
+        mAlloc.subData1D(PROJECTION_OFFSET, 16, m.mMat);
+    }
+
+    public void loadTexture(Matrix m) {
+        mTexture = m;
+        mAlloc.subData1D(TEXTURE_OFFSET, 16, m.mMat);
+    }
+
+    public void setupOrthoWindow(int w, int h) {
+        mProjection.loadOrtho(0,w, h,0, -1,1);
+        mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+    }
+
+    public void setupOrthoNormalized(int w, int h) {
+        // range -1,1 in the narrow axis.
+        if(w > h) {
+            float aspect = ((float)w) / h;
+            mProjection.loadOrtho(-aspect,aspect,  -1,1,  -1,1);
+        } else {
+            float aspect = ((float)h) / w;
+            mProjection.loadOrtho(-1,1, -aspect,aspect,  -1,1);
+        }
+        mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+    }
+
+    public void setupProjectionNormalized(int w, int h) {
+        // range -1,1 in the narrow axis at z = 0.
+        Matrix m1 = new Matrix();
+        Matrix m2 = new Matrix();
+
+        if(w > h) {
+            float aspect = ((float)w) / h;
+            m1.loadFrustum(-aspect,aspect,  -1,1,  1,100);
+        } else {
+            float aspect = ((float)h) / w;
+            m1.loadFrustum(-1,1, -aspect,aspect, 1,100);
+        }
+
+        m2.loadRotate(180, 0, 1, 0);
+        m1.loadMultiply(m1, m2);
+
+        m2.loadScale(-2, 2, 1);
+        m1.loadMultiply(m1, m2);
+
+        m2.loadTranslate(0, 0, 2);
+        m1.loadMultiply(m1, m2);
+
+        mProjection = m1;
+        mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+    }
+
+}
+
+
+
+
+
+
diff --git a/libs/rs/java/RenderScript/android/renderscript/RSSurfaceView.java b/libs/rs/java/RenderScript/android/renderscript/RSSurfaceView.java
new file mode 100644
index 0000000..3835793
--- /dev/null
+++ b/libs/rs/java/RenderScript/android/renderscript/RSSurfaceView.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+public class RSSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+    private SurfaceHolder mSurfaceHolder;
+
+    /**
+     * Standard View constructor. In order to render something, you
+     * must call {@link #setRenderer} to register a renderer.
+     */
+    public RSSurfaceView(Context context) {
+        super(context);
+        init();
+        Log.v(RenderScript.LOG_TAG, "RSSurfaceView");
+    }
+
+    /**
+     * Standard View constructor. In order to render something, you
+     * must call {@link #setRenderer} to register a renderer.
+     */
+    public RSSurfaceView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+        Log.v(RenderScript.LOG_TAG, "RSSurfaceView");
+    }
+
+    private void init() {
+        // Install a SurfaceHolder.Callback so we get notified when the
+        // underlying surface is created and destroyed
+        SurfaceHolder holder = getHolder();
+        holder.addCallback(this);
+        holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+    }
+
+    /**
+     * This method is part of the SurfaceHolder.Callback interface, and is
+     * not normally called or subclassed by clients of RSSurfaceView.
+     */
+    public void surfaceCreated(SurfaceHolder holder) {
+        Log.v(RenderScript.LOG_TAG, "surfaceCreated");
+        mSurfaceHolder = holder;
+        //mGLThread.surfaceCreated();
+    }
+
+    /**
+     * This method is part of the SurfaceHolder.Callback interface, and is
+     * not normally called or subclassed by clients of RSSurfaceView.
+     */
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        // Surface will be destroyed when we return
+        Log.v(RenderScript.LOG_TAG, "surfaceDestroyed");
+        //mGLThread.surfaceDestroyed();
+    }
+
+    /**
+     * This method is part of the SurfaceHolder.Callback interface, and is
+     * not normally called or subclassed by clients of RSSurfaceView.
+     */
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        Log.v(RenderScript.LOG_TAG, "surfaceChanged");
+
+        //mGLThread.onWindowResize(w, h);
+    }
+
+    /**
+     * Inform the view that the activity is paused. The owner of this view must
+     * call this method when the activity is paused. Calling this method will
+     * pause the rendering thread.
+     * Must not be called before a renderer has been set.
+     */
+    public void onPause() {
+        Log.v(RenderScript.LOG_TAG, "onPause");
+        //mGLThread.onPause();
+    }
+
+    /**
+     * Inform the view that the activity is resumed. The owner of this view must
+     * call this method when the activity is resumed. Calling this method will
+     * recreate the OpenGL display and resume the rendering
+     * thread.
+     * Must not be called before a renderer has been set.
+     */
+    public void onResume() {
+        Log.v(RenderScript.LOG_TAG, "onResume");
+        //mGLThread.onResume();
+    }
+
+    /**
+     * Queue a runnable to be run on the GL rendering thread. This can be used
+     * to communicate with the Renderer on the rendering thread.
+     * Must not be called before a renderer has been set.
+     * @param r the runnable to be run on the GL rendering thread.
+     */
+    public void queueEvent(Runnable r) {
+        Log.v(RenderScript.LOG_TAG, "queueEvent");
+        //mGLThread.queueEvent(r);
+    }
+
+    /**
+     * This method is used as part of the View class and is not normally
+     * called or subclassed by clients of RSSurfaceView.
+     * Must not be called before a renderer has been set.
+     */
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        //mGLThread.requestExitAndWait();
+    }
+
+    // ----------------------------------------------------------------------
+
+    public RenderScript createRenderScript() {
+        Log.v(RenderScript.LOG_TAG, "createRenderScript 1");
+        Surface sur = null;
+        while ((sur == null) || (mSurfaceHolder == null)) {
+            sur = getHolder().getSurface();
+        }
+        Log.v(RenderScript.LOG_TAG, "createRenderScript 2");
+        RenderScript rs = new RenderScript(sur);
+        Log.v(RenderScript.LOG_TAG, "createRenderScript 3 rs");
+        return rs;
+    }
+
+}
+
diff --git a/libs/rs/java/RenderScript/android/renderscript/RenderScript.java b/libs/rs/java/RenderScript/android/renderscript/RenderScript.java
new file mode 100644
index 0000000..a9db15f
--- /dev/null
+++ b/libs/rs/java/RenderScript/android/renderscript/RenderScript.java
@@ -0,0 +1,956 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import android.os.Bundle;
+import android.content.res.Resources;
+import android.util.Log;
+import android.util.Config;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.Window;
+import android.view.View;
+import android.view.Surface;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+
+public class RenderScript {
+    static final String LOG_TAG = "libRS_jni";
+    private static final boolean DEBUG  = false;
+    private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+
+
+
+     /*
+     * We use a class initializer to allow the native code to cache some
+     * field offsets.
+     */
+    private static boolean sInitialized;
+    native private static void _nInit();
+
+    static {
+        sInitialized = false;
+        try {
+            System.loadLibrary("RS_jni");
+            _nInit();
+            sInitialized = true;
+        } catch (UnsatisfiedLinkError e) {
+            Log.d(LOG_TAG, "RenderScript JNI library not found!");
+        }
+    }
+
+    native private int  nDeviceCreate();
+    native private void nDeviceDestroy(int dev);
+    native private int  nContextCreate(int dev, Surface sur, int ver);
+    native private void nContextDestroy(int con);
+
+    //void rsContextBindSampler (uint32_t slot, RsSampler sampler);
+    //void rsContextBindRootScript (RsScript sampler);
+    native private void nContextBindRootScript(int script);
+    native private void nContextBindSampler(int sampler, int slot);
+    native private void nContextBindProgramFragmentStore(int pfs);
+    native private void nContextBindProgramFragment(int pf);
+    native private void nContextBindProgramVertex(int pf);
+
+    native private void nAssignName(int obj, byte[] name);
+
+    native private void nElementBegin();
+    native private void nElementAddPredefined(int predef);
+    native private void nElementAdd(int kind, int type, int norm, int bits);
+    native private int  nElementCreate();
+    native private int  nElementGetPredefined(int predef);
+    native private void nElementDestroy(int obj);
+
+    native private void nTypeBegin(int elementID);
+    native private void nTypeAdd(int dim, int val);
+    native private int  nTypeCreate();
+    native private void nTypeDestroy(int id);
+
+    native private int  nAllocationCreateTyped(int type);
+    native private int  nAllocationCreatePredefSized(int predef, int count);
+    native private int  nAllocationCreateSized(int elem, int count);
+    native private int  nAllocationCreateFromBitmap(int dstFmt, boolean genMips, Bitmap bmp);
+
+    native private void nAllocationUploadToTexture(int alloc, int baseMioLevel);
+    native private void nAllocationDestroy(int alloc);
+    native private void nAllocationData(int id, int[] d);
+    native private void nAllocationData(int id, float[] d);
+    native private void nAllocationSubData1D(int id, int off, int count, int[] d);
+    native private void nAllocationSubData1D(int id, int off, int count, float[] d);
+    native private void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, int[] d);
+    native private void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, float[] d);
+
+    native private void nTriangleMeshDestroy(int id);
+    native private void nTriangleMeshBegin(int vertex, int index);
+    native private void nTriangleMeshAddVertex_XY (float x, float y);
+    native private void nTriangleMeshAddVertex_XYZ (float x, float y, float z);
+    native private void nTriangleMeshAddVertex_XY_ST (float x, float y, float s, float t);
+    native private void nTriangleMeshAddVertex_XYZ_ST (float x, float y, float z, float s, float t);
+    native private void nTriangleMeshAddVertex_XYZ_ST_NORM (float x, float y, float z, float s, float t, float nx, float ny, float nz);
+    native private void nTriangleMeshAddTriangle(int i1, int i2, int i3);
+    native private int  nTriangleMeshCreate();
+
+    native private void nAdapter1DDestroy(int id);
+    native private void nAdapter1DBindAllocation(int ad, int alloc);
+    native private void nAdapter1DSetConstraint(int ad, int dim, int value);
+    native private void nAdapter1DData(int ad, int[] d);
+    native private void nAdapter1DSubData(int ad, int off, int count, int[] d);
+    native private void nAdapter1DData(int ad, float[] d);
+    native private void nAdapter1DSubData(int ad, int off, int count, float[] d);
+    native private int  nAdapter1DCreate();
+
+    native private void nScriptDestroy(int script);
+    native private void nScriptBindAllocation(int vtm, int alloc, int slot);
+    native private void nScriptCBegin();
+    native private void nScriptCSetClearColor(float r, float g, float b, float a);
+    native private void nScriptCSetClearDepth(float depth);
+    native private void nScriptCSetClearStencil(int stencil);
+    native private void nScriptCAddType(int type);
+    native private void nScriptCSetRoot(boolean isRoot);
+    native private void nScriptCSetScript(byte[] script, int offset, int length);
+    native private int  nScriptCCreate();
+
+    native private void nSamplerDestroy(int sampler);
+    native private void nSamplerBegin();
+    native private void nSamplerSet(int param, int value);
+    native private int  nSamplerCreate();
+
+    native private void nProgramFragmentStoreBegin(int in, int out);
+    native private void nProgramFragmentStoreDepthFunc(int func);
+    native private void nProgramFragmentStoreDepthMask(boolean enable);
+    native private void nProgramFragmentStoreColorMask(boolean r, boolean g, boolean b, boolean a);
+    native private void nProgramFragmentStoreBlendFunc(int src, int dst);
+    native private void nProgramFragmentStoreDither(boolean enable);
+    native private int  nProgramFragmentStoreCreate();
+    native private void nProgramFragmentStoreDestroy(int pgm);
+
+    native private void nProgramFragmentBegin(int in, int out);
+    native private void nProgramFragmentBindTexture(int vpf, int slot, int a);
+    native private void nProgramFragmentBindSampler(int vpf, int slot, int s);
+    native private void nProgramFragmentSetType(int slot, int vt);
+    native private void nProgramFragmentSetEnvMode(int slot, int env);
+    native private void nProgramFragmentSetTexEnable(int slot, boolean enable);
+    native private int  nProgramFragmentCreate();
+    native private void nProgramFragmentDestroy(int pgm);
+
+    native private void nProgramVertexDestroy(int pv);
+    native private void nProgramVertexBindAllocation(int pv, int slot, int mID);
+    native private void nProgramVertexBegin(int inID, int outID);
+    native private void nProgramVertexSetType(int slot, int mID);
+    native private void nProgramVertexSetTextureMatrixEnable(boolean enable);
+    native private int  nProgramVertexCreate();
+
+    native private void nLightBegin();
+    native private void nLightSetIsMono(boolean isMono);
+    native private void nLightSetIsLocal(boolean isLocal);
+    native private int  nLightCreate();
+    native private void nLightDestroy(int l);
+    native private void nLightSetColor(int l, float r, float g, float b);
+    native private void nLightSetPosition(int l, float x, float y, float z);
+
+
+    private int     mDev;
+    private int     mContext;
+    private Surface mSurface;
+
+
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+
+    RenderScript(Surface sur) {
+        mSurface = sur;
+        mDev = nDeviceCreate();
+        mContext = nContextCreate(mDev, mSurface, 0);
+    }
+
+    private class BaseObj {
+        BaseObj() {
+            mID = 0;
+        }
+
+        public int getID() {
+            return mID;
+        }
+
+        int mID;
+        String mName;
+
+        public void setName(String s) throws IllegalStateException, IllegalArgumentException
+        {
+            if(s.length() < 1) {
+                throw new IllegalArgumentException("setName does not accept a zero length string.");
+            }
+            if(mName != null) {
+                throw new IllegalArgumentException("setName object already has a name.");
+            }
+
+            try {
+                byte[] bytes = s.getBytes("UTF-8");
+                nAssignName(mID, bytes);
+                mName = s;
+            } catch (java.io.UnsupportedEncodingException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        protected void finalize() throws Throwable
+        {
+            if (mID != 0) {
+                Log.v(LOG_TAG,
+                      "Element finalized without having released the RS reference.");
+            }
+            super.finalize();
+        }
+    }
+
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // Element
+
+    public enum ElementPredefined {
+        USER_U8 (0),
+        USER_I8 (1),
+        USER_U16 (2),
+        USER_I16 (3),
+        USER_U32 (4),
+        USER_I32 (5),
+        USER_FLOAT (6),
+
+        A_8                (7),
+        RGB_565            (8),
+        RGB_888            (11),
+        RGBA_5551          (9),
+        RGBA_4444          (10),
+        RGBA_8888          (12),
+
+        INDEX_16           (13),
+        INDEX_32           (14),
+        XY_F32             (15),
+        XYZ_F32            (16),
+        ST_XY_F32          (17),
+        ST_XYZ_F32         (18),
+        NORM_XYZ_F32       (19),
+        NORM_ST_XYZ_F32    (20);
+
+        int mID;
+        ElementPredefined(int id) {
+            mID = id;
+        }
+    }
+
+    public enum DataType {
+        FLOAT (0),
+        UNSIGNED (1),
+        SIGNED (2);
+
+        int mID;
+        DataType(int id) {
+            mID = id;
+        }
+    }
+
+    public enum DataKind {
+        USER (0),
+        RED (1),
+        GREEN (2),
+        BLUE (3),
+        ALPHA (4),
+        LUMINANCE (5),
+        INTENSITY (6),
+        X (7),
+        Y (8),
+        Z (9),
+        W (10),
+        S (11),
+        T (12),
+        Q (13),
+        R (14),
+        NX (15),
+        NY (16),
+        NZ (17),
+        INDEX (18);
+
+        int mID;
+        DataKind(int id) {
+            mID = id;
+        }
+    }
+
+    public enum DepthFunc {
+        ALWAYS (0),
+        LESS (1),
+        LEQUAL (2),
+        GREATER (3),
+        GEQUAL (4),
+        EQUAL (5),
+        NOTEQUAL (6);
+
+        int mID;
+        DepthFunc(int id) {
+            mID = id;
+        }
+    }
+
+    public enum BlendSrcFunc {
+        ZERO (0),
+        ONE (1),
+        DST_COLOR (2),
+        ONE_MINUS_DST_COLOR (3),
+        SRC_ALPHA (4),
+        ONE_MINUS_SRC_ALPHA (5),
+        DST_ALPHA (6),
+        ONE_MINUS_DST_ALPA (7),
+        SRC_ALPHA_SATURATE (8);
+
+        int mID;
+        BlendSrcFunc(int id) {
+            mID = id;
+        }
+    }
+
+    public enum BlendDstFunc {
+        ZERO (0),
+        ONE (1),
+        SRC_COLOR (2),
+        ONE_MINUS_SRC_COLOR (3),
+        SRC_ALPHA (4),
+        ONE_MINUS_SRC_ALPHA (5),
+        DST_ALPHA (6),
+        ONE_MINUS_DST_ALPA (7);
+
+        int mID;
+        BlendDstFunc(int id) {
+            mID = id;
+        }
+    }
+
+    public enum EnvMode {
+        REPLACE (0),
+        MODULATE (1),
+        DECAL (2);
+
+        int mID;
+        EnvMode(int id) {
+            mID = id;
+        }
+    }
+
+    public enum SamplerParam {
+        FILTER_MIN (0),
+        FILTER_MAG (1),
+        WRAP_MODE_S (2),
+        WRAP_MODE_T (3),
+        WRAP_MODE_R (4);
+
+        int mID;
+        SamplerParam(int id) {
+            mID = id;
+        }
+    }
+
+    public enum SamplerValue {
+        NEAREST (0),
+        LINEAR (1),
+        LINEAR_MIP_LINEAR (2),
+        WRAP (3),
+        CLAMP (4);
+
+        int mID;
+        SamplerValue(int id) {
+            mID = id;
+        }
+    }
+
+
+
+    public class Element extends BaseObj {
+        Element(int id) {
+            mID = id;
+        }
+
+        public void estroy() {
+            nElementDestroy(mID);
+            mID = 0;
+        }
+    }
+
+    public void elementBegin() {
+        nElementBegin();
+    }
+
+    public void elementAddPredefined(ElementPredefined e) {
+        nElementAddPredefined(e.mID);
+    }
+
+    public void elementAdd(DataType dt, DataKind dk, boolean isNormalized, int bits) {
+        int norm = 0;
+        if (isNormalized) {
+            norm = 1;
+        }
+        nElementAdd(dt.mID, dk.mID, norm, bits);
+    }
+
+    public Element elementCreate() {
+        int id = nElementCreate();
+        return new Element(id);
+    }
+
+    public Element elementGetPredefined(ElementPredefined predef) {
+        int id = nElementGetPredefined(predef.mID);
+        return new Element(id);
+    }
+
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // Type
+
+    public enum Dimension {
+        X (0),
+        Y (1),
+        Z (2),
+        LOD (3),
+        FACE (4),
+        ARRAY_0 (100);
+
+        int mID;
+        Dimension(int id) {
+            mID = id;
+        }
+    }
+
+    public class Type extends BaseObj {
+        Type(int id) {
+            mID = id;
+        }
+
+        public void destroy() {
+            nTypeDestroy(mID);
+            mID = 0;
+        }
+    }
+
+    public void typeBegin(Element e) {
+        nTypeBegin(e.mID);
+    }
+
+    public void typeAdd(Dimension d, int value) {
+        nTypeAdd(d.mID, value);
+    }
+
+    public Type typeCreate() {
+        int id = nTypeCreate();
+        return new Type(id);
+    }
+
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // Allocation
+
+    public class Allocation extends BaseObj {
+        Allocation(int id) {
+            mID = id;
+        }
+
+        public void uploadToTexture(int baseMipLevel) {
+            nAllocationUploadToTexture(mID, baseMipLevel);
+        }
+
+        public void destroy() {
+            nAllocationDestroy(mID);
+            mID = 0;
+        }
+
+        public void data(int[] d) {
+            nAllocationData(mID, d);
+        }
+
+        public void data(float[] d) {
+            nAllocationData(mID, d);
+        }
+
+        public void subData1D(int off, int count, int[] d) {
+            nAllocationSubData1D(mID, off, count, d);
+        }
+
+        public void subData1D(int off, int count, float[] d) {
+            nAllocationSubData1D(mID, off, count, d);
+        }
+
+        public void subData2D(int xoff, int yoff, int w, int h, int[] d) {
+            nAllocationSubData2D(mID, xoff, yoff, w, h, d);
+        }
+
+        public void subData2D(int xoff, int yoff, int w, int h, float[] d) {
+            nAllocationSubData2D(mID, xoff, yoff, w, h, d);
+        }
+    }
+
+    public Allocation allocationCreateTyped(Type type) {
+        int id = nAllocationCreateTyped(type.mID);
+        return new Allocation(id);
+    }
+
+    public Allocation allocationCreatePredefSized(ElementPredefined e, int count) {
+        int id = nAllocationCreatePredefSized(e.mID, count);
+        return new Allocation(id);
+    }
+
+    public Allocation allocationCreateSized(Element e, int count) {
+        int id = nAllocationCreateSized(e.mID, count);
+        return new Allocation(id);
+    }
+
+    public Allocation allocationCreateFromBitmap(Bitmap b, ElementPredefined dstFmt, boolean genMips) {
+        int id = nAllocationCreateFromBitmap(dstFmt.mID, genMips, b); 
+        return new Allocation(id);
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // Adapter1D
+
+    public class Adapter1D extends BaseObj {
+        Adapter1D(int id) {
+            mID = id;
+        }
+
+        public void destroy() {
+            nAdapter1DDestroy(mID);
+            mID = 0;
+        }
+
+        public void bindAllocation(Allocation a) {
+            nAdapter1DBindAllocation(mID, a.mID);
+        }
+
+        public void setConstraint(Dimension dim, int value) {
+            nAdapter1DSetConstraint(mID, dim.mID, value);
+        }
+
+        public void data(int[] d) {
+            nAdapter1DData(mID, d);
+        }
+
+        public void subData(int off, int count, int[] d) {
+            nAdapter1DSubData(mID, off, count, d);
+        }
+
+        public void data(float[] d) {
+            nAdapter1DData(mID, d);
+        }
+
+        public void subData(int off, int count, float[] d) {
+            nAdapter1DSubData(mID, off, count, d);
+        }
+    }
+
+    public Adapter1D adapter1DCreate() {
+        int id = nAdapter1DCreate();
+        return new Adapter1D(id);
+    }
+
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // Triangle Mesh
+
+    public class TriangleMesh extends BaseObj {
+        TriangleMesh(int id) {
+            mID = id;
+        }
+
+        public void destroy() {
+            nTriangleMeshDestroy(mID);
+            mID = 0;
+        }
+    }
+
+    public void triangleMeshBegin(Element vertex, Element index) {
+        nTriangleMeshBegin(vertex.mID, index.mID);
+    }
+
+    public void triangleMeshAddVertex_XY(float x, float y) {
+        nTriangleMeshAddVertex_XY(x, y);
+    }
+
+    public void triangleMeshAddVertex_XYZ(float x, float y, float z) {
+        nTriangleMeshAddVertex_XYZ(x, y, z);
+    }
+
+    public void triangleMeshAddVertex_XY_ST(float x, float y, float s, float t) {
+        nTriangleMeshAddVertex_XY_ST(x, y, s, t);
+    }
+
+    public void triangleMeshAddVertex_XYZ_ST(float x, float y, float z, float s, float t) {
+        nTriangleMeshAddVertex_XYZ_ST(x, y, z, s, t);
+    }
+
+    public void triangleMeshAddVertex_XYZ_ST_NORM(float x, float y, float z, float s, float t, float nx, float ny, float nz) {
+        nTriangleMeshAddVertex_XYZ_ST_NORM(x, y, z, s, t, nx, ny, nz);
+    }
+
+    public void triangleMeshAddTriangle(int i1, int i2, int i3) {
+        nTriangleMeshAddTriangle(i1, i2, i3);
+    }
+
+    public TriangleMesh triangleMeshCreate() {
+        int id = nTriangleMeshCreate();
+        return new TriangleMesh(id);
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // Script
+
+    public class Script extends BaseObj {
+        Script(int id) {
+            mID = id;
+        }
+
+        public void destroy() {
+            nScriptDestroy(mID);
+            mID = 0;
+        }
+
+        public void bindAllocation(Allocation va, int slot) {
+            nScriptBindAllocation(mID, va.mID, slot);
+        }
+    }
+
+    public void scriptCBegin() {
+        nScriptCBegin();
+    }
+
+    public void scriptCSetClearColor(float r, float g, float b, float a) {
+        nScriptCSetClearColor(r, g, b, a);
+    }
+
+    public void scriptCSetClearDepth(float d) {
+        nScriptCSetClearDepth(d);
+    }
+
+    public void scriptCSetClearStencil(int stencil) {
+        nScriptCSetClearStencil(stencil);
+    }
+
+    public void scriptCAddType(Type t) {
+        nScriptCAddType(t.mID);
+    }
+
+    public void scriptCSetRoot(boolean r) {
+        nScriptCSetRoot(r);
+    }
+
+    public void scriptCSetScript(String s) {
+        try {
+            byte[] bytes = s.getBytes("UTF-8");
+            nScriptCSetScript(bytes, 0, bytes.length);
+        } catch (java.io.UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void scriptCSetScript(Resources resources, int id) {
+        InputStream is = resources.openRawResource(id);
+        try {
+            try {
+                scriptCSetScript(is);
+            } finally {
+                is.close();
+            }
+        } catch(IOException e) {
+            throw new Resources.NotFoundException();
+        }
+    }
+
+    public void  scriptCSetScript(InputStream is) throws IOException {
+        byte[] buf = new byte[1024];
+        int currentPos = 0;
+        while(true) {
+            int bytesLeft = buf.length - currentPos;
+            if (bytesLeft == 0) {
+                byte[] buf2 = new byte[buf.length * 2];
+                System.arraycopy(buf, 0, buf2, 0, buf.length);
+                buf = buf2;
+                bytesLeft = buf.length - currentPos;
+            }
+            int bytesRead = is.read(buf, currentPos, bytesLeft);
+            if (bytesRead <= 0) {
+                break;
+            }
+            currentPos += bytesRead;
+        }
+        nScriptCSetScript(buf, 0, currentPos);
+    }
+
+    public Script scriptCCreate() {
+        int id = nScriptCCreate();
+        return new Script(id);
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // ProgramVertex
+
+    public class ProgramVertex extends BaseObj {
+        ProgramVertex(int id) {
+            mID = id;
+        }
+
+        public void destroy() {
+            nProgramVertexDestroy(mID);
+            mID = 0;
+        }
+
+        public void bindAllocation(int slot, Allocation va) {
+            nProgramVertexBindAllocation(mID, slot, va.mID);
+        }
+
+    }
+
+    public void programVertexBegin(Element in, Element out) {
+        int inID = 0;
+        int outID = 0;
+        if (in != null) {
+            inID = in.mID;
+        }
+        if (out != null) {
+            outID = out.mID;
+        }
+        nProgramVertexBegin(inID, outID);
+    }
+
+    public void programVertexSetType(int slot, Type t) {
+        nProgramVertexSetType(slot, t.mID);
+    }
+
+    public void programVertexSetTextureMatrixEnable(boolean enable) {
+        nProgramVertexSetTextureMatrixEnable(enable);
+    }
+
+    public ProgramVertex programVertexCreate() {
+        int id = nProgramVertexCreate();
+        return new ProgramVertex(id);
+    }
+
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // ProgramFragmentStore
+
+    public class ProgramFragmentStore extends BaseObj {
+        ProgramFragmentStore(int id) {
+            mID = id;
+        }
+
+        public void destroy() {
+            nProgramFragmentStoreDestroy(mID);
+            mID = 0;
+        }
+    }
+
+    public void programFragmentStoreBegin(Element in, Element out) {
+        int inID = 0;
+        int outID = 0;
+        if (in != null) {
+            inID = in.mID;
+        }
+        if (out != null) {
+            outID = out.mID;
+        }
+        nProgramFragmentStoreBegin(inID, outID);
+    }
+
+    public void programFragmentStoreDepthFunc(DepthFunc func) {
+        nProgramFragmentStoreDepthFunc(func.mID);
+    }
+
+    public void programFragmentStoreDepthMask(boolean enable) {
+        nProgramFragmentStoreDepthMask(enable);
+    }
+
+    public void programFragmentStoreColorMask(boolean r, boolean g, boolean b, boolean a) {
+        nProgramFragmentStoreColorMask(r,g,b,a);
+    }
+
+    public void programFragmentStoreBlendFunc(BlendSrcFunc src, BlendDstFunc dst) {
+        nProgramFragmentStoreBlendFunc(src.mID, dst.mID);
+    }
+
+    public void programFragmentStoreDitherEnable(boolean enable) {
+        nProgramFragmentStoreDither(enable);
+    }
+
+    public ProgramFragmentStore programFragmentStoreCreate() {
+        int id = nProgramFragmentStoreCreate();
+        return new ProgramFragmentStore(id);
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // ProgramFragment
+
+    public class ProgramFragment extends BaseObj {
+        ProgramFragment(int id) {
+            mID = id;
+        }
+
+        public void destroy() {
+            nProgramFragmentDestroy(mID);
+            mID = 0;
+        }
+
+        public void bindTexture(Allocation va, int slot) {
+            nProgramFragmentBindTexture(mID, slot, va.mID);
+        }
+
+        public void bindSampler(Sampler vs, int slot) {
+            nProgramFragmentBindSampler(mID, slot, vs.mID);
+        }
+    }
+
+    public void programFragmentBegin(Element in, Element out) {
+        int inID = 0;
+        int outID = 0;
+        if (in != null) {
+            inID = in.mID;
+        }
+        if (out != null) {
+            outID = out.mID;
+        }
+        nProgramFragmentBegin(inID, outID);
+    }
+
+    public void programFragmentSetType(int slot, Type t) {
+        nProgramFragmentSetType(slot, t.mID);
+    }
+
+    public void programFragmentSetType(int slot, EnvMode t) {
+        nProgramFragmentSetEnvMode(slot, t.mID);
+    }
+
+    public void programFragmentSetTexEnable(int slot, boolean enable) {
+        nProgramFragmentSetTexEnable(slot, enable);
+    }
+
+    public ProgramFragment programFragmentCreate() {
+        int id = nProgramFragmentCreate();
+        return new ProgramFragment(id);
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // Sampler
+
+    public class Sampler extends BaseObj {
+        Sampler(int id) {
+            mID = id;
+        }
+
+        public void destroy() {
+            nSamplerDestroy(mID);
+            mID = 0;
+        }
+    }
+
+    public void samplerBegin() {
+        nSamplerBegin();
+    }
+
+    public void samplerSet(SamplerParam p, SamplerValue v) {
+        nSamplerSet(p.mID, v.mID);
+    }
+
+    public Sampler samplerCreate() {
+        int id = nSamplerCreate();
+        return new Sampler(id);
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////
+    // Light
+
+    public class Light extends BaseObj {
+        Light(int id) {
+            mID = id;
+        }
+
+        public void destroy() {
+            nLightDestroy(mID);
+            mID = 0;
+        }
+
+        public void setColor(float r, float g, float b) {
+            nLightSetColor(mID, r, g, b);
+        }
+
+        public void setPosition(float x, float y, float z) {
+            nLightSetPosition(mID, x, y, z);
+        }
+    }
+
+    public void lightBegin() {
+        nLightBegin();
+    }
+
+    public void lightSetIsMono(boolean isMono) {
+        nLightSetIsMono(isMono);
+    }
+
+    public void lightSetIsLocal(boolean isLocal) {
+        nLightSetIsLocal(isLocal);
+    }
+
+    public Light lightCreate() {
+        int id = nLightCreate();
+        return new Light(id);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    // Root state
+
+    public void contextBindRootScript(Script s) {
+        nContextBindRootScript(s.mID);
+    }
+
+    //public void contextBindSampler(Sampler s, int slot) {
+        //nContextBindSampler(s.mID);
+    //}
+
+    public void contextBindProgramFragmentStore(ProgramFragmentStore pfs) {
+        nContextBindProgramFragmentStore(pfs.mID);
+    }
+
+    public void contextBindProgramFragment(ProgramFragment pf) {
+        nContextBindProgramFragment(pf.mID);
+    }
+
+    public void contextBindProgramVertex(ProgramVertex pf) {
+        nContextBindProgramVertex(pf.mID);
+    }
+
+/*
+    RsAdapter2D rsAdapter2DCreate ();
+    void rsAdapter2DBindAllocation (RsAdapter2D adapt, RsAllocation alloc);
+    void rsAdapter2DDestroy (RsAdapter2D adapter);
+    void rsAdapter2DSetConstraint (RsAdapter2D adapter, RsDimension dim, uint32_t value);
+    void rsAdapter2DData (RsAdapter2D adapter, const void * data);
+    void rsAdapter2DSubData (RsAdapter2D adapter, uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void * data);
+    void rsSamplerBegin ();
+    void rsSamplerSet (RsSamplerParam p, RsSamplerValue value);
+    RsSampler rsSamplerCreate ();
+    void rsSamplerBind (RsSampler sampler, RsAllocation alloc);
+*/
+
+}
+
diff --git a/libs/rs/java/Rollo/Android.mk b/libs/rs/java/Rollo/Android.mk
new file mode 100644
index 0000000..1c6dfdf
--- /dev/null
+++ b/libs/rs/java/Rollo/Android.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+
+LOCAL_PACKAGE_NAME := Rollo
+
+include $(BUILD_PACKAGE)
diff --git a/libs/rs/java/Rollo/AndroidManifest.xml b/libs/rs/java/Rollo/AndroidManifest.xml
new file mode 100644
index 0000000..da160a3
--- /dev/null
+++ b/libs/rs/java/Rollo/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.rollo">
+    <application android:label="Rollo">
+        <activity android:name="Rollo"
+                  android:theme="@android:style/Theme.Black.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/libs/rs/java/Rollo/res/drawable/browser.png b/libs/rs/java/Rollo/res/drawable/browser.png
new file mode 100644
index 0000000..513f0be
--- /dev/null
+++ b/libs/rs/java/Rollo/res/drawable/browser.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/drawable/market.png b/libs/rs/java/Rollo/res/drawable/market.png
new file mode 100644
index 0000000..83b6910
--- /dev/null
+++ b/libs/rs/java/Rollo/res/drawable/market.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/drawable/photos.png b/libs/rs/java/Rollo/res/drawable/photos.png
new file mode 100644
index 0000000..1ed8f1e
--- /dev/null
+++ b/libs/rs/java/Rollo/res/drawable/photos.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/drawable/settings.png b/libs/rs/java/Rollo/res/drawable/settings.png
new file mode 100644
index 0000000..dd2cd95
--- /dev/null
+++ b/libs/rs/java/Rollo/res/drawable/settings.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/rollo.c b/libs/rs/java/Rollo/res/raw/rollo.c
new file mode 100644
index 0000000..64a0d0f
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/rollo.c
@@ -0,0 +1,64 @@
+#pragma version(1)
+#pragma stateVertex(PV)
+#pragma stateFragment(PF)
+#pragma stateFragmentStore(PFS)
+
+int main(void* con, int ft, int launchID)
+{
+    int rowCount;
+    int x;
+    int y;
+    int row;
+    int col;
+    int imageID;
+    int tx1;
+    int ty1;
+    int tz1;
+    int tx2;
+    int ty2;
+    int tz2;
+    int rot;
+    int rotStep;
+    int tmpSin;
+    int tmpCos;
+    int iconCount;
+    int pressure;
+
+
+    iconCount = 38;//loadI32(0, 1);
+    rotStep = 20 * 0x10000;
+    pressure = loadI32(0, 2);
+
+    rowCount = 4;
+    rot = (-20 + loadI32(0, 0)) * 0x10000;
+
+    while (iconCount) {
+        tmpSin = sinx(rot);
+        tmpCos = cosx(rot);
+
+        tx1 = tmpSin * 8 - tmpCos;
+        tx2 = tx1 + tmpCos * 2;
+
+        tz1 = tmpCos * 8 + tmpSin + pressure;
+        tz2 = tz1 - tmpSin * 2;
+
+        for (y = 0; (y < rowCount) && iconCount; y++) {
+            ty1 = (y * 0x30000) - 0x48000;
+            ty2 = ty1 + 0x20000;
+
+            pfBindTexture(NAMED_PF, 0, loadI32(1, y));
+
+            drawQuad(tx1, ty1, tz1,
+                     tx2, ty1, tz2,
+                     tx2, ty2, tz2,
+                     tx1, ty2, tz1);
+            iconCount--;
+        }
+        rot = rot + rotStep;
+    }
+
+    //renderTriangleMesh(con, NAMED_MeshCard);
+    //renderTriangleMesh(con, NAMED_MeshTab);
+    return 1;
+}
+
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/Rollo.java b/libs/rs/java/Rollo/src/com/android/rollo/Rollo.java
new file mode 100644
index 0000000..400d801
--- /dev/null
+++ b/libs/rs/java/Rollo/src/com/android/rollo/Rollo.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rollo;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class Rollo extends Activity {
+    //EventListener mListener = new EventListener();
+
+    private static final String LOG_TAG = "libRS_jni";
+    private static final boolean DEBUG  = false;
+    private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+
+    private RolloView mView;
+
+    // get the current looper (from your Activity UI thread for instance
+
+
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new RolloView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mView.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onPause();
+        mView.onPause();
+
+        Runtime.getRuntime().exit(0);
+    }
+
+
+    static void log(String message) {
+        if (LOG_ENABLED) {
+            Log.v(LOG_TAG, message);
+        }
+    }
+
+
+}
+
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloMesh.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloMesh.java
new file mode 100644
index 0000000..d7252fb
--- /dev/null
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloMesh.java
@@ -0,0 +1,90 @@
+ /*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.rollo;
+
+import java.io.Writer;
+import java.lang.Math;
+
+import android.renderscript.RenderScript;
+
+
+class RolloMesh {
+    static public final float mCardHeight = 1.2f;
+    static public final float mCardWidth = 1.8f;
+    static public final float mTabHeight = 0.2f;
+    static public final float mTabs = 3;
+    static public final float mTabGap = 0.1f;
+
+    static RenderScript.TriangleMesh createCard(RenderScript rs) {
+        RenderScript.Element vtx = rs.elementGetPredefined(
+            RenderScript.ElementPredefined.ST_XYZ_F32);
+        RenderScript.Element idx = rs.elementGetPredefined(
+            RenderScript.ElementPredefined.INDEX_16);
+
+        float w = mCardWidth / 2;
+        float h = mCardHeight;
+        float z = 0;
+
+        rs.triangleMeshBegin(vtx, idx);
+        rs.triangleMeshAddVertex_XYZ_ST(-w, 0, z,  0, 0);
+        rs.triangleMeshAddVertex_XYZ_ST(-w, h, z,  0, 1);
+        rs.triangleMeshAddVertex_XYZ_ST( w, h, z,  1, 1);
+        rs.triangleMeshAddVertex_XYZ_ST( w, 0, z,  1, 0);
+        rs.triangleMeshAddTriangle(0,1,2);
+        rs.triangleMeshAddTriangle(0,2,3);
+        return rs.triangleMeshCreate();
+    }
+
+    static RenderScript.TriangleMesh createTab(RenderScript rs) {
+        RenderScript.Element vtx = rs.elementGetPredefined(
+            RenderScript.ElementPredefined.ST_XYZ_F32);
+        RenderScript.Element idx = rs.elementGetPredefined(
+            RenderScript.ElementPredefined.INDEX_16);
+
+
+        float tabSlope = 0.1f;
+        float num = 0;
+
+        float w = (mCardWidth - ((mTabs - 1) * mTabGap)) / mTabs;
+        float w1 = -(mCardWidth / 2) + ((w + mTabGap) * num);
+        float w2 = w1 + (w * tabSlope);
+        float w3 = w1 + w - (w * tabSlope);
+        float w4 = w1 + w;
+        float h1 = mCardHeight;
+        float h2 = h1 + mTabHeight;
+        float z = 0;
+
+        float stScale = w / mTabHeight / 2;
+        float stScale2 = stScale * (tabSlope / w);
+
+
+        rs.triangleMeshBegin(vtx, idx);
+        rs.triangleMeshAddVertex_XYZ_ST(w1, h1, z,  -stScale, 0);
+        rs.triangleMeshAddVertex_XYZ_ST(w2, h2, z,  -stScale2, 1);
+        rs.triangleMeshAddVertex_XYZ_ST(w3, h2, z,   stScale2, 1);
+        rs.triangleMeshAddVertex_XYZ_ST(w4, h1, z,   stScale, 0);
+        rs.triangleMeshAddTriangle(0,1,2);
+        rs.triangleMeshAddTriangle(0,2,3);
+        return rs.triangleMeshCreate();
+    }
+
+
+
+}
+
+
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
new file mode 100644
index 0000000..003a2a0
--- /dev/null
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rollo;
+
+import java.io.Writer;
+
+import android.renderscript.RenderScript;
+import android.renderscript.ProgramVertexAlloc;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class RolloRS {
+
+    public RolloRS() {
+    }
+
+    public void init(RenderScript rs, Resources res, int width, int height) {
+        mRS = rs;
+        mRes = res;
+        initNamed();
+        initRS();
+    }
+
+    public void setPosition(float dx, float pressure) {
+        mAllocStateBuf[0] += (int)(dx);
+        mAllocStateBuf[2] = (int)(pressure * 0x40000);
+        mAllocState.data(mAllocStateBuf);
+    }
+
+
+    private Resources mRes;
+    private RenderScript mRS;
+
+
+    private RenderScript.Script mScript;
+
+    private RenderScript.Sampler mSampler;
+    private RenderScript.ProgramFragmentStore mPFSBackground;
+    private RenderScript.ProgramFragmentStore mPFSImages;
+    private RenderScript.ProgramFragment mPFBackground;
+    private RenderScript.ProgramFragment mPFImages;
+    private RenderScript.ProgramVertex mPV;
+    private ProgramVertexAlloc mPVAlloc;
+    private RenderScript.Allocation[] mIcons;
+
+    private int[] mAllocStateBuf;
+    private RenderScript.Allocation mAllocState;
+
+    private int[] mAllocIconIDBuf;
+    private RenderScript.Allocation mAllocIconID;
+
+    private void initNamed() {
+        mRS.samplerBegin();
+        mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN,
+                       RenderScript.SamplerValue.LINEAR_MIP_LINEAR);
+        mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_S,
+                       RenderScript.SamplerValue.CLAMP);
+        mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_T,
+                       RenderScript.SamplerValue.CLAMP);
+        mSampler = mRS.samplerCreate();
+
+
+        mRS.programFragmentBegin(null, null);
+        mRS.programFragmentSetTexEnable(0, true);
+        //mRS.programFragmentSetEnvMode(0, RS_TEX_ENV_MODE_REPLACE);
+        mPFImages = mRS.programFragmentCreate();
+        mPFImages.setName("PF");
+        mPFImages.bindSampler(mSampler, 0);
+
+        mRS.programFragmentStoreBegin(null, null);
+        mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS);
+        mRS.programFragmentStoreDitherEnable(false);
+        mRS.programFragmentStoreDepthMask(false);
+        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE, 
+                                          RenderScript.BlendDstFunc.ONE);
+        mPFSBackground = mRS.programFragmentStoreCreate();
+        mPFSBackground.setName("PFS");
+
+
+        mPVAlloc = new ProgramVertexAlloc(mRS);
+        mRS.programVertexBegin(null, null);
+        mRS.programVertexSetTextureMatrixEnable(true);
+        mPV = mRS.programVertexCreate();
+        mPV.setName("PV");
+        mPV.bindAllocation(0, mPVAlloc.mAlloc);
+
+
+
+        mPVAlloc.setupProjectionNormalized(320, 480);
+        //mPVAlloc.setupOrthoNormalized(320, 480);
+        mRS.contextBindProgramVertex(mPV);
+
+
+        Log.e("rs", "Done loading named");
+
+
+        {
+            mIcons = new RenderScript.Allocation[4];
+            mAllocIconIDBuf = new int[mIcons.length];
+            mAllocIconID = mRS.allocationCreatePredefSized(
+                RenderScript.ElementPredefined.USER_I32, mAllocIconIDBuf.length);
+
+            
+            BitmapDrawable bd;
+            Bitmap b;
+            
+            bd = (BitmapDrawable)mRes.getDrawable(R.drawable.browser);
+            mIcons[0] = mRS.allocationCreateFromBitmap(bd.getBitmap(), RenderScript.ElementPredefined.RGB_565, true);
+
+            bd = (BitmapDrawable)mRes.getDrawable(R.drawable.market);
+            mIcons[1] = mRS.allocationCreateFromBitmap(bd.getBitmap(), RenderScript.ElementPredefined.RGB_565, true);
+
+            bd = (BitmapDrawable)mRes.getDrawable(R.drawable.photos);
+            mIcons[2] = mRS.allocationCreateFromBitmap(bd.getBitmap(), RenderScript.ElementPredefined.RGB_565, true);
+
+            bd = (BitmapDrawable)mRes.getDrawable(R.drawable.settings);
+            mIcons[3] = mRS.allocationCreateFromBitmap(bd.getBitmap(), RenderScript.ElementPredefined.RGB_565, true);
+
+            for(int ct=0; ct < mIcons.length; ct++) {
+                mIcons[ct].uploadToTexture(0);
+                mAllocIconIDBuf[ct] = mIcons[ct].getID();
+            }
+            mAllocIconID.data(mAllocIconIDBuf);
+        }
+
+    }
+
+
+
+    private void initRS() {
+        mRS.scriptCBegin();
+        mRS.scriptCSetClearColor(0.0f, 0.0f, 0.1f, 1.0f);
+        mRS.scriptCSetScript(mRes, R.raw.rollo);
+        mRS.scriptCSetRoot(true);
+        mScript = mRS.scriptCCreate();
+
+        mAllocStateBuf = new int[] {0, 38, 0};
+        mAllocState = mRS.allocationCreatePredefSized(
+            RenderScript.ElementPredefined.USER_I32, mAllocStateBuf.length);
+        mScript.bindAllocation(mAllocState, 0);
+        mScript.bindAllocation(mAllocIconID, 1);
+        setPosition(0, 0);
+
+        mRS.contextBindRootScript(mScript);
+    }
+}
+
+
+
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
new file mode 100644
index 0000000..27d2dd6
--- /dev/null
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rollo;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class RolloView extends RSSurfaceView {
+
+    public RolloView(Context context) {
+        super(context);
+
+        //setFocusable(true);
+    }
+
+    private RenderScript mRS;
+    private RolloRS mRender;
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+
+        mRS = createRenderScript();
+        mRender = new RolloRS();
+        mRender.init(mRS, getResources(), w, h);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event)
+    {
+        // break point at here
+        // this method doesn't work when 'extends View' include 'extends ScrollView'.
+        return super.onKeyDown(keyCode, event);
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        boolean ret = true;
+        int act = ev.getAction();
+        if (act == ev.ACTION_UP) {
+            ret = false;
+        }
+        float x = ev.getX();
+        x = (x - 180) / 40;
+        //Log.e("rs", Float(x).toString());
+
+        mRender.setPosition(x, ev.getPressure());
+        //mRender.newTouchPosition((int)ev.getX(), (int)ev.getY());
+        return ret;
+    }
+}
+
+
diff --git a/libs/rs/jni/Android.mk b/libs/rs/jni/Android.mk
new file mode 100644
index 0000000..b3142ae
--- /dev/null
+++ b/libs/rs/jni/Android.mk
@@ -0,0 +1,36 @@
+LOCAL_PATH:=$(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	RenderScript_jni.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+        libandroid_runtime \
+        libacc \
+        libnativehelper \
+        libRS \
+        libcutils \
+        libsgl \
+        libutils \
+        libui
+
+LOCAL_STATIC_LIBRARIES :=
+
+rs_generated_include_dir := $(call intermediates-dir-for,SHARED_LIBRARIES,libRS,,)
+
+LOCAL_C_INCLUDES += \
+	$(JNI_H_INCLUDE) \
+	$(rs_generated_include_dir) \
+	$(call include-path-for, corecg graphics)
+
+LOCAL_CFLAGS +=
+
+LOCAL_LDLIBS := -lpthread
+
+LOCAL_MODULE:= libRS_jni
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_ADDITIONAL_DEPENDENCIES += $(rs_generated_source)
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/libs/rs/jni/RenderScript_jni.cpp b/libs/rs/jni/RenderScript_jni.cpp
new file mode 100644
index 0000000..5b33dd3
--- /dev/null
+++ b/libs/rs/jni/RenderScript_jni.cpp
@@ -0,0 +1,1088 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libRS_jni"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <math.h>
+#include <utils/misc.h>
+
+#include <ui/EGLNativeWindowSurface.h>
+#include <ui/Surface.h>
+
+#include <core/SkBitmap.h>
+
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include "../RenderScript.h"
+#include "../RenderScriptEnv.h"
+
+//#define LOG_API LOGE
+#define LOG_API(...)
+
+using namespace android;
+
+// ---------------------------------------------------------------------------
+
+static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
+{
+    jclass npeClazz = env->FindClass(exc);
+    env->ThrowNew(npeClazz, msg);
+}
+
+static jfieldID gContextId = 0;
+static jfieldID gNativeBitmapID = 0;
+
+static void _nInit(JNIEnv *_env, jclass _this)
+{
+    gContextId             = _env->GetFieldID(_this, "mContext", "I");
+
+    jclass bitmapClass = _env->FindClass("android/graphics/Bitmap");
+    gNativeBitmapID = _env->GetFieldID(bitmapClass, "mNativeBitmap", "I");
+}
+
+
+// ---------------------------------------------------------------------------
+
+static void
+nAssignName(JNIEnv *_env, jobject _this, jint obj, jbyteArray str)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nAssignName, con(%p), obj(%p)", con, obj);
+
+    jint len = _env->GetArrayLength(str);
+    jbyte * cptr = (jbyte *) _env->GetPrimitiveArrayCritical(str, 0);
+    rsAssignName((void *)obj, (const char *)cptr, len);
+    _env->ReleasePrimitiveArrayCritical(str, cptr, JNI_ABORT);
+}
+
+
+// ---------------------------------------------------------------------------
+
+static jint
+nDeviceCreate(JNIEnv *_env, jobject _this)
+{
+    LOG_API("nDeviceCreate");
+    return (jint)rsDeviceCreate();
+}
+
+static void
+nDeviceDestroy(JNIEnv *_env, jobject _this, jint dev)
+{
+    LOG_API("nDeviceDestroy");
+    return rsDeviceDestroy((RsDevice)dev);
+}
+
+static jint
+nContextCreate(JNIEnv *_env, jobject _this, jint dev, jobject wnd, jint ver)
+{
+    LOG_API("nContextCreate");
+
+    if (wnd == NULL) {
+        not_valid_surface:
+        doThrow(_env, "java/lang/IllegalArgumentException",
+                "Make sure the SurfaceView or associated SurfaceHolder has a valid Surface");
+        return 0;
+    }
+    jclass surface_class = _env->FindClass("android/view/Surface");
+    jfieldID surfaceFieldID = _env->GetFieldID(surface_class, "mSurface", "I");
+    Surface * window = (Surface*)_env->GetIntField(wnd, surfaceFieldID);
+    if (window == NULL)
+        goto not_valid_surface;
+
+    return (jint)rsContextCreate((RsDevice)dev, window, ver);
+}
+
+static void
+nContextDestroy(JNIEnv *_env, jobject _this, jint con)
+{
+    LOG_API("nContextDestroy, con(%p)", (RsContext)con);
+    return rsContextDestroy((RsContext)con);
+}
+
+
+static void
+nElementBegin(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nElementBegin, con(%p)", con);
+    rsElementBegin();
+}
+
+static void
+nElementAddPredefined(JNIEnv *_env, jobject _this, jint predef)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nElementAddPredefined, con(%p), predef(%i)", con, predef);
+    rsElementAddPredefined((RsElementPredefined)predef);
+}
+
+static void
+nElementAdd(JNIEnv *_env, jobject _this, jint kind, jint type, jint norm, jint bits)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nElementAdd, con(%p), kind(%i), type(%i), norm(%i), bits(%i)", con, kind, type, norm, bits);
+    rsElementAdd((RsDataKind)kind, (RsDataType)type, norm != 0, (size_t)bits);
+}
+
+static jint
+nElementCreate(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nElementCreate, con(%p)", con);
+    return (jint)rsElementCreate();
+}
+
+static jint
+nElementGetPredefined(JNIEnv *_env, jobject _this, jint predef)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nElementGetPredefined, con(%p) predef(%i)", con, predef);
+    return (jint)rsElementGetPredefined((RsElementPredefined)predef);
+}
+
+static void
+nElementDestroy(JNIEnv *_env, jobject _this, jint e)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nElementDestroy, con(%p) e(%p)", con, (RsElement)e);
+    rsElementDestroy((RsElement)e);
+}
+
+// -----------------------------------
+
+static void
+nTypeBegin(JNIEnv *_env, jobject _this, jint eID)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTypeBegin, con(%p) e(%p)", con, (RsElement)eID);
+    rsTypeBegin((RsElement)eID);
+}
+
+static void
+nTypeAdd(JNIEnv *_env, jobject _this, jint dim, jint val)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTypeAdd, con(%p) dim(%i), val(%i)", con, dim, val);
+    rsTypeAdd((RsDimension)dim, val);
+}
+
+static jint
+nTypeCreate(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTypeCreate, con(%p)", con);
+    return (jint)rsTypeCreate();
+}
+
+static void
+nTypeDestroy(JNIEnv *_env, jobject _this, jint eID)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTypeDestroy, con(%p), t(%p)", con, (RsType)eID);
+    rsTypeDestroy((RsType)eID);
+}
+
+// -----------------------------------
+
+static jint
+nAllocationCreateTyped(JNIEnv *_env, jobject _this, jint e)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nAllocationCreateTyped, con(%p), e(%p)", con, (RsElement)e);
+    return (jint) rsAllocationCreateTyped((RsElement)e);
+}
+
+static jint
+nAllocationCreatePredefSized(JNIEnv *_env, jobject _this, jint predef, jint count)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nAllocationCreatePredefSized, con(%p), predef(%i), count(%i)", con, predef, count);
+    return (jint) rsAllocationCreatePredefSized((RsElementPredefined)predef, count);
+}
+
+static jint
+nAllocationCreateSized(JNIEnv *_env, jobject _this, jint e, jint count)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nAllocationCreateSized, con(%p), e(%p), count(%i)", con, (RsElement)e, count);
+    return (jint) rsAllocationCreateSized((RsElement)e, count);
+}
+
+static void
+nAllocationUploadToTexture(JNIEnv *_env, jobject _this, jint a, jint mip)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nAllocationUploadToTexture, con(%p), a(%p), mip(%i)", con, (RsAllocation)a, mip);
+    rsAllocationUploadToTexture((RsAllocation)a, mip);
+}
+
+static RsElementPredefined SkBitmapToPredefined(SkBitmap::Config cfg)
+{
+    switch (cfg) {
+    case SkBitmap::kA8_Config:
+        return RS_ELEMENT_A_8;
+    case SkBitmap::kARGB_4444_Config:
+        return RS_ELEMENT_RGBA_4444;
+    case SkBitmap::kARGB_8888_Config:
+        return RS_ELEMENT_RGBA_8888;
+    case SkBitmap::kRGB_565_Config:
+        return RS_ELEMENT_RGB_565;
+
+    default:
+        break;
+    }
+    // If we don't have a conversion mark it as a user type.
+    LOGE("Unsupported bitmap type");
+    return RS_ELEMENT_USER_U8;
+}
+
+static int
+nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jobject jbitmap)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    SkBitmap const * nativeBitmap =
+            (SkBitmap const *)_env->GetIntField(jbitmap, gNativeBitmapID);
+    const SkBitmap& bitmap(*nativeBitmap);
+    SkBitmap::Config config = bitmap.getConfig();
+
+    RsElementPredefined e = SkBitmapToPredefined(config);
+
+    if (e != RS_ELEMENT_USER_U8) {
+        bitmap.lockPixels();
+        const int w = bitmap.width();
+        const int h = bitmap.height();
+        const void* ptr = bitmap.getPixels();
+        jint id = (jint)rsAllocationCreateFromBitmap(w, h, (RsElementPredefined)dstFmt, e, genMips, ptr);
+        bitmap.unlockPixels();
+        return id;
+    }
+    return 0;
+}
+
+
+static void
+nAllocationDestroy(JNIEnv *_env, jobject _this, jint a)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nAllocationDestroy, con(%p), a(%p)", con, (RsAllocation)a);
+    rsAllocationDestroy((RsAllocation)a);
+}
+
+static void
+nAllocationData_i(JNIEnv *_env, jobject _this, jint alloc, jintArray data)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    jint len = _env->GetArrayLength(data);
+    LOG_API("nAllocationData_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
+    jint *ptr = _env->GetIntArrayElements(data, NULL);
+    rsAllocationData((RsAllocation)alloc, ptr);
+    _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData_f(JNIEnv *_env, jobject _this, jint alloc, jfloatArray data)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    jint len = _env->GetArrayLength(data);
+    LOG_API("nAllocationData_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
+    jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+    rsAllocationData((RsAllocation)alloc, ptr);
+    _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationSubData1D_i(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jintArray data)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    jint len = _env->GetArrayLength(data);
+    LOG_API("nAllocation1DSubData_i, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAllocation)alloc, offset, count, len);
+    jint *ptr = _env->GetIntArrayElements(data, NULL);
+    rsAllocation1DSubData((RsAllocation)alloc, offset, count, ptr);
+    _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationSubData1D_f(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jfloatArray data)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    jint len = _env->GetArrayLength(data);
+    LOG_API("nAllocation1DSubData_f, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAllocation)alloc, offset, count, len);
+    jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+    rsAllocation1DSubData((RsAllocation)alloc, offset, count, ptr);
+    _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationSubData2D_i(JNIEnv *_env, jobject _this, jint alloc, jint xoff, jint yoff, jint w, jint h, jintArray data)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    jint len = _env->GetArrayLength(data);
+    LOG_API("nAllocation2DSubData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, w, h, len);
+    jint *ptr = _env->GetIntArrayElements(data, NULL);
+    rsAllocation2DSubData((RsAllocation)alloc, xoff, yoff, w, h, ptr);
+    _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationSubData2D_f(JNIEnv *_env, jobject _this, jint alloc, jint xoff, jint yoff, jint w, jint h, jfloatArray data)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    jint len = _env->GetArrayLength(data);
+    LOG_API("nAllocation2DSubData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, w, h, len);
+    jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+    rsAllocation2DSubData((RsAllocation)alloc, xoff, yoff, w, h, ptr);
+    _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT);
+}
+
+
+
+// -----------------------------------
+
+static void
+nTriangleMeshDestroy(JNIEnv *_env, jobject _this, jint tm)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTriangleMeshDestroy, con(%p), tm(%p)", con, (RsAllocation)tm);
+    rsTriangleMeshDestroy((RsTriangleMesh)tm);
+}
+
+static void
+nTriangleMeshBegin(JNIEnv *_env, jobject _this, jint v, jint i)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTriangleMeshBegin, con(%p), vertex(%p), index(%p)", con, (RsElement)v, (RsElement)i);
+    rsTriangleMeshBegin((RsElement)v, (RsElement)i);
+}
+
+static void
+nTriangleMeshAddVertex_XY(JNIEnv *_env, jobject _this, jfloat x, jfloat y)
+{
+    float v[] = {x, y};
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTriangleMeshAddVertex_XY, con(%p), x(%f), y(%f)", con, x, y);
+    rsTriangleMeshAddVertex(v);
+}
+
+static void
+nTriangleMeshAddVertex_XYZ(JNIEnv *_env, jobject _this, jfloat x, jfloat y, jfloat z)
+{
+    float v[] = {x, y, z};
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTriangleMeshAddVertex_XYZ, con(%p), x(%f), y(%f), z(%f)", con, x, y, z);
+    rsTriangleMeshAddVertex(v);
+}
+
+static void
+nTriangleMeshAddVertex_XY_ST(JNIEnv *_env, jobject _this, jfloat x, jfloat y, jfloat s, jfloat t)
+{
+    float v[] = {s, t, x, y};
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTriangleMeshAddVertex_XY_ST, con(%p), x(%f), y(%f), s(%f), t(%f)", con, x, y, s, t);
+    rsTriangleMeshAddVertex(v);
+}
+
+static void
+nTriangleMeshAddVertex_XYZ_ST(JNIEnv *_env, jobject _this, jfloat x, jfloat y, jfloat z, jfloat s, jfloat t)
+{
+    float v[] = {s, t, x, y, z};
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTriangleMeshAddVertex_XYZ_ST, con(%p), x(%f), y(%f), z(%f), s(%f), t(%f)", con, x, y, z, s, t);
+    rsTriangleMeshAddVertex(v);
+}
+
+static void
+nTriangleMeshAddVertex_XYZ_ST_NORM(JNIEnv *_env, jobject _this, jfloat x, jfloat y, jfloat z, jfloat s, jfloat t, jfloat nx, jfloat ny, jfloat nz)
+{
+    float v[] = {nx, ny, nz, s, t, x, y, z};
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTriangleMeshAddVertex_XYZ_ST, con(%p), x(%f), y(%f), z(%f), s(%f), t(%f)", con, x, y, z, s, t);
+    rsTriangleMeshAddVertex(v);
+}
+
+static void
+nTriangleMeshAddTriangle(JNIEnv *_env, jobject _this, jint i1, jint i2, jint i3)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTriangleMeshAddTriangle, con(%p), i1(%i), i2(%i), i3(%i)", con, i1, i2, i3);
+    rsTriangleMeshAddTriangle(i1, i2, i3);
+}
+
+static jint
+nTriangleMeshCreate(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nTriangleMeshCreate, con(%p)", con);
+    return (jint) rsTriangleMeshCreate();
+}
+
+// -----------------------------------
+
+static void
+nAdapter1DDestroy(JNIEnv *_env, jobject _this, jint adapter)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nAdapter1DDestroy, con(%p), adapter(%p)", con, (RsAdapter1D)adapter);
+    rsAdapter1DDestroy((RsAdapter1D)adapter);
+}
+
+static void
+nAdapter1DBindAllocation(JNIEnv *_env, jobject _this, jint adapter, jint alloc)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nAdapter1DBindAllocation, con(%p), adapter(%p), alloc(%p)", con, (RsAdapter1D)adapter, (RsAllocation)alloc);
+    rsAdapter1DBindAllocation((RsAdapter1D)adapter, (RsAllocation)alloc);
+}
+
+static void
+nAdapter1DSetConstraint(JNIEnv *_env, jobject _this, jint adapter, jint dim, jint value)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nAdapter1DSetConstraint, con(%p), adapter(%p), dim(%i), value(%i)", con, (RsAdapter1D)adapter, dim, value);
+    rsAdapter1DSetConstraint((RsAdapter1D)adapter, (RsDimension)dim, value);
+}
+
+static void
+nAdapter1DData_i(JNIEnv *_env, jobject _this, jint adapter, jintArray data)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    jint len = _env->GetArrayLength(data);
+    LOG_API("nAdapter1DData_i, con(%p), adapter(%p), len(%i)", con, (RsAdapter1D)adapter, len);
+    jint *ptr = _env->GetIntArrayElements(data, NULL);
+    rsAdapter1DData((RsAdapter1D)adapter, ptr);
+    _env->ReleaseIntArrayElements(data, ptr, 0/*JNI_ABORT*/);
+}
+
+static void
+nAdapter1DSubData_i(JNIEnv *_env, jobject _this, jint adapter, jint offset, jint count, jintArray data)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    jint len = _env->GetArrayLength(data);
+    LOG_API("nAdapter1DSubData_i, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAdapter1D)adapter, offset, count, len);
+    jint *ptr = _env->GetIntArrayElements(data, NULL);
+    rsAdapter1DSubData((RsAdapter1D)adapter, offset, count, ptr);
+    _env->ReleaseIntArrayElements(data, ptr, 0/*JNI_ABORT*/);
+}
+
+static void
+nAdapter1DData_f(JNIEnv *_env, jobject _this, jint adapter, jfloatArray data)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    jint len = _env->GetArrayLength(data);
+    LOG_API("nAdapter1DData_f, con(%p), adapter(%p), len(%i)", con, (RsAdapter1D)adapter, len);
+    jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+    rsAdapter1DData((RsAdapter1D)adapter, ptr);
+    _env->ReleaseFloatArrayElements(data, ptr, 0/*JNI_ABORT*/);
+}
+
+static void
+nAdapter1DSubData_f(JNIEnv *_env, jobject _this, jint adapter, jint offset, jint count, jfloatArray data)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    jint len = _env->GetArrayLength(data);
+    LOG_API("nAdapter1DSubData_f, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAdapter1D)adapter, offset, count, len);
+    jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+    rsAdapter1DSubData((RsAdapter1D)adapter, offset, count, ptr);
+    _env->ReleaseFloatArrayElements(data, ptr, 0/*JNI_ABORT*/);
+}
+
+static jint
+nAdapter1DCreate(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nAdapter1DCreate, con(%p)", con);
+    return (jint)rsAdapter1DCreate();
+}
+
+// -----------------------------------
+
+static void
+nScriptDestroy(JNIEnv *_env, jobject _this, jint script)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nScriptDestroy, con(%p), script(%p)", con, (RsScript)script);
+    rsScriptDestroy((RsScript)script);
+}
+
+static void
+nScriptBindAllocation(JNIEnv *_env, jobject _this, jint script, jint alloc, jint slot)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nScriptBindAllocation, con(%p), script(%p), alloc(%p), slot(%i)", con, (RsScript)script, (RsAllocation)alloc, slot);
+    rsScriptBindAllocation((RsScript)script, (RsAllocation)alloc, slot);
+}
+
+static void
+nScriptCBegin(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nScriptCBegin, con(%p)", con);
+    rsScriptCBegin();
+}
+
+static void
+nScriptCSetClearColor(JNIEnv *_env, jobject _this, jfloat r, jfloat g, jfloat b, jfloat a)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nScriptCSetClearColor, con(%p), r(%f), g(%f), b(%f), a(%f)", con, r, g, b, a);
+    rsScriptCSetClearColor(r, g, b, a);
+}
+
+static void
+nScriptCSetClearDepth(JNIEnv *_env, jobject _this, jfloat d)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nScriptCSetClearColor, con(%p), depth(%f)", con, d);
+    rsScriptCSetClearDepth(d);
+}
+
+static void
+nScriptCSetClearStencil(JNIEnv *_env, jobject _this, jint stencil)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nScriptCSetClearStencil, con(%p), stencil(%i)", con, stencil);
+    rsScriptCSetClearStencil(stencil);
+}
+
+static void
+nScriptCAddType(JNIEnv *_env, jobject _this, jint type)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nScriptCAddType, con(%p), type(%p)", con, (RsType)type);
+    rsScriptCAddType((RsType)type);
+}
+
+static void
+nScriptCSetRoot(JNIEnv *_env, jobject _this, jboolean isRoot)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nScriptCSetRoot, con(%p), isRoot(%i)", con, isRoot);
+    rsScriptCSetRoot(isRoot);
+}
+
+static void
+nScriptCSetScript(JNIEnv *_env, jobject _this, jbyteArray scriptRef,
+                  jint offset, jint length)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("!!! nScriptCSetScript, con(%p)", con);
+    jint _exception = 0;
+    jint remaining;
+    jbyte* script_base = 0;
+    jbyte* script_ptr;
+    if (!scriptRef) {
+        _exception = 1;
+        //_env->ThrowNew(IAEClass, "script == null");
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        //_env->ThrowNew(IAEClass, "offset < 0");
+        goto exit;
+    }
+    if (length < 0) {
+        _exception = 1;
+        //_env->ThrowNew(IAEClass, "length < 0");
+        goto exit;
+    }
+    remaining = _env->GetArrayLength(scriptRef) - offset;
+    if (remaining < length) {
+        _exception = 1;
+        //_env->ThrowNew(IAEClass, "length > script.length - offset");
+        goto exit;
+    }
+    script_base = (jbyte *)
+        _env->GetPrimitiveArrayCritical(scriptRef, (jboolean *)0);
+    script_ptr = script_base + offset;
+
+    rsScriptCSetText((const char *)script_ptr, length);
+
+exit:
+    if (script_base) {
+        _env->ReleasePrimitiveArrayCritical(scriptRef, script_base,
+                _exception ? JNI_ABORT: 0);
+    }
+}
+
+static jint
+nScriptCCreate(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nScriptCCreate, con(%p)", con);
+    return (jint)rsScriptCCreate();
+}
+
+// ---------------------------------------------------------------------------
+
+static void
+nProgramFragmentStoreBegin(JNIEnv *_env, jobject _this, jint in, jint out)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentStoreBegin, con(%p), in(%p), out(%p)", con, (RsElement)in, (RsElement)out);
+    rsProgramFragmentStoreBegin((RsElement)in, (RsElement)out);
+}
+
+static void
+nProgramFragmentStoreDepthFunc(JNIEnv *_env, jobject _this, jint func)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentStoreDepthFunc, con(%p), func(%i)", con, func);
+    rsProgramFragmentStoreDepthFunc((RsDepthFunc)func);
+}
+
+static void
+nProgramFragmentStoreDepthMask(JNIEnv *_env, jobject _this, jboolean enable)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentStoreDepthMask, con(%p), enable(%i)", con, enable);
+    rsProgramFragmentStoreDepthMask(enable);
+}
+
+static void
+nProgramFragmentStoreColorMask(JNIEnv *_env, jobject _this, jboolean r, jboolean g, jboolean b, jboolean a)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentStoreColorMask, con(%p), r(%i), g(%i), b(%i), a(%i)", con, r, g, b, a);
+    rsProgramFragmentStoreColorMask(r, g, b, a);
+}
+
+static void
+nProgramFragmentStoreBlendFunc(JNIEnv *_env, jobject _this, int src, int dst)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentStoreBlendFunc, con(%p), src(%i), dst(%i)", con, src, dst);
+    rsProgramFragmentStoreBlendFunc((RsBlendSrcFunc)src, (RsBlendDstFunc)dst);
+}
+
+static void
+nProgramFragmentStoreDither(JNIEnv *_env, jobject _this, jboolean enable)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentStoreDither, con(%p), enable(%i)", con, enable);
+    rsProgramFragmentStoreDither(enable);
+}
+
+static jint
+nProgramFragmentStoreCreate(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentStoreCreate, con(%p)", con);
+
+    return (jint)rsProgramFragmentStoreCreate();
+}
+
+static void
+nProgramFragmentStoreDestroy(JNIEnv *_env, jobject _this, jint pgm)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentStoreDestroy, con(%p), pgm(%i)", con, pgm);
+    rsProgramFragmentStoreDestroy((RsProgramFragmentStore)pgm);
+}
+
+// ---------------------------------------------------------------------------
+
+static void
+nProgramFragmentBegin(JNIEnv *_env, jobject _this, jint in, jint out)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentBegin, con(%p), in(%p), out(%p)", con, (RsElement)in, (RsElement)out);
+    rsProgramFragmentBegin((RsElement)in, (RsElement)out);
+}
+
+static void
+nProgramFragmentBindTexture(JNIEnv *_env, jobject _this, jint vpf, jint slot, jint a)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentBindTexture, con(%p), vpf(%p), slot(%i), a(%p)", con, (RsProgramFragment)vpf, slot, (RsAllocation)a);
+    rsProgramFragmentBindTexture((RsProgramFragment)vpf, slot, (RsAllocation)a);
+}
+
+static void
+nProgramFragmentBindSampler(JNIEnv *_env, jobject _this, jint vpf, jint slot, jint a)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentBindSampler, con(%p), vpf(%p), slot(%i), a(%p)", con, (RsProgramFragment)vpf, slot, (RsSampler)a);
+    rsProgramFragmentBindSampler((RsProgramFragment)vpf, slot, (RsSampler)a);
+}
+
+static void
+nProgramFragmentSetType(JNIEnv *_env, jobject _this, jint slot, jint vt)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentSetType, con(%p), slot(%i), vt(%p)", con, slot, (RsType)vt);
+    rsProgramFragmentSetType(slot, (RsType)vt);
+}
+
+static void
+nProgramFragmentSetEnvMode(JNIEnv *_env, jobject _this, jint slot, jint env)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentSetEnvMode, con(%p), slot(%i), vt(%i)", con, slot, env);
+    rsProgramFragmentSetEnvMode(slot, (RsTexEnvMode)env);
+}
+
+static void
+nProgramFragmentSetTexEnable(JNIEnv *_env, jobject _this, jint slot, jboolean enable)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentSetTexEnable, con(%p), slot(%i), enable(%i)", con, slot, enable);
+    rsProgramFragmentSetTexEnable(slot, enable);
+}
+
+static jint
+nProgramFragmentCreate(JNIEnv *_env, jobject _this, jint slot, jboolean enable)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentCreate, con(%p)", con);
+    return (jint)rsProgramFragmentCreate();
+}
+
+static void
+nProgramFragmentDestroy(JNIEnv *_env, jobject _this, jint pgm)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentDestroy, con(%p), pgm(%i)", con, pgm);
+    rsProgramFragmentDestroy((RsProgramFragment)pgm);
+}
+
+// ---------------------------------------------------------------------------
+
+static void
+nProgramVertexBegin(JNIEnv *_env, jobject _this, jint in, jint out)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramVertexBegin, con(%p), in(%p), out(%p)", con, (RsElement)in, (RsElement)out);
+    rsProgramVertexBegin((RsElement)in, (RsElement)out);
+}
+
+static void
+nProgramVertexBindAllocation(JNIEnv *_env, jobject _this, jint vpv, jint slot, jint a)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramVertexBindAllocation, con(%p), vpf(%p), slot(%i), a(%p)", con, (RsProgramVertex)vpv, slot, (RsAllocation)a);
+    rsProgramVertexBindAllocation((RsProgramFragment)vpv, slot, (RsAllocation)a);
+}
+
+static void
+nProgramVertexSetType(JNIEnv *_env, jobject _this, jint slot, jint t)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramVertexSetType, con(%p), vpf(%p), slot(%i), a(%p)", con, (RsProgramVertex)vpv, slot, (RsType)t);
+    rsProgramVertexSetType(slot, (RsType)t);
+}
+
+static void
+nProgramVertexSetTextureMatrixEnable(JNIEnv *_env, jobject _this, jboolean enable)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramVertexSetTextureMatrixEnable, con(%p), enable(%i)", con, enable);
+    rsProgramVertexSetTextureMatrixEnable(enable);
+}
+
+static jint
+nProgramVertexCreate(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramVertexCreate, con(%p)", con);
+    return (jint)rsProgramVertexCreate();
+}
+
+static void
+nProgramVertexDestroy(JNIEnv *_env, jobject _this, jint pgm)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nProgramFragmentDestroy, con(%p), pgm(%i)", con, pgm);
+    rsProgramFragmentDestroy((RsProgramFragment)pgm);
+}
+
+
+
+
+// ---------------------------------------------------------------------------
+
+static void
+nContextBindRootScript(JNIEnv *_env, jobject _this, jint script)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nContextBindRootScript, con(%p), script(%p)", con, (RsScript)script);
+    rsContextBindRootScript((RsScript)script);
+}
+
+static void
+nContextBindProgramFragmentStore(JNIEnv *_env, jobject _this, jint pfs)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nContextBindProgramFragmentStore, con(%p), pfs(%p)", con, (RsProgramFragmentStore)pfs);
+    rsContextBindProgramFragmentStore((RsProgramFragmentStore)pfs);
+}
+
+static void
+nContextBindProgramFragment(JNIEnv *_env, jobject _this, jint pf)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nContextBindProgramFragment, con(%p), pf(%p)", con, (RsProgramFragment)pf);
+    rsContextBindProgramFragment((RsProgramFragment)pf);
+}
+
+static void
+nContextBindProgramVertex(JNIEnv *_env, jobject _this, jint pf)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nContextBindProgramVertex, con(%p), pf(%p)", con, (RsProgramVertex)pf);
+    rsContextBindProgramVertex((RsProgramVertex)pf);
+}
+
+// ---------------------------------------------------------------------------
+
+static void
+nSamplerDestroy(JNIEnv *_env, jobject _this, jint s)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nSamplerDestroy, con(%p), sampler(%p)", con, (RsSampler)s);
+    rsSamplerDestroy((RsSampler)s);
+}
+
+static void
+nSamplerBegin(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nSamplerBegin, con(%p)", con);
+    rsSamplerBegin();
+}
+
+static void
+nSamplerSet(JNIEnv *_env, jobject _this, jint p, jint v)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nSamplerSet, con(%p), param(%i), value(%i)", con, p, v);
+    rsSamplerSet((RsSamplerParam)p, (RsSamplerValue)v);
+}
+
+static jint
+nSamplerCreate(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nSamplerCreate, con(%p)", con);
+    return (jint)rsSamplerCreate();
+}
+
+// ---------------------------------------------------------------------------
+
+static void
+nLightBegin(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nLightBegin, con(%p)", con);
+    rsLightBegin();
+}
+
+static void
+nLightSetIsMono(JNIEnv *_env, jobject _this, jboolean isMono)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nLightSetIsMono, con(%p), isMono(%i)", con, isMono);
+    rsLightSetMonochromatic(isMono);
+}
+
+static void
+nLightSetIsLocal(JNIEnv *_env, jobject _this, jboolean isLocal)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nLightSetIsLocal, con(%p), isLocal(%i)", con, isLocal);
+    rsLightSetLocal(isLocal);
+}
+
+static jint
+nLightCreate(JNIEnv *_env, jobject _this)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nLightCreate, con(%p)", con);
+    return (jint)rsLightCreate();
+}
+
+static void
+nLightDestroy(JNIEnv *_env, jobject _this, jint light)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nLightDestroy, con(%p), light(%p)", con, (RsLight)light);
+    rsLightDestroy((RsLight)light);
+}
+
+static void
+nLightSetColor(JNIEnv *_env, jobject _this, jint light, float r, float g, float b)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nLightSetColor, con(%p), light(%p), r(%f), g(%f), b(%f)", con, (RsLight)light, r, g, b);
+    rsLightSetColor((RsLight)light, r, g, b);
+}
+
+static void
+nLightSetPosition(JNIEnv *_env, jobject _this, jint light, float x, float y, float z)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nLightSetPosition, con(%p), light(%p), x(%f), y(%f), z(%f)", con, (RsLight)light, x, y, z);
+    rsLightSetPosition((RsLight)light, x, y, z);
+}
+
+// ---------------------------------------------------------------------------
+
+
+static const char *classPathName = "android/renderscript/RenderScript";
+
+static JNINativeMethod methods[] = {
+{"_nInit",                         "()V",                                  (void*)_nInit },
+{"nDeviceCreate",                  "()I",                                  (void*)nDeviceCreate },
+{"nDeviceDestroy",                 "(I)V",                                 (void*)nDeviceDestroy },
+{"nContextCreate",                 "(ILandroid/view/Surface;I)I",          (void*)nContextCreate },
+{"nContextDestroy",                "(I)V",                                 (void*)nContextDestroy },
+{"nAssignName",                    "(I[B)V",                               (void*)nAssignName },
+
+{"nElementBegin",                  "()V",                                  (void*)nElementBegin },
+{"nElementAddPredefined",          "(I)V",                                 (void*)nElementAddPredefined },
+{"nElementAdd",                    "(IIII)V",                              (void*)nElementAdd },
+{"nElementCreate",                 "()I",                                  (void*)nElementCreate },
+{"nElementGetPredefined",          "(I)I",                                 (void*)nElementGetPredefined },
+{"nElementDestroy",                "(I)V",                                 (void*)nElementDestroy },
+
+{"nTypeBegin",                     "(I)V",                                 (void*)nTypeBegin },
+{"nTypeAdd",                       "(II)V",                                (void*)nTypeAdd },
+{"nTypeCreate",                    "()I",                                  (void*)nTypeCreate },
+{"nTypeDestroy",                   "(I)V",                                 (void*)nTypeDestroy },
+
+{"nAllocationCreateTyped",         "(I)I",                                 (void*)nAllocationCreateTyped },
+{"nAllocationCreatePredefSized",   "(II)I",                                (void*)nAllocationCreatePredefSized },
+{"nAllocationCreateSized",         "(II)I",                                (void*)nAllocationCreateSized },
+{"nAllocationCreateFromBitmap",    "(IZLandroid/graphics/Bitmap;)I",       (void*)nAllocationCreateFromBitmap },
+{"nAllocationUploadToTexture",     "(II)V",                                (void*)nAllocationUploadToTexture },
+{"nAllocationDestroy",             "(I)V",                                 (void*)nAllocationDestroy },
+{"nAllocationData",                "(I[I)V",                               (void*)nAllocationData_i },
+{"nAllocationData",                "(I[F)V",                               (void*)nAllocationData_f },
+{"nAllocationSubData1D",           "(III[I)V",                             (void*)nAllocationSubData1D_i },
+{"nAllocationSubData1D",           "(III[F)V",                             (void*)nAllocationSubData1D_f },
+{"nAllocationSubData2D",           "(IIIII[I)V",                           (void*)nAllocationSubData2D_i },
+{"nAllocationSubData2D",           "(IIIII[F)V",                           (void*)nAllocationSubData2D_f },
+
+{"nTriangleMeshDestroy",           "(I)V",                                 (void*)nTriangleMeshDestroy },
+{"nTriangleMeshBegin",             "(II)V",                                (void*)nTriangleMeshBegin },
+{"nTriangleMeshAddVertex_XY",      "(FF)V",                                (void*)nTriangleMeshAddVertex_XY },
+{"nTriangleMeshAddVertex_XYZ",     "(FFF)V",                               (void*)nTriangleMeshAddVertex_XYZ },
+{"nTriangleMeshAddVertex_XY_ST",   "(FFFF)V",                              (void*)nTriangleMeshAddVertex_XY_ST },
+{"nTriangleMeshAddVertex_XYZ_ST",  "(FFFFF)V",                             (void*)nTriangleMeshAddVertex_XYZ_ST },
+{"nTriangleMeshAddVertex_XYZ_ST_NORM",  "(FFFFFFFF)V",                     (void*)nTriangleMeshAddVertex_XYZ_ST_NORM },
+{"nTriangleMeshAddTriangle",       "(III)V",                               (void*)nTriangleMeshAddTriangle },
+{"nTriangleMeshCreate",            "()I",                                  (void*)nTriangleMeshCreate },
+
+{"nAdapter1DDestroy",              "(I)V",                                 (void*)nAdapter1DDestroy },
+{"nAdapter1DBindAllocation",       "(II)V",                                (void*)nAdapter1DBindAllocation },
+{"nAdapter1DSetConstraint",        "(III)V",                               (void*)nAdapter1DSetConstraint },
+{"nAdapter1DData",                 "(I[I)V",                               (void*)nAdapter1DData_i },
+{"nAdapter1DSubData",              "(III[I)V",                             (void*)nAdapter1DSubData_i },
+{"nAdapter1DData",                 "(I[F)V",                               (void*)nAdapter1DData_f },
+{"nAdapter1DSubData",              "(III[F)V",                             (void*)nAdapter1DSubData_f },
+{"nAdapter1DCreate",               "()I",                                  (void*)nAdapter1DCreate },
+
+{"nScriptDestroy",                 "(I)V",                                 (void*)nScriptDestroy },
+{"nScriptBindAllocation",          "(III)V",                               (void*)nScriptBindAllocation },
+{"nScriptCBegin",                  "()V",                                  (void*)nScriptCBegin },
+{"nScriptCSetClearColor",          "(FFFF)V",                              (void*)nScriptCSetClearColor },
+{"nScriptCSetClearDepth",          "(F)V",                                 (void*)nScriptCSetClearDepth },
+{"nScriptCSetClearStencil",        "(I)V",                                 (void*)nScriptCSetClearStencil },
+{"nScriptCAddType",                "(I)V",                                 (void*)nScriptCAddType },
+{"nScriptCSetRoot",                "(Z)V",                                 (void*)nScriptCSetRoot },
+{"nScriptCSetScript",              "([BII)V",                              (void*)nScriptCSetScript },
+{"nScriptCCreate",                 "()I",                                  (void*)nScriptCCreate },
+
+{"nProgramFragmentStoreBegin",     "(II)V",                                (void*)nProgramFragmentStoreBegin },
+{"nProgramFragmentStoreDepthFunc", "(I)V",                                 (void*)nProgramFragmentStoreDepthFunc },
+{"nProgramFragmentStoreDepthMask", "(Z)V",                                 (void*)nProgramFragmentStoreDepthMask },
+{"nProgramFragmentStoreColorMask", "(ZZZZ)V",                              (void*)nProgramFragmentStoreColorMask },
+{"nProgramFragmentStoreBlendFunc", "(II)V",                                (void*)nProgramFragmentStoreBlendFunc },
+{"nProgramFragmentStoreDither",    "(Z)V",                                 (void*)nProgramFragmentStoreDither },
+{"nProgramFragmentStoreCreate",    "()I",                                  (void*)nProgramFragmentStoreCreate },
+{"nProgramFragmentStoreDestroy",   "(I)V",                                 (void*)nProgramFragmentStoreDestroy },
+
+{"nProgramFragmentBegin",          "(II)V",                                (void*)nProgramFragmentBegin },
+{"nProgramFragmentBindTexture",    "(III)V",                               (void*)nProgramFragmentBindTexture },
+{"nProgramFragmentBindSampler",    "(III)V",                               (void*)nProgramFragmentBindSampler },
+{"nProgramFragmentSetType",        "(II)V",                                (void*)nProgramFragmentSetType },
+{"nProgramFragmentSetEnvMode",     "(II)V",                                (void*)nProgramFragmentSetEnvMode },
+{"nProgramFragmentSetTexEnable",   "(IZ)V",                                (void*)nProgramFragmentSetTexEnable },
+{"nProgramFragmentCreate",         "()I",                                  (void*)nProgramFragmentCreate },
+{"nProgramFragmentDestroy",        "(I)V",                                 (void*)nProgramFragmentDestroy },
+
+{"nProgramVertexDestroy",          "(I)V",                                 (void*)nProgramVertexDestroy },
+{"nProgramVertexBindAllocation",   "(III)V",                               (void*)nProgramVertexBindAllocation },
+{"nProgramVertexBegin",            "(II)V",                                (void*)nProgramVertexBegin },
+{"nProgramVertexSetType",          "(II)V",                                (void*)nProgramVertexSetType },
+{"nProgramVertexSetTextureMatrixEnable",   "(Z)V",                         (void*)nProgramVertexSetTextureMatrixEnable },
+{"nProgramVertexCreate",           "()I",                                  (void*)nProgramVertexCreate },
+
+{"nLightBegin",                    "()V",                                  (void*)nLightBegin },
+{"nLightSetIsMono",                "(Z)V",                                 (void*)nLightSetIsMono },
+{"nLightSetIsLocal",               "(Z)V",                                 (void*)nLightSetIsLocal },
+{"nLightCreate",                   "()I",                                  (void*)nLightCreate },
+{"nLightDestroy",                  "(I)V",                                 (void*)nLightDestroy },
+{"nLightSetColor",                 "(IFFF)V",                              (void*)nLightSetColor },
+{"nLightSetPosition",              "(IFFF)V",                              (void*)nLightSetPosition },
+
+{"nContextBindRootScript",         "(I)V",                                 (void*)nContextBindRootScript },
+{"nContextBindProgramFragmentStore","(I)V",                                (void*)nContextBindProgramFragmentStore },
+{"nContextBindProgramFragment",    "(I)V",                                 (void*)nContextBindProgramFragment },
+{"nContextBindProgramVertex",      "(I)V",                                 (void*)nContextBindProgramVertex },
+
+{"nSamplerDestroy",                "(I)V",                                 (void*)nSamplerDestroy },
+{"nSamplerBegin",                  "()V",                                  (void*)nSamplerBegin },
+{"nSamplerSet",                    "(II)V",                                (void*)nSamplerSet },
+{"nSamplerCreate",                 "()I",                                  (void*)nSamplerCreate },
+
+};
+
+static int registerFuncs(JNIEnv *_env)
+{
+    return android::AndroidRuntime::registerNativeMethods(
+            _env, classPathName, methods, NELEM(methods));
+}
+
+// ---------------------------------------------------------------------------
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        LOGE("ERROR: GetEnv failed\n");
+        goto bail;
+    }
+    assert(env != NULL);
+
+    if (registerFuncs(env) < 0) {
+        LOGE("ERROR: MediaPlayer native registration failed\n");
+        goto bail;
+    }
+
+    /* success -- return valid version number */
+    result = JNI_VERSION_1_4;
+
+bail:
+    return result;
+}
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
new file mode 100644
index 0000000..fadda59
--- /dev/null
+++ b/libs/rs/rs.spec
@@ -0,0 +1,438 @@
+
+
+ContextBindRootScript {
+	param RsScript sampler
+	}
+
+ContextBindProgramFragmentStore {
+	param RsProgramFragmentStore pgm
+	}
+
+ContextBindProgramFragment {
+	param RsProgramFragment pgm
+	}
+
+ContextBindProgramVertex {
+	param RsProgramVertex pgm
+	}
+
+AssignName {
+	param void *obj
+	param const char *name
+	param size_t len
+	}
+
+ElementBegin {
+}
+
+ElementAddPredefined {
+	param RsElementPredefined predef
+	}
+
+ElementAdd {
+	param RsDataKind dataKind
+	param RsDataType dataType
+	param bool isNormalized
+	param size_t bits
+	}
+
+ElementCreate {
+	ret RsElement
+	}
+
+ElementGetPredefined {
+	param RsElementPredefined predef
+	ret RsElement
+	}
+
+ElementDestroy {
+	param RsElement ve
+	}
+
+TypeBegin {
+	param RsElement type
+	}
+
+TypeAdd {
+	param RsDimension dim
+	param size_t value
+	}
+
+TypeCreate {
+	ret RsType
+	}
+
+TypeDestroy {
+	param RsType p
+	}
+
+AllocationCreateTyped {
+	param RsType type
+	ret RsAllocation
+	}
+
+AllocationCreatePredefSized {
+	param RsElementPredefined predef
+	param size_t count
+	ret RsAllocation
+	}
+
+AllocationCreateSized {
+	param RsElement e
+	param size_t count
+	ret RsAllocation
+	}
+
+AllocationCreateFromFile {
+	param const char *file
+	param bool genMips
+	ret RsAllocation
+	}
+
+AllocationCreateFromBitmap {
+	param uint32_t width
+	param uint32_t height
+	param RsElementPredefined dstFmt
+	param RsElementPredefined srcFmt
+	param bool genMips
+	param const void * data
+	ret RsAllocation
+	}
+
+
+AllocationUploadToTexture {
+	param RsAllocation alloc
+	param uint32_t baseMipLevel
+	}
+
+AllocationUploadToBufferObject {
+	param RsAllocation alloc
+	}
+
+AllocationDestroy {
+	param RsAllocation alloc
+	}
+
+
+AllocationData {
+	param RsAllocation va
+	param const void * data
+	} 
+
+Allocation1DSubData {
+	param RsAllocation va
+	param uint32_t xoff
+	param uint32_t count
+	param const void *data
+	} 
+
+Allocation2DSubData {
+	param RsAllocation va
+	param uint32_t xoff
+	param uint32_t yoff
+	param uint32_t w
+	param uint32_t h
+	param const void *data
+	}
+
+
+Adapter1DCreate {
+	ret RsAdapter1D
+	}
+
+Adapter1DBindAllocation {
+	param RsAdapter1D adapt
+	param RsAllocation alloc
+	}
+
+Adapter1DDestroy {
+	param RsAdapter1D adapter
+	}
+
+Adapter1DSetConstraint {
+	param RsAdapter1D adapter
+	param RsDimension dim
+	param uint32_t value
+	}
+
+Adapter1DData {
+	param RsAdapter1D adapter
+	param const void * data
+	} 
+
+Adapter1DSubData {
+	param RsAdapter1D adapter
+	param uint32_t xoff
+	param uint32_t count
+	param const void *data
+	} 
+
+Adapter2DCreate {
+	ret RsAdapter2D
+	}
+
+Adapter2DBindAllocation {
+	param RsAdapter2D adapt
+	param RsAllocation alloc
+	}
+
+Adapter2DDestroy {
+	param RsAdapter2D adapter
+	}
+
+Adapter2DSetConstraint {
+	param RsAdapter2D adapter
+	param RsDimension dim
+	param uint32_t value
+	}
+
+Adapter2DData {
+	param RsAdapter2D adapter
+	param const void *data
+	} 
+
+Adapter2DSubData {
+	param RsAdapter2D adapter
+	param uint32_t xoff
+	param uint32_t yoff
+	param uint32_t w
+	param uint32_t h
+	param const void *data
+	}
+
+SamplerBegin {
+	}
+
+SamplerSet {
+	param RsSamplerParam p
+	param RsSamplerValue value
+	}
+
+SamplerCreate {
+	ret RsSampler
+	}
+
+SamplerDestroy {
+	param RsSampler s
+	}
+
+TriangleMeshBegin {
+	param RsElement vertex
+	param RsElement index
+	}
+
+TriangleMeshAddVertex {
+	param const void *vtx
+	}
+
+TriangleMeshAddTriangle {
+	param uint32_t idx1
+	param uint32_t idx2
+	param uint32_t idx3
+	}
+
+TriangleMeshCreate {
+	ret RsTriangleMesh
+	}
+
+TriangleMeshDestroy {
+	param RsTriangleMesh mesh
+	}
+
+TriangleMeshRender {
+	param RsTriangleMesh vtm
+	}
+
+TriangleMeshRenderRange {
+	param RsTriangleMesh vtm
+	param uint32_t start
+	param uint32_t count
+	}
+
+ScriptDestroy {
+	param RsScript script
+	}
+
+ScriptBindAllocation {
+	param RsScript vtm
+	param RsAllocation va
+	param uint32_t slot
+	}
+
+
+ScriptCBegin {
+	}
+
+ScriptCSetClearColor {
+	param float r
+	param float g
+	param float b
+	param float a
+	}
+
+ScriptCSetClearDepth {
+	param float depth
+	}
+
+ScriptCSetClearStencil {
+	param uint32_t stencil
+	}
+
+ScriptCAddType {
+	param RsType type
+	}
+
+ScriptCSetRoot {
+	param bool isRoot
+	}
+
+ScriptCSetScript {
+	param void * codePtr
+	}
+
+ScriptCSetText {
+	param const char * text
+	param uint32_t length
+	}
+
+ScriptCCreate {
+	ret RsScript
+	}
+
+
+ProgramFragmentStoreBegin {
+	param RsElement in
+	param RsElement out
+	}
+
+ProgramFragmentStoreColorMask {
+	param bool r
+	param bool g
+	param bool b
+	param bool a
+	}
+
+ProgramFragmentStoreBlendFunc {
+	param RsBlendSrcFunc srcFunc
+	param RsBlendDstFunc destFunc
+	}
+
+ProgramFragmentStoreDepthMask {
+	param bool enable
+}
+
+ProgramFragmentStoreDither {
+	param bool enable
+}
+
+ProgramFragmentStoreDepthFunc {
+	param RsDepthFunc func
+}
+
+ProgramFragmentStoreCreate {
+	ret RsProgramFragmentStore
+	}
+
+ProgramFragmentStoreDestroy {
+	param RsProgramFragmentStore pfs
+	}
+
+
+ProgramFragmentBegin {
+	param RsElement in
+	param RsElement out
+	}
+
+ProgramFragmentBindTexture {
+	param RsProgramFragment pf
+	param uint32_t slot
+	param RsAllocation a
+	}
+
+ProgramFragmentBindSampler {
+	param RsProgramFragment pf
+	param uint32_t slot
+	param RsSampler s
+	}
+
+ProgramFragmentSetType {
+	param uint32_t slot
+	param RsType t
+	}
+
+ProgramFragmentSetEnvMode {
+	param uint32_t slot
+	param RsTexEnvMode env
+	}
+
+ProgramFragmentSetTexEnable {
+	param uint32_t slot
+	param bool enable
+	}
+
+ProgramFragmentCreate {
+	ret RsProgramFragment
+	}
+
+ProgramFragmentDestroy {
+	param RsProgramFragment pf
+	}
+
+
+ProgramVertexBegin {
+	param RsElement in
+	param RsElement out
+	}
+
+ProgramVertexCreate {
+	ret RsProgramVertex
+	}
+
+ProgramVertexBindAllocation {
+	param RsProgramVertex vpgm
+	param uint32_t slot
+	param RsAllocation constants
+	}
+
+ProgramVertexSetType {
+	param uint32_t slot
+	param RsType constants
+	}
+
+ProgramVertexSetTextureMatrixEnable {
+	param bool enable
+	}
+
+LightBegin {
+	}
+
+LightSetLocal {
+	param bool isLocal
+	}
+
+LightSetMonochromatic {
+	param bool isMono
+	}
+
+LightCreate {
+	ret RsLight light
+	}
+
+LightDestroy {
+	param RsLight light
+	}
+
+LightSetPosition {
+	param RsLight light
+	param float x
+	param float y
+	param float z
+	}
+
+LightSetColor {
+	param RsLight light
+	param float r
+	param float g
+	param float b
+	}
+
diff --git a/libs/rs/rsAdapter.cpp b/libs/rs/rsAdapter.cpp
new file mode 100644
index 0000000..7ac2aed
--- /dev/null
+++ b/libs/rs/rsAdapter.cpp
@@ -0,0 +1,245 @@
+
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Adapter1D::Adapter1D()
+{
+    reset();
+}
+
+Adapter1D::Adapter1D(Allocation *a)
+{
+    reset();
+    setAllocation(a);
+}
+
+void Adapter1D::reset()
+{
+    mY = 0;
+    mZ = 0;
+    mLOD = 0;
+    mFace = 0;
+}
+
+void * Adapter1D::getElement(uint32_t x)
+{
+    rsAssert(mAllocation.get());
+    rsAssert(mAllocation->getPtr());
+    rsAssert(mAllocation->getType());
+    uint8_t * ptr = static_cast<uint8_t *>(mAllocation->getPtr());
+    ptr += mAllocation->getType()->getLODOffset(mLOD, x, mY);
+    return ptr;
+}
+
+void Adapter1D::subData(uint32_t xoff, uint32_t count, const void *data)
+{
+    if (mAllocation.get() && mAllocation.get()->getType()) {
+        void *ptr = getElement(xoff);
+        count *= mAllocation.get()->getType()->getElementSizeBytes();
+        memcpy(ptr, data, count);
+    }
+}
+
+void Adapter1D::data(const void *data)
+{
+    memcpy(getElement(0), 
+           data, 
+           mAllocation.get()->getType()->getSizeBytes());
+}
+
+namespace android {
+namespace renderscript {
+
+RsAdapter1D rsi_Adapter1DCreate(Context *rsc)
+{
+    return new Adapter1D();
+}
+
+void rsi_Adapter1DDestroy(Context *rsc, RsAdapter1D va)
+{
+    Adapter1D * a = static_cast<Adapter1D *>(va);
+    a->decRef();
+}
+
+void rsi_Adapter1DBindAllocation(Context *rsc, RsAdapter1D va, RsAllocation valloc)
+{
+    Adapter1D * a = static_cast<Adapter1D *>(va);
+    Allocation * alloc = static_cast<Allocation *>(valloc);
+    a->setAllocation(alloc);
+}
+
+void rsi_Adapter1DSetConstraint(Context *rsc, RsAdapter1D va, RsDimension dim, uint32_t value)
+{
+    Adapter1D * a = static_cast<Adapter1D *>(va);
+    switch(dim) {
+    case RS_DIMENSION_X:
+        rsAssert(!"Cannot contrain X in an 1D adapter");
+        return;
+    case RS_DIMENSION_Y:
+        a->setY(value);
+        break;
+    case RS_DIMENSION_Z:
+        a->setZ(value);
+        break;
+    case RS_DIMENSION_LOD:
+        a->setLOD(value);
+        break;
+    case RS_DIMENSION_FACE:
+        a->setFace(value);
+        break;
+    default:
+        rsAssert(!"Unimplemented constraint");
+        return;
+    }
+}
+
+void rsi_Adapter1DSubData(Context *rsc, RsAdapter1D va, uint32_t xoff, uint32_t count, const void *data)
+{
+    Adapter1D * a = static_cast<Adapter1D *>(va);
+    a->subData(xoff, count, data);
+}
+
+void rsi_Adapter1DData(Context *rsc, RsAdapter1D va, const void *data)
+{
+    Adapter1D * a = static_cast<Adapter1D *>(va);
+    a->data(data);
+}
+
+}
+}
+
+//////////////////////////
+
+Adapter2D::Adapter2D()
+{
+    reset();
+}
+
+Adapter2D::Adapter2D(Allocation *a)
+{
+    reset();
+    setAllocation(a);
+}
+
+void Adapter2D::reset()
+{
+    mZ = 0;
+    mLOD = 0;
+    mFace = 0;
+}
+
+void * Adapter2D::getElement(uint32_t x, uint32_t y) const
+{
+    rsAssert(mAllocation.get());
+    rsAssert(mAllocation->getPtr());
+    rsAssert(mAllocation->getType());
+    uint8_t * ptr = static_cast<uint8_t *>(mAllocation->getPtr());
+    ptr += mAllocation->getType()->getLODOffset(mLOD, x, y);
+    return ptr;
+}
+
+void Adapter2D::subData(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data)
+{
+    rsAssert(mAllocation.get());
+    rsAssert(mAllocation->getPtr());
+    rsAssert(mAllocation->getType());
+
+    uint32_t eSize = mAllocation.get()->getType()->getElementSizeBytes();
+    uint32_t lineSize = eSize * w;
+    uint32_t destW = getDimX();
+
+    const uint8_t *src = static_cast<const uint8_t *>(data);
+    for (uint32_t line=yoff; line < (yoff+h); line++) {
+        memcpy(getElement(xoff, line), src, lineSize);
+        src += lineSize;
+    }
+}
+
+void Adapter2D::data(const void *data)
+{
+    memcpy(getElement(0,0), 
+           data, 
+           mAllocation.get()->getType()->getSizeBytes());
+}
+
+
+
+namespace android {
+namespace renderscript {
+
+RsAdapter2D rsi_Adapter2DCreate(Context *rsc)
+{
+    return new Adapter2D();
+}
+
+void rsi_Adapter2DDestroy(Context *rsc, RsAdapter2D va)
+{
+    Adapter2D * a = static_cast<Adapter2D *>(va);
+    a->decRef();
+}
+
+void rsi_Adapter2DBindAllocation(Context *rsc, RsAdapter2D va, RsAllocation valloc)
+{
+    Adapter2D * a = static_cast<Adapter2D *>(va);
+    Allocation * alloc = static_cast<Allocation *>(valloc);
+    a->setAllocation(alloc);
+}
+
+void rsi_Adapter2DSetConstraint(Context *rsc, RsAdapter2D va, RsDimension dim, uint32_t value)
+{
+    Adapter2D * a = static_cast<Adapter2D *>(va);
+    switch(dim) {
+    case RS_DIMENSION_X:
+        rsAssert(!"Cannot contrain X in an 2D adapter");
+        return;
+    case RS_DIMENSION_Y:
+        rsAssert(!"Cannot contrain Y in an 2D adapter");
+        break;
+    case RS_DIMENSION_Z:
+        a->setZ(value);
+        break;
+    case RS_DIMENSION_LOD:
+        a->setLOD(value);
+        break;
+    case RS_DIMENSION_FACE:
+        a->setFace(value);
+        break;
+    default:
+        rsAssert(!"Unimplemented constraint");
+        return;
+    }
+}
+
+void rsi_Adapter2DData(Context *rsc, RsAdapter2D va, const void *data)
+{
+    Adapter2D * a = static_cast<Adapter2D *>(va);
+    a->data(data);
+}
+
+void rsi_Adapter2DSubData(Context *rsc, RsAdapter2D va, uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data)
+{
+    Adapter2D * a = static_cast<Adapter2D *>(va);
+    a->subData(xoff, yoff, w, h, data);
+}
+
+}
+}
diff --git a/libs/rs/rsAdapter.h b/libs/rs/rsAdapter.h
new file mode 100644
index 0000000..865535e
--- /dev/null
+++ b/libs/rs/rsAdapter.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_ADAPTER_H
+#define ANDROID_RS_ADAPTER_H
+
+#include "rsAllocation.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+    
+class Adapter1D : public ObjectBase
+{
+
+public:
+    // By policy this allocation will hold a pointer to the type
+    // but will not destroy it on destruction.
+    Adapter1D();
+    Adapter1D(Allocation *);
+    void reset();
+    void * getElement(uint32_t x);
+
+    void setAllocation(Allocation *a) {mAllocation.set(a);}
+
+    uint32_t getDimX() const {return mAllocation->getType()->getLODDimX(mLOD);}
+
+    const Type * getBaseType() const {return mAllocation->getType();}
+
+    inline void setY(uint32_t y) {mY = y;}
+    inline void setZ(uint32_t z) {mZ = z;}
+    inline void setLOD(uint32_t lod) {mLOD = lod;}
+    inline void setFace(uint32_t face) {mFace = face;}
+    //void setArray(uint32_t num, uint32_t value);
+
+    void subData(uint32_t xoff, uint32_t count, const void *data);
+    void data(const void *data);
+
+protected:
+    ObjectBaseRef<Allocation> mAllocation;
+    uint32_t mY;
+    uint32_t mZ;
+    uint32_t mLOD;
+    uint32_t mFace;
+};
+
+class Adapter2D : public ObjectBase
+{
+
+public:
+    // By policy this allocation will hold a pointer to the type
+    // but will not destroy it on destruction.
+    Adapter2D();
+    Adapter2D(Allocation *);
+    void reset();
+    void * getElement(uint32_t x, uint32_t y) const;
+
+    uint32_t getDimX() const {return mAllocation->getType()->getLODDimX(mLOD);}
+    uint32_t getDimY() const {return mAllocation->getType()->getLODDimY(mLOD);}
+    const Type * getBaseType() const {return mAllocation->getType();}
+
+    void setAllocation(Allocation *a) {mAllocation.set(a);}
+    inline void setZ(uint32_t z) {mZ = z;}
+    inline void setLOD(uint32_t lod) {mLOD = lod;}
+    inline void setFace(uint32_t face) {mFace = face;}
+    //void setArray(uint32_t num, uint32_t value);
+
+    void data(const void *data); 
+    void subData(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data); 
+
+protected:
+    ObjectBaseRef<Allocation> mAllocation;
+    uint32_t mZ;
+    uint32_t mLOD;
+    uint32_t mFace;
+};
+
+
+}
+}
+#endif
+
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
new file mode 100644
index 0000000..bc14eac
--- /dev/null
+++ b/libs/rs/rsAllocation.cpp
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+Allocation::Allocation(const Type *type)
+{
+    mPtr = NULL;
+
+    mCpuWrite = false;
+    mCpuRead = false;
+    mGpuWrite = false;
+    mGpuRead = false;
+
+    mReadWriteRatio = 0;
+    mUpdateSize = 0;
+
+    mIsTexture = false;
+    mTextureID = 0;
+
+    mIsVertexBuffer = false;
+    mBufferID = 0;
+
+    mType.set(type);
+    mPtr = malloc(mType->getSizeBytes());
+    if (!mPtr) {
+        LOGE("Allocation::Allocation, alloc failure");
+    }
+
+}
+
+Allocation::~Allocation()
+{
+}
+
+void Allocation::setCpuWritable(bool)
+{
+}
+
+void Allocation::setGpuWritable(bool)
+{
+}
+
+void Allocation::setCpuReadable(bool)
+{
+}
+
+void Allocation::setGpuReadable(bool)
+{
+}
+
+bool Allocation::fixAllocation()
+{
+    return false;
+}
+
+void Allocation::uploadToTexture(uint32_t lodOffset)
+{
+    //rsAssert(!mTextureId);
+    rsAssert(lodOffset < mType->getLODCount());
+
+    GLenum type = mType->getElement()->getGLType();
+    GLenum format = mType->getElement()->getGLFormat();
+
+    if (!type || !format) {
+        return;
+    }
+
+    if (!mTextureID) {
+        glGenTextures(1, &mTextureID);
+    }
+    glBindTexture(GL_TEXTURE_2D, mTextureID);
+
+    Adapter2D adapt(this);
+    for(uint32_t lod = 0; (lod + lodOffset) < mType->getLODCount(); lod++) {
+        adapt.setLOD(lod+lodOffset);
+
+        uint16_t * ptr = static_cast<uint16_t *>(adapt.getElement(0,0));
+        glTexImage2D(GL_TEXTURE_2D, lod, format,
+                     adapt.getDimX(), adapt.getDimY(),
+                     0, format, type, ptr);
+    }
+}
+
+void Allocation::uploadToBufferObject()
+{
+    rsAssert(!mType->getDimY());
+    rsAssert(!mType->getDimZ());
+
+    if (!mBufferID) {
+        glGenBuffers(1, &mBufferID);
+    }
+    glBindBuffer(GL_ARRAY_BUFFER, mBufferID);
+    glBufferData(GL_ARRAY_BUFFER, mType->getSizeBytes(), getPtr(), GL_DYNAMIC_DRAW);
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void Allocation::data(const void *data)
+{
+    memcpy(mPtr, data, mType->getSizeBytes());
+}
+
+void Allocation::subData(uint32_t xoff, uint32_t count, const void *data)
+{
+    uint32_t eSize = mType->getElementSizeBytes();
+    uint8_t * ptr = static_cast<uint8_t *>(mPtr);
+    ptr += eSize * xoff;
+    memcpy(ptr, data, count * eSize);
+}
+
+void Allocation::subData(uint32_t xoff, uint32_t yoff,
+             uint32_t w, uint32_t h, const void *data)
+{
+    uint32_t eSize = mType->getElementSizeBytes();
+    uint32_t lineSize = eSize * w;
+    uint32_t destW = mType->getDimX();
+
+    const uint8_t *src = static_cast<const uint8_t *>(data);
+    uint8_t *dst = static_cast<uint8_t *>(mPtr);
+    dst += eSize * (xoff + yoff * destW);
+    for (uint32_t line=yoff; line < (yoff+h); line++) {
+        uint8_t * ptr = static_cast<uint8_t *>(mPtr);
+        memcpy(dst, src, lineSize);
+        src += lineSize;
+        dst += destW * eSize;
+    }
+}
+
+void Allocation::subData(uint32_t xoff, uint32_t yoff, uint32_t zoff,
+             uint32_t w, uint32_t h, uint32_t d, const void *data)
+{
+}
+
+
+
+/////////////////
+//
+
+
+namespace android {
+namespace renderscript {
+
+RsAllocation rsi_AllocationCreateTyped(Context *rsc, RsType vtype)
+{
+    const Type * type = static_cast<const Type *>(vtype);
+
+    Allocation * alloc = new Allocation(type);
+    return alloc;
+}
+
+RsAllocation rsi_AllocationCreatePredefSized(Context *rsc, RsElementPredefined t, size_t count)
+{
+    RsElement e = rsi_ElementGetPredefined(rsc, t);
+    return rsi_AllocationCreateSized(rsc, e, count);
+}
+
+RsAllocation rsi_AllocationCreateSized(Context *rsc, RsElement e, size_t count)
+{
+    Type * type = new Type();
+    type->setDimX(count);
+    type->setElement(static_cast<Element *>(e));
+    type->compute();
+    return rsi_AllocationCreateTyped(rsc, type);
+}
+
+void rsi_AllocationUploadToTexture(Context *rsc, RsAllocation va, uint32_t baseMipLevel)
+{
+    Allocation *alloc = static_cast<Allocation *>(va);
+    alloc->uploadToTexture(baseMipLevel);
+}
+
+void rsi_AllocationUploadToBufferObject(Context *rsc, RsAllocation va)
+{
+    Allocation *alloc = static_cast<Allocation *>(va);
+    alloc->uploadToBufferObject();
+}
+
+void rsi_AllocationDestroy(Context *rsc, RsAllocation)
+{
+}
+
+static void mip565(const Adapter2D &out, const Adapter2D &in)
+{
+    uint32_t w = out.getDimX();
+    uint32_t h = out.getDimY();
+
+    for (uint32_t y=0; y < w; y++) {
+        uint16_t *oPtr = static_cast<uint16_t *>(out.getElement(0, y));
+        const uint16_t *i1 = static_cast<uint16_t *>(in.getElement(0, y*2));
+        const uint16_t *i2 = static_cast<uint16_t *>(in.getElement(0, y*2+1));
+
+        for (uint32_t x=0; x < h; x++) {
+            *oPtr = rsBoxFilter565(i1[0], i1[1], i2[0], i2[1]);
+            oPtr ++;
+            i1 += 2;
+            i2 += 2;
+        }
+    }
+}
+
+static void mip8888(const Adapter2D &out, const Adapter2D &in)
+{
+    uint32_t w = out.getDimX();
+    uint32_t h = out.getDimY();
+
+    for (uint32_t y=0; y < w; y++) {
+        uint32_t *oPtr = static_cast<uint32_t *>(out.getElement(0, y));
+        const uint32_t *i1 = static_cast<uint32_t *>(in.getElement(0, y*2));
+        const uint32_t *i2 = static_cast<uint32_t *>(in.getElement(0, y*2+1));
+
+        for (uint32_t x=0; x < h; x++) {
+            *oPtr = rsBoxFilter8888(i1[0], i1[1], i2[0], i2[1]);
+            oPtr ++;
+            i1 += 2;
+            i2 += 2;
+        }
+    }
+
+}
+
+
+typedef void (*ElementConverter_t)(void *dst, const void *src, uint32_t count);
+
+static void elementConverter_cpy_16(void *dst, const void *src, uint32_t count)
+{
+    memcpy(dst, src, count * 2);
+}
+static void elementConverter_cpy_8(void *dst, const void *src, uint32_t count)
+{
+    memcpy(dst, src, count);
+}
+static void elementConverter_cpy_32(void *dst, const void *src, uint32_t count)
+{
+    memcpy(dst, src, count * 4);
+}
+
+
+static void elementConverter_888_to_565(void *dst, const void *src, uint32_t count)
+{
+    uint16_t *d = static_cast<uint16_t *>(dst);
+    const uint8_t *s = static_cast<const uint8_t *>(src);
+
+    while(count--) {
+        *d = rs888to565(s[0], s[1], s[2]);
+        d++;
+        s+= 3;
+    }
+}
+
+static void elementConverter_8888_to_565(void *dst, const void *src, uint32_t count)
+{
+    uint16_t *d = static_cast<uint16_t *>(dst);
+    const uint8_t *s = static_cast<const uint8_t *>(src);
+
+    while(count--) {
+        *d = rs888to565(s[0], s[1], s[2]);
+        d++;
+        s+= 4;
+    }
+}
+
+static ElementConverter_t pickConverter(RsElementPredefined dstFmt, RsElementPredefined srcFmt)
+{
+    if ((dstFmt == RS_ELEMENT_RGB_565) &&
+        (srcFmt == RS_ELEMENT_RGB_565)) {
+        return elementConverter_cpy_16;
+    }
+
+    if ((dstFmt == RS_ELEMENT_RGB_565) &&
+        (srcFmt == RS_ELEMENT_RGB_888)) {
+        return elementConverter_888_to_565;
+    }
+
+    if ((dstFmt == RS_ELEMENT_RGB_565) &&
+        (srcFmt == RS_ELEMENT_RGBA_8888)) {
+        return elementConverter_8888_to_565;
+    }
+
+    if ((dstFmt == RS_ELEMENT_RGBA_8888) &&
+        (srcFmt == RS_ELEMENT_RGBA_8888)) {
+        return elementConverter_cpy_32;
+    }
+
+    LOGE("pickConverter, unsuported combo");
+    return 0;
+}
+
+
+RsAllocation rsi_AllocationCreateFromBitmap(Context *rsc, uint32_t w, uint32_t h, RsElementPredefined dstFmt, RsElementPredefined srcFmt,  bool genMips, const void *data)
+{
+    rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGB_565));
+    rsi_TypeAdd(rsc, RS_DIMENSION_X, w);
+    rsi_TypeAdd(rsc, RS_DIMENSION_Y, h);
+    if (genMips) {
+        rsi_TypeAdd(rsc, RS_DIMENSION_LOD, 1);
+    }
+    RsType type = rsi_TypeCreate(rsc);
+
+    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, type);
+    Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
+    if (texAlloc == NULL) {
+        LOGE("Memory allocation failure");
+        return NULL;
+    }
+    texAlloc->incRef();
+
+    ElementConverter_t cvt = pickConverter(dstFmt, srcFmt);
+    cvt(texAlloc->getPtr(), data, w * h);
+
+    if (genMips) {
+        Adapter2D adapt(texAlloc);
+        Adapter2D adapt2(texAlloc);
+        for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
+            adapt.setLOD(lod);
+            adapt2.setLOD(lod + 1);
+            mip565(adapt2, adapt);
+        }
+    }
+
+    return texAlloc;
+}
+
+RsAllocation rsi_AllocationCreateFromFile(Context *rsc, const char *file, bool genMips)
+{
+    bool use32bpp = false;
+
+    typedef struct _Win3xBitmapHeader
+    {
+       uint16_t type;
+       uint32_t totalSize;
+       uint32_t reserved;
+       uint32_t offset;
+       int32_t hdrSize;            /* Size of this header in bytes */
+       int32_t width;           /* Image width in pixels */
+       int32_t height;          /* Image height in pixels */
+       int16_t planes;          /* Number of color planes */
+       int16_t bpp;             /* Number of bits per pixel */
+       /* Fields added for Windows 3.x follow this line */
+       int32_t compression;     /* Compression methods used */
+       int32_t sizeOfBitmap;    /* Size of bitmap in bytes */
+       int32_t horzResolution;  /* Horizontal resolution in pixels per meter */
+       int32_t vertResolution;  /* Vertical resolution in pixels per meter */
+       int32_t colorsUsed;      /* Number of colors in the image */
+       int32_t colorsImportant; /* Minimum number of important colors */
+    } __attribute__((__packed__)) WIN3XBITMAPHEADER;
+
+    _Win3xBitmapHeader hdr;
+
+    FILE *f = fopen(file, "rb");
+    if (f == NULL) {
+        LOGE("rsAllocationCreateFromBitmap failed to open file %s", file);
+        return NULL;
+    }
+    memset(&hdr, 0, sizeof(hdr));
+    fread(&hdr, sizeof(hdr), 1, f);
+
+    if (hdr.bpp != 24) {
+        LOGE("Unsuported BMP type");
+        fclose(f);
+        return NULL;
+    }
+
+    int32_t texWidth = rsHigherPow2(hdr.width);
+    int32_t texHeight = rsHigherPow2(hdr.height);
+
+    if (use32bpp) {
+        rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGBA_8888));
+    } else {
+        rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGB_565));
+    }
+    rsi_TypeAdd(rsc, RS_DIMENSION_X, texWidth);
+    rsi_TypeAdd(rsc, RS_DIMENSION_Y, texHeight);
+    if (genMips) {
+        rsi_TypeAdd(rsc, RS_DIMENSION_LOD, 1);
+    }
+    RsType type = rsi_TypeCreate(rsc);
+
+    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, type);
+    Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
+    texAlloc->incRef();
+    if (texAlloc == NULL) {
+        LOGE("Memory allocation failure");
+        fclose(f);
+        return NULL;
+    }
+
+    // offset to letterbox if height is not pow2
+    Adapter2D adapt(texAlloc);
+    uint8_t * fileInBuf = new uint8_t[texWidth * 3];
+    uint32_t yOffset = (hdr.width - hdr.height) / 2;
+
+    if (use32bpp) {
+        uint8_t *tmp = static_cast<uint8_t *>(adapt.getElement(0, yOffset));
+        for (int y=0; y < hdr.height; y++) {
+            fseek(f, hdr.offset + (y*hdr.width*3), SEEK_SET);
+            fread(fileInBuf, 1, hdr.width * 3, f);
+            for(int x=0; x < hdr.width; x++) {
+                tmp[0] = fileInBuf[x*3 + 2];
+                tmp[1] = fileInBuf[x*3 + 1];
+                tmp[2] = fileInBuf[x*3];
+                tmp[3] = 0xff;
+                tmp += 4;
+            }
+        }
+    } else {
+        uint16_t *tmp = static_cast<uint16_t *>(adapt.getElement(0, yOffset));
+        for (int y=0; y < hdr.height; y++) {
+            fseek(f, hdr.offset + (y*hdr.width*3), SEEK_SET);
+            fread(fileInBuf, 1, hdr.width * 3, f);
+            for(int x=0; x < hdr.width; x++) {
+                *tmp = rs888to565(fileInBuf[x*3 + 2], fileInBuf[x*3 + 1], fileInBuf[x*3]);
+                tmp++;
+            }
+        }
+    }
+
+    fclose(f);
+    delete [] fileInBuf;
+
+    if (genMips) {
+        Adapter2D adapt2(texAlloc);
+        for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
+            adapt.setLOD(lod);
+            adapt2.setLOD(lod + 1);
+            if (use32bpp) {
+                mip8888(adapt2, adapt);
+            } else {
+                mip565(adapt2, adapt);
+            }
+        }
+    }
+
+    return texAlloc;
+}
+
+
+void rsi_AllocationData(Context *rsc, RsAllocation va, const void *data)
+{
+    Allocation *a = static_cast<Allocation *>(va);
+    a->data(data);
+}
+
+void rsi_Allocation1DSubData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t count, const void *data)
+{
+    Allocation *a = static_cast<Allocation *>(va);
+    a->subData(xoff, count, data);
+}
+
+void rsi_Allocation2DSubData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data)
+{
+    Allocation *a = static_cast<Allocation *>(va);
+    a->subData(xoff, yoff, w, h, data);
+}
+
+
+}
+}
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
new file mode 100644
index 0000000..d0b91fd
--- /dev/null
+++ b/libs/rs/rsAllocation.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STRUCTURED_ALLOCATION_H
+#define ANDROID_STRUCTURED_ALLOCATION_H
+
+#include "rsType.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+    
+
+class Allocation : public ObjectBase
+{
+    // The graphics equilivent of malloc.  The allocation contains a structure of elements.
+
+
+public:
+    // By policy this allocation will hold a pointer to the type
+    // but will not destroy it on destruction.
+    Allocation(const Type *);
+    virtual ~Allocation();
+
+    void setCpuWritable(bool);
+    void setGpuWritable(bool);
+    void setCpuReadable(bool);
+    void setGpuReadable(bool);
+
+    bool fixAllocation();
+
+    void * getPtr() const {return mPtr;}
+    const Type * getType() const {return mType.get();}
+
+    void uploadToTexture(uint32_t lodOffset = 0);
+    uint32_t getTextureID() const {return mTextureID;}
+
+    void uploadToBufferObject();
+    uint32_t getBufferObjectID() const {return mBufferID;}
+
+
+    void data(const void *data);
+    void subData(uint32_t xoff, uint32_t count, const void *data);
+    void subData(uint32_t xoff, uint32_t yoff, 
+                 uint32_t w, uint32_t h, const void *data);
+    void subData(uint32_t xoff, uint32_t yoff, uint32_t zoff,
+                 uint32_t w, uint32_t h, uint32_t d, const void *data);
+
+protected:
+    ObjectBaseRef<const Type> mType;
+    void * mPtr;
+
+    // Usage restrictions
+    bool mCpuWrite;
+    bool mCpuRead;
+    bool mGpuWrite;
+    bool mGpuRead;
+
+    // more usage hint data from the application
+    // which can be used by a driver to pick the best memory type.
+    // Likely ignored for now
+    float mReadWriteRatio;
+    float mUpdateSize;
+
+
+    // Is this a legal structure to be used as a texture source.
+    // Initially this will require 1D or 2D and color data
+    bool mIsTexture;
+    uint32_t mTextureID;
+
+    // Is this a legal structure to be used as a vertex source.
+    // Initially this will require 1D and x(yzw).  Additional per element data
+    // is allowed.
+    bool mIsVertexBuffer;
+    uint32_t mBufferID;
+
+};
+
+}
+}
+#endif
+
diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp
new file mode 100644
index 0000000..a931811
--- /dev/null
+++ b/libs/rs/rsComponent.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsComponent.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Component::Component()
+{
+    mType = FLOAT;
+    mKind = NONE;
+    mIsNormalized = false;
+    mBits = 0;
+}
+
+Component::Component(
+    DataKind dk, DataType dt, 
+    bool isNormalized, uint32_t bits)
+{
+    mType = dt;
+    mKind = dk;
+    mIsNormalized = isNormalized;
+    mBits = bits;
+}
+
+Component::~Component()
+{
+}
diff --git a/libs/rs/rsComponent.h b/libs/rs/rsComponent.h
new file mode 100644
index 0000000..e1b0585
--- /dev/null
+++ b/libs/rs/rsComponent.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_STRUCTURED_COMPONENT_H
+#define ANDROID_RS_STRUCTURED_COMPONENT_H
+
+#include "rsUtils.h"
+#include "rsObjectBase.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class Component : public ObjectBase
+{
+public:
+    enum DataType {
+        FLOAT,
+        UNSIGNED,
+        SIGNED
+    };
+
+    enum DataKind {
+        NONE,
+        RED, GREEN, BLUE, ALPHA, LUMINANCE, INTENSITY,
+        X, Y, Z, W,
+        S, T, Q, R,
+        NX, NY, NZ,
+        INDEX,
+        USER
+    };
+
+
+    Component(DataKind dk, DataType dt, bool isNormalized, uint32_t bits);
+    virtual ~Component();
+
+    DataType getType() const {return mType;}
+    bool getIsNormalized() const {return mIsNormalized;}
+    DataKind getKind() const {return mKind;}
+    uint32_t getBits() const {return mBits;}
+
+protected:
+
+    DataType mType;
+    bool mIsNormalized;
+    DataKind mKind;
+    uint32_t mBits;
+
+private:
+    Component();
+};
+
+
+}
+}
+
+#endif //ANDROID_RS_STRUCTURED_COMPONENT_H
+
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
new file mode 100644
index 0000000..c731db0
--- /dev/null
+++ b/libs/rs/rsContext.cpp
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsDevice.h"
+#include "rsContext.h"
+#include "rsThreadIO.h"
+#include "utils/String8.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+Context * Context::gCon = NULL;
+pthread_key_t Context::gThreadTLSKey = 0;
+
+void Context::initEGL()
+{
+    mNumConfigs = -1;
+
+    EGLint s_configAttribs[] = {
+         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+         EGL_RED_SIZE,       5,
+         EGL_GREEN_SIZE,     6,
+         EGL_BLUE_SIZE,      5,
+         EGL_DEPTH_SIZE,     16,
+         EGL_NONE
+     };
+
+     mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+     eglInitialize(mDisplay, &mMajorVersion, &mMinorVersion);
+     eglChooseConfig(mDisplay, s_configAttribs, &mConfig, 1, &mNumConfigs);
+
+     if (mWndSurface) {
+         mSurface = eglCreateWindowSurface(mDisplay, mConfig,
+                 new EGLNativeWindowSurface(mWndSurface),
+                 NULL);
+     } else {
+         mSurface = eglCreateWindowSurface(mDisplay, mConfig,
+                 android_createDisplaySurface(),
+                 NULL);
+     }
+
+     mContext = eglCreateContext(mDisplay, mConfig, NULL, NULL);
+     eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);
+     eglQuerySurface(mDisplay, mSurface, EGL_WIDTH, &mWidth);
+     eglQuerySurface(mDisplay, mSurface, EGL_HEIGHT, &mHeight);
+}
+
+bool Context::runScript(Script *s, uint32_t launchID)
+{
+    ObjectBaseRef<ProgramFragment> frag(mFragment);
+    ObjectBaseRef<ProgramVertex> vtx(mVertex);
+    ObjectBaseRef<ProgramFragmentStore> store(mFragmentStore);
+
+    bool ret = s->run(this, launchID);
+
+    mFragment.set(frag);
+    mVertex.set(vtx);
+    mFragmentStore.set(store);
+    return true;
+
+}
+
+
+bool Context::runRootScript()
+{
+    rsAssert(mRootScript->mEnviroment.mIsRoot);
+
+    glColor4f(1,1,1,1);
+    glEnable(GL_LIGHT0);
+    glViewport(0, 0, mWidth, mHeight);
+
+    glDepthMask(GL_TRUE);
+    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+    glClearColor(mRootScript->mEnviroment.mClearColor[0],
+                 mRootScript->mEnviroment.mClearColor[1],
+                 mRootScript->mEnviroment.mClearColor[2],
+                 mRootScript->mEnviroment.mClearColor[3]);
+    glClearDepthf(mRootScript->mEnviroment.mClearDepth);
+    glClear(GL_COLOR_BUFFER_BIT);
+    glClear(GL_DEPTH_BUFFER_BIT);
+
+    return runScript(mRootScript.get(), 0);
+}
+
+void Context::setupCheck()
+{
+    if (mFragmentStore.get()) {
+        mFragmentStore->setupGL();
+    }
+    if (mFragment.get()) {
+        mFragment->setupGL();
+    }
+    if (mVertex.get()) {
+        mVertex->setupGL();
+    }
+
+}
+
+
+void * Context::threadProc(void *vrsc)
+{
+     Context *rsc = static_cast<Context *>(vrsc);
+
+     gIO = new ThreadIO();
+     rsc->initEGL();
+
+     ScriptTLSStruct *tlsStruct = new ScriptTLSStruct;
+     if (!tlsStruct) {
+         LOGE("Error allocating tls storage");
+         return NULL;
+     }
+     tlsStruct->mContext = rsc;
+     tlsStruct->mScript = NULL;
+     int status = pthread_setspecific(rsc->gThreadTLSKey, tlsStruct);
+     if (status) {
+         LOGE("pthread_setspecific %i", status);
+     }
+
+     rsc->mStateVertex.init(rsc, rsc->mWidth, rsc->mHeight);
+     rsc->setVertex(NULL);
+     rsc->mStateFragment.init(rsc, rsc->mWidth, rsc->mHeight);
+     rsc->setFragment(NULL);
+     rsc->mStateFragmentStore.init(rsc, rsc->mWidth, rsc->mHeight);
+     rsc->setFragmentStore(NULL);
+
+     rsc->mRunning = true;
+     bool mDraw = true;
+     while (!rsc->mExit) {
+         mDraw |= gIO->playCoreCommands(rsc, !mDraw);
+         mDraw &= (rsc->mRootScript.get() != NULL);
+
+         if (mDraw) {
+             mDraw = rsc->runRootScript();
+             eglSwapBuffers(rsc->mDisplay, rsc->mSurface);
+         }
+     }
+
+     glClearColor(0,0,0,0);
+     glClear(GL_COLOR_BUFFER_BIT);
+     eglSwapBuffers(rsc->mDisplay, rsc->mSurface);
+     eglTerminate(rsc->mDisplay);
+     return NULL;
+}
+
+Context::Context(Device *dev, Surface *sur)
+{
+    dev->addContext(this);
+    mDev = dev;
+    mRunning = false;
+    mExit = false;
+
+    // see comment in header
+    gCon = this;
+
+    int status;
+    pthread_attr_t threadAttr;
+
+    status = pthread_key_create(&gThreadTLSKey, NULL);
+    if (status) {
+        LOGE("Failed to init thread tls key.");
+        return;
+    }
+
+    status = pthread_attr_init(&threadAttr);
+    if (status) {
+        LOGE("Failed to init thread attribute.");
+        return;
+    }
+
+    sched_param sparam;
+    sparam.sched_priority = ANDROID_PRIORITY_DISPLAY;
+    pthread_attr_setschedparam(&threadAttr, &sparam);
+
+    mWndSurface = sur;
+
+    LOGV("RS Launching thread");
+    status = pthread_create(&mThreadId, &threadAttr, threadProc, this);
+    if (status) {
+        LOGE("Failed to start rs context thread.");
+    }
+
+    while(!mRunning) {
+        sleep(1);
+    }
+
+    pthread_attr_destroy(&threadAttr);
+}
+
+Context::~Context()
+{
+    mExit = true;
+    void *res;
+
+    int status = pthread_join(mThreadId, &res);
+
+    if (mDev) {
+        mDev->removeContext(this);
+        pthread_key_delete(gThreadTLSKey);
+    }
+}
+
+void Context::swapBuffers()
+{
+    eglSwapBuffers(mDisplay, mSurface);
+}
+
+void rsContextSwap(RsContext vrsc)
+{
+    Context *rsc = static_cast<Context *>(vrsc);
+    rsc->swapBuffers();
+}
+
+void Context::setRootScript(Script *s)
+{
+    mRootScript.set(s);
+}
+
+void Context::setFragmentStore(ProgramFragmentStore *pfs)
+{
+    if (pfs == NULL) {
+        mFragmentStore.set(mStateFragmentStore.mDefault);
+    } else {
+        mFragmentStore.set(pfs);
+    }
+    mFragmentStore->setupGL();
+}
+
+void Context::setFragment(ProgramFragment *pf)
+{
+    if (pf == NULL) {
+        mFragment.set(mStateFragment.mDefault);
+    } else {
+        mFragment.set(pf);
+    }
+    mFragment->setupGL();
+}
+
+void Context::setVertex(ProgramVertex *pv)
+{
+    if (pv == NULL) {
+        mVertex.set(mStateVertex.mDefault);
+    } else {
+        mVertex.set(pv);
+    }
+    mVertex->setupGL();
+}
+
+void Context::assignName(ObjectBase *obj, const char *name, uint32_t len)
+{
+    rsAssert(!obj->getName());
+    obj->setName(name, len);
+    mNames.add(obj);
+}
+
+void Context::removeName(ObjectBase *obj)
+{
+    for(size_t ct=0; ct < mNames.size(); ct++) {
+        if (obj == mNames[ct]) {
+            mNames.removeAt(ct);
+            return;
+        }
+    }
+}
+
+ObjectBase * Context::lookupName(const char *name) const
+{
+    for(size_t ct=0; ct < mNames.size(); ct++) {
+        if (!strcmp(name, mNames[ct]->getName())) {
+            return mNames[ct];
+        }
+    }
+    return NULL;
+}
+
+void Context::appendNameDefines(String8 *str) const
+{
+    char buf[256];
+    for (size_t ct=0; ct < mNames.size(); ct++) {
+        str->append("#define NAMED_");
+        str->append(mNames[ct]->getName());
+        str->append(" ");
+        sprintf(buf, "%i\n", (int)mNames[ct]);
+        str->append(buf);
+    }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+//
+
+namespace android {
+namespace renderscript {
+
+
+void rsi_ContextBindRootScript(Context *rsc, RsScript vs)
+{
+    Script *s = static_cast<Script *>(vs);
+    rsc->setRootScript(s);
+}
+
+void rsi_ContextBindSampler(Context *rsc, uint32_t slot, RsSampler vs)
+{
+    Sampler *s = static_cast<Sampler *>(vs);
+
+    if (slot > RS_MAX_SAMPLER_SLOT) {
+        LOGE("Invalid sampler slot");
+        return;
+    }
+
+    s->bindToContext(&rsc->mStateSampler, slot);
+}
+
+void rsi_ContextBindProgramFragmentStore(Context *rsc, RsProgramFragmentStore vpfs)
+{
+    ProgramFragmentStore *pfs = static_cast<ProgramFragmentStore *>(vpfs);
+    rsc->setFragmentStore(pfs);
+}
+
+void rsi_ContextBindProgramFragment(Context *rsc, RsProgramFragment vpf)
+{
+    ProgramFragment *pf = static_cast<ProgramFragment *>(vpf);
+    rsc->setFragment(pf);
+}
+
+void rsi_ContextBindProgramVertex(Context *rsc, RsProgramVertex vpv)
+{
+    ProgramVertex *pv = static_cast<ProgramVertex *>(vpv);
+    rsc->setVertex(pv);
+}
+
+void rsi_AssignName(Context *rsc, void * obj, const char *name, uint32_t len)
+{
+    ObjectBase *ob = static_cast<ObjectBase *>(obj);
+    rsc->assignName(ob, name, len);
+}
+
+
+}
+}
+
+
+RsContext rsContextCreate(RsDevice vdev, void *sur, uint32_t version)
+{
+    Device * dev = static_cast<Device *>(vdev);
+    Context *rsc = new Context(dev, (Surface *)sur);
+    return rsc;
+}
+
+void rsContextDestroy(RsContext vrsc)
+{
+    Context * rsc = static_cast<Context *>(vrsc);
+    delete rsc;
+}
+
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
new file mode 100644
index 0000000..f555090
--- /dev/null
+++ b/libs/rs/rsContext.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_CONTEXT_H
+#define ANDROID_RS_CONTEXT_H
+
+#include "rsType.h"
+#include "rsMatrix.h"
+#include "rsAllocation.h"
+#include "rsTriangleMesh.h"
+#include "rsMesh.h"
+#include "rsDevice.h"
+#include "rsScriptC.h"
+#include "rsAllocation.h"
+#include "rsAdapter.h"
+#include "rsSampler.h"
+#include "rsProgramFragment.h"
+#include "rsProgramFragmentStore.h"
+#include "rsProgramVertex.h"
+#include "rsLight.h"
+
+#include "rsgApiStructs.h"
+#include "rsLocklessFifo.h"
+
+#include <ui/EGLNativeWindowSurface.h>
+#include <ui/Surface.h>
+
+
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class Context 
+{
+public:
+    Context(Device *, Surface *);
+    ~Context();
+
+    static pthread_key_t gThreadTLSKey;
+    struct ScriptTLSStruct {
+        Context * mContext;
+        Script * mScript;
+    };
+
+
+    //StructuredAllocationContext mStateAllocation;
+    ElementState mStateElement;
+    TypeState mStateType;
+    SamplerState mStateSampler;
+    ProgramFragmentState mStateFragment;
+    ProgramFragmentStoreState mStateFragmentStore;
+    ProgramVertexState mStateVertex;
+    LightState mStateLight;
+
+    TriangleMeshContext mStateTriangleMesh;
+
+    ScriptCState mScriptC;
+
+    static Context * getContext() {return gCon;}
+
+    void swapBuffers();
+    void setRootScript(Script *);
+    void setVertex(ProgramVertex *);
+    void setFragment(ProgramFragment *);
+    void setFragmentStore(ProgramFragmentStore *);
+
+    void updateSurface(void *sur);
+
+    const ProgramFragment * getFragment() {return mFragment.get();}
+    const ProgramFragmentStore * getFragmentStore() {return mFragmentStore.get();}
+
+    void setupCheck();
+
+    void assignName(ObjectBase *obj, const char *name, uint32_t len);
+    void removeName(ObjectBase *obj);
+    ObjectBase * lookupName(const char *name) const;
+    void appendNameDefines(String8 *str) const;
+
+
+    ProgramFragment * getDefaultProgramFragment() const {
+        return mStateFragment.mDefault.get();
+    }
+    ProgramVertex * getDefaultProgramVertex() const {
+        return mStateVertex.mDefault.get();
+    }
+    ProgramFragmentStore * getDefaultProgramFragmentStore() const {
+        return mStateFragmentStore.mDefault.get();
+    }
+
+protected:
+    Device *mDev;
+
+    EGLint mNumConfigs;
+    EGLint mMajorVersion;
+    EGLint mMinorVersion;
+    EGLConfig mConfig;
+    EGLContext mContext;
+    EGLSurface mSurface;
+    EGLint mWidth;
+    EGLint mHeight;
+    EGLDisplay mDisplay;
+
+    bool mRunning;
+    bool mExit;
+
+    pthread_t mThreadId;
+
+    ObjectBaseRef<Script> mRootScript;
+    ObjectBaseRef<ProgramFragment> mFragment;
+    ObjectBaseRef<ProgramVertex> mVertex;
+    ObjectBaseRef<ProgramFragmentStore> mFragmentStore;
+
+private:
+    Context();
+
+    void initEGL();
+
+    bool runScript(Script *s, uint32_t launchID);
+    bool runRootScript();
+
+    static void * threadProc(void *);
+
+    // todo: put in TLS
+    static Context *gCon;
+    Surface *mWndSurface;
+
+    Vector<ObjectBase *> mNames;
+};
+
+
+}
+}
+#endif
diff --git a/libs/rs/rsDevice.cpp b/libs/rs/rsDevice.cpp
new file mode 100644
index 0000000..1b3c41b
--- /dev/null
+++ b/libs/rs/rsDevice.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsDevice.h"
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+Device::Device()
+{
+
+}
+
+Device::~Device()
+{
+
+}
+
+void Device::addContext(Context *rsc)
+{
+    mContexts.add(rsc);
+}
+
+void Device::removeContext(Context *rsc)
+{
+    for (size_t idx=0; idx < mContexts.size(); idx++) {
+        if (mContexts[idx] == rsc) {
+            mContexts.removeAt(idx);
+            break;
+        }
+    }
+}
+
+
+
+RsDevice rsDeviceCreate()
+{
+    Device * d = new Device();
+    return d;
+}
+
+void rsDeviceDestroy(RsDevice dev)
+{
+    Device * d = static_cast<Device *>(dev);
+    delete d;
+
+}
+
diff --git a/libs/utils/IInterface.cpp b/libs/rs/rsDevice.h
similarity index 63%
copy from libs/utils/IInterface.cpp
copy to libs/rs/rsDevice.h
index 6ea8178..156315f 100644
--- a/libs/utils/IInterface.cpp
+++ b/libs/rs/rsDevice.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,22 +14,35 @@
  * limitations under the License.
  */
 
-#include <utils/IInterface.h>
+#ifndef ANDROID_RS_DEVICE_H
+#define ANDROID_RS_DEVICE_H
 
+#include "rsUtils.h"
+
+// ---------------------------------------------------------------------------
 namespace android {
+namespace renderscript {
 
-// ---------------------------------------------------------------------------
+class Context;
 
-sp<IBinder> IInterface::asBinder()
-{
-    return this ? onAsBinder() : NULL;
+class Device {
+public:
+    Device();
+    ~Device();
+
+    void addContext(Context *);
+    void removeContext(Context *);
+
+protected:
+    Vector<Context *> mContexts;
+
+
+};
+
+
+
+
+
 }
-
-sp<const IBinder> IInterface::asBinder() const
-{
-    return this ? const_cast<IInterface*>(this)->onAsBinder() : NULL;
 }
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
+#endif
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
new file mode 100644
index 0000000..069a128
--- /dev/null
+++ b/libs/rs/rsElement.cpp
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+#include <GLES/gl.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+void ElementState::initPredefined()
+{
+    Component * u_8  = new Component(Component::USER,   Component::UNSIGNED,  true,  8);
+    Component * i_8  = new Component(Component::USER,   Component::SIGNED,    true,  8);
+    Component * u_16 = new Component(Component::USER,   Component::UNSIGNED,  true,  16);
+    Component * i_16 = new Component(Component::USER,   Component::SIGNED,    true,  16);
+    Component * u_32 = new Component(Component::USER,   Component::UNSIGNED,  true,  32);
+    Component * i_32 = new Component(Component::USER,   Component::SIGNED,    true,  32);
+    Component * f_32 = new Component(Component::USER,   Component::FLOAT,     true,  32);
+
+
+    Component * r_4  = new Component(Component::RED,    Component::UNSIGNED,  true,  4);
+    Component * r_5  = new Component(Component::RED,    Component::UNSIGNED,  true,  5);
+    Component * r_8  = new Component(Component::RED,    Component::UNSIGNED,  true,  8);
+
+    Component * g_4  = new Component(Component::GREEN,  Component::UNSIGNED,  true,  4);
+    Component * g_5  = new Component(Component::GREEN,  Component::UNSIGNED,  true,  5);
+    Component * g_6  = new Component(Component::GREEN,  Component::UNSIGNED,  true,  6);
+    Component * g_8  = new Component(Component::GREEN,  Component::UNSIGNED,  true,  8);
+
+    Component * b_4  = new Component(Component::BLUE,   Component::UNSIGNED,  true,  4);
+    Component * b_5  = new Component(Component::BLUE,   Component::UNSIGNED,  true,  5);
+    Component * b_8  = new Component(Component::BLUE,   Component::UNSIGNED,  true,  8);
+
+    Component * a_1  = new Component(Component::ALPHA,  Component::UNSIGNED,  true,  1);
+    Component * a_4  = new Component(Component::ALPHA,  Component::UNSIGNED,  true,  4);
+    Component * a_8  = new Component(Component::ALPHA,  Component::UNSIGNED,  true,  8);
+
+    Component * idx_16 = new Component(Component::INDEX,  Component::UNSIGNED,  false, 16);
+    Component * idx_32 = new Component(Component::INDEX,  Component::UNSIGNED,  false, 32);
+
+    Component * x    = new Component(Component::X,      Component::FLOAT,     false, 32);
+    Component * y    = new Component(Component::Y,      Component::FLOAT,     false, 32);
+    Component * z    = new Component(Component::Z,      Component::FLOAT,     false, 32);
+
+    Component * nx   = new Component(Component::NX,     Component::FLOAT,     false, 32);
+    Component * ny   = new Component(Component::NY,     Component::FLOAT,     false, 32);
+    Component * nz   = new Component(Component::NZ,     Component::FLOAT,     false, 32);
+
+    Component * s    = new Component(Component::S,      Component::FLOAT,     false, 32);
+    Component * t    = new Component(Component::T,      Component::FLOAT,     false, 32);
+
+    Element * e;
+
+    e = new Element(1);
+    e->setComponent(0, u_8);
+    mPredefinedList.add(Predefined(RS_ELEMENT_USER_U8, e));
+
+    e = new Element(1);
+    e->setComponent(0, i_8);
+    mPredefinedList.add(Predefined(RS_ELEMENT_USER_I8, e));
+
+    e = new Element(1);
+    e->setComponent(0, u_16);
+    mPredefinedList.add(Predefined(RS_ELEMENT_USER_U16, e));
+
+    e = new Element(1);
+    e->setComponent(0, i_16);
+    mPredefinedList.add(Predefined(RS_ELEMENT_USER_I16, e));
+
+    e = new Element(1);
+    e->setComponent(0, u_32);
+    mPredefinedList.add(Predefined(RS_ELEMENT_USER_U32, e));
+
+    e = new Element(1);
+    e->setComponent(0, i_32);
+    mPredefinedList.add(Predefined(RS_ELEMENT_USER_I32, e));
+
+    e = new Element(1);
+    e->setComponent(0, f_32);
+    mPredefinedList.add(Predefined(RS_ELEMENT_USER_FLOAT, e));
+
+    e = new Element(1);
+    e->setComponent(0, a_8);
+    mPredefinedList.add(Predefined(RS_ELEMENT_A_8, e));
+
+    e = new Element(3);
+    e->setComponent(0, r_5);
+    e->setComponent(1, g_6);
+    e->setComponent(2, b_5);
+    mPredefinedList.add(Predefined(RS_ELEMENT_RGB_565, e));
+
+    e = new Element(4);
+    e->setComponent(0, r_5);
+    e->setComponent(1, g_5);
+    e->setComponent(2, b_5);
+    e->setComponent(3, a_1);
+    mPredefinedList.add(Predefined(RS_ELEMENT_RGBA_5551, e));
+
+    e = new Element(4);
+    e->setComponent(0, r_4);
+    e->setComponent(1, g_4);
+    e->setComponent(2, b_4);
+    e->setComponent(3, a_4);
+    mPredefinedList.add(Predefined(RS_ELEMENT_RGBA_4444, e));
+
+    e = new Element(3);
+    e->setComponent(0, r_8);
+    e->setComponent(1, g_8);
+    e->setComponent(2, b_8);
+    mPredefinedList.add(Predefined(RS_ELEMENT_RGB_888, e));
+
+    e = new Element(4);
+    e->setComponent(0, r_8);
+    e->setComponent(1, g_8);
+    e->setComponent(2, b_8);
+    e->setComponent(3, a_8);
+    mPredefinedList.add(Predefined(RS_ELEMENT_RGBA_8888, e));
+
+    e = new Element(1);
+    e->setComponent(0, idx_16);
+    mPredefinedList.add(Predefined(RS_ELEMENT_INDEX_16, e));
+
+    e = new Element(1);
+    e->setComponent(0, idx_32);
+    mPredefinedList.add(Predefined(RS_ELEMENT_INDEX_32, e));
+
+    e = new Element(2);
+    e->setComponent(0, x);
+    e->setComponent(1, y);
+    mPredefinedList.add(Predefined(RS_ELEMENT_XY_F32, e));
+
+    e = new Element(3);
+    e->setComponent(0, x);
+    e->setComponent(1, y);
+    e->setComponent(2, z);
+    mPredefinedList.add(Predefined(RS_ELEMENT_XYZ_F32, e));
+
+    e = new Element(4);
+    e->setComponent(0, s);
+    e->setComponent(1, t);
+    e->setComponent(2, x);
+    e->setComponent(3, y);
+    mPredefinedList.add(Predefined(RS_ELEMENT_ST_XY_F32, e));
+
+    e = new Element(5);
+    e->setComponent(0, s);
+    e->setComponent(1, t);
+    e->setComponent(2, x);
+    e->setComponent(3, y);
+    e->setComponent(4, z);
+    mPredefinedList.add(Predefined(RS_ELEMENT_ST_XYZ_F32, e));
+
+    e = new Element(6);
+    e->setComponent(0, nx);
+    e->setComponent(1, ny);
+    e->setComponent(2, nz);
+    e->setComponent(3, x);
+    e->setComponent(4, y);
+    e->setComponent(5, z);
+    mPredefinedList.add(Predefined(RS_ELEMENT_NORM_XYZ_F32, e));
+
+    e = new Element(8);
+    e->setComponent(0, nx);
+    e->setComponent(1, ny);
+    e->setComponent(2, nz);
+    e->setComponent(3, s);
+    e->setComponent(4, t);
+    e->setComponent(5, x);
+    e->setComponent(6, y);
+    e->setComponent(7, z);
+    mPredefinedList.add(Predefined(RS_ELEMENT_NORM_ST_XYZ_F32, e));
+}
+
+
+Element::Element()
+{
+    mComponents = NULL;
+    mComponentCount = 0;
+}
+
+Element::Element(uint32_t count)
+{
+    mComponents = new ObjectBaseRef<Component> [count];
+    mComponentCount = count;
+}
+
+Element::~Element()
+{
+    clear();
+}
+
+void Element::clear()
+{
+    delete [] mComponents;
+    mComponents = NULL;
+    mComponentCount = 0;
+}
+
+void Element::setComponent(uint32_t idx, Component *c)
+{
+    rsAssert(!mComponents[idx].get());
+    rsAssert(idx < mComponentCount);
+    mComponents[idx].set(c);
+    c->incRef();
+}
+
+
+size_t Element::getSizeBits() const
+{
+    size_t total = 0;
+    for (size_t ct=0; ct < mComponentCount; ct++) {
+        total += mComponents[ct]->getBits();
+    }
+    return total;
+}
+
+size_t Element::getComponentOffsetBits(uint32_t componentNumber) const
+{
+    size_t offset = 0;
+    for (uint32_t ct = 0; ct < componentNumber; ct++) {
+        offset += mComponents[ct]->getBits();
+    }
+    return offset;
+}
+
+uint32_t Element::getGLType() const
+{
+    int bits[4];
+
+    if (mComponentCount > 4) {
+        return 0;
+    }
+
+    for (uint32_t ct=0; ct < mComponentCount; ct++) {
+        bits[ct] = mComponents[ct]->getBits();
+        if (mComponents[ct]->getType() != Component::UNSIGNED) {
+            return 0;
+        }
+        if (!mComponents[ct]->getIsNormalized()) {
+            return 0;
+        }
+    }
+
+    switch(mComponentCount) {
+    case 1:
+        if (bits[0] == 8) {
+            return GL_UNSIGNED_BYTE;
+        }
+        return 0;
+    case 2:
+        if ((bits[0] == 8) &&
+            (bits[1] == 8)) {
+            return GL_UNSIGNED_BYTE;
+        }
+        return 0;
+    case 3:
+        if ((bits[0] == 8) &&
+            (bits[1] == 8) &&
+            (bits[2] == 8)) {
+            return GL_UNSIGNED_BYTE;
+        }
+        if ((bits[0] == 5) &&
+            (bits[1] == 6) &&
+            (bits[2] == 5)) {
+            return GL_UNSIGNED_SHORT_5_6_5;
+        }
+        return 0;
+    case 4:
+        if ((bits[0] == 8) &&
+            (bits[1] == 8) &&
+            (bits[2] == 8) &&
+            (bits[3] == 8)) {
+            return GL_UNSIGNED_BYTE;
+        }
+        if ((bits[0] == 4) &&
+            (bits[1] == 4) &&
+            (bits[2] == 4) &&
+            (bits[3] == 4)) {
+            return GL_UNSIGNED_SHORT_4_4_4_4;
+        }
+        if ((bits[0] == 5) &&
+            (bits[1] == 5) &&
+            (bits[2] == 5) &&
+            (bits[3] == 1)) {
+            return GL_UNSIGNED_SHORT_5_5_5_1;
+        }
+    }
+    return 0;
+}
+
+uint32_t Element::getGLFormat() const
+{
+    switch(mComponentCount) {
+    case 1:
+        if (mComponents[0]->getKind() == Component::ALPHA) {
+            return GL_ALPHA;
+        }
+        if (mComponents[0]->getKind() == Component::LUMINANCE) {
+            return GL_LUMINANCE;
+        }
+        break;
+    case 2:
+        if ((mComponents[0]->getKind() == Component::LUMINANCE) &&
+            (mComponents[1]->getKind() == Component::ALPHA)) {
+            return GL_LUMINANCE_ALPHA;
+        }
+        break;
+    case 3:
+        if ((mComponents[0]->getKind() == Component::RED) &&
+            (mComponents[1]->getKind() == Component::GREEN) &&
+            (mComponents[2]->getKind() == Component::BLUE)) {
+            return GL_RGB;
+        }
+        break;
+    case 4:
+        if ((mComponents[0]->getKind() == Component::RED) &&
+            (mComponents[1]->getKind() == Component::GREEN) &&
+            (mComponents[2]->getKind() == Component::BLUE) &&
+            (mComponents[3]->getKind() == Component::ALPHA)) {
+            return GL_RGBA;
+        }
+        break;
+    }
+    return 0;
+}
+
+
+ElementState::ElementState()
+{
+}
+
+ElementState::~ElementState()
+{
+}
+
+/////////////////////////////////////////
+// 
+
+namespace android {
+namespace renderscript {
+
+void rsi_ElementBegin(Context *rsc)
+{
+    rsc->mStateElement.mComponentBuildList.clear();
+}
+
+void rsi_ElementAddPredefined(Context *rsc, RsElementPredefined predef)
+{
+    ElementState * sec = &rsc->mStateElement;
+
+    RsElement ve = rsi_ElementGetPredefined(rsc, predef);
+    const Element *e = static_cast<const Element *>(ve);
+
+    for(size_t ct = 0; ct < sec->mPredefinedList[predef].mElement->getComponentCount(); ct++) {
+        sec->mComponentBuildList.add(sec->mPredefinedList[predef].mElement->getComponent(ct));
+    }
+}
+
+RsElement rsi_ElementGetPredefined(Context *rsc, RsElementPredefined predef)
+{
+    ElementState * sec = &rsc->mStateElement;
+
+    if (!sec->mPredefinedList.size()) {
+        sec->initPredefined();
+    }
+
+    if ((predef < 0) || 
+        (static_cast<uint32_t>(predef) >= sec->mPredefinedList.size())) {
+        LOGE("rsElementGetPredefined: Request for bad predefined type");
+        // error
+        return NULL;
+    }
+
+    rsAssert(sec->mPredefinedList[predef].mEnum == predef);
+    Element * e = sec->mPredefinedList[predef].mElement;
+    e->incRef();
+    return e;
+}
+
+void rsi_ElementAdd(Context *rsc, RsDataKind dk, RsDataType dt, bool isNormalized, size_t bits)
+{
+    ElementState * sec = &rsc->mStateElement;
+
+}
+
+RsElement rsi_ElementCreate(Context *rsc)
+{
+    ElementState * sec = &rsc->mStateElement;
+
+    Element *se = new Element(sec->mComponentBuildList.size());
+    sec->mAllElements.add(se);
+
+    for (size_t ct = 0; ct < se->getComponentCount(); ct++) {
+        se->setComponent(ct, sec->mComponentBuildList[ct]);
+    }
+
+    rsc->mStateElement.mComponentBuildList.clear();
+    se->incRef();
+    return se;
+}
+
+void rsi_ElementDestroy(Context *rsc, RsElement vse)
+{
+    ElementState * sec = &rsc->mStateElement;
+    Element * se = static_cast<Element *>(vse);
+
+    for (size_t ct = 0; ct < sec->mAllElements.size(); ct++) {
+        if (sec->mAllElements[ct] == se) {
+            sec->mAllElements.removeAt(ct);
+            break;
+        }
+    }
+    se->decRef();
+}
+
+
+}
+}
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
new file mode 100644
index 0000000..ea6fa8f
--- /dev/null
+++ b/libs/rs/rsElement.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STRUCTURED_ELEMENT_H
+#define ANDROID_STRUCTURED_ELEMENT_H
+
+#include "rsComponent.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+// An element is a group of Components that occupies one cell in a structure.
+class Element : public ObjectBase
+{
+public:
+    Element(uint32_t count);
+    ~Element();
+
+
+    void setComponent(uint32_t idx, Component *c);
+
+    uint32_t getGLType() const;
+    uint32_t getGLFormat() const;
+
+
+    size_t getSizeBits() const;
+    size_t getSizeBytes() const { 
+        return (getSizeBits() + 7) >> 3; 
+    }
+
+    size_t getComponentOffsetBits(uint32_t componentNumber) const;
+    size_t getComponentOffsetBytes(uint32_t componentNumber) const { 
+        return (getComponentOffsetBits(componentNumber) + 7) >> 3;
+    }
+
+    uint32_t getComponentCount() const {return mComponentCount;}
+    Component * getComponent(uint32_t idx) const {return mComponents[idx].get();}
+
+protected:
+    // deallocate any components that are part of this element.
+    void clear();
+
+    size_t mComponentCount;
+    ObjectBaseRef<Component> * mComponents;
+    //uint32_t *mOffsetTable;
+
+    Element();
+};
+
+
+class ElementState {
+public:
+    ElementState();
+    ~ElementState();
+
+    Vector<Element *> mAllElements;
+    Vector<Component *> mComponentBuildList;
+
+
+
+    struct Predefined {
+        Predefined() {
+            mElement = NULL;
+        }
+        Predefined(RsElementPredefined en, Element *e) {
+            mEnum = en; 
+            mElement = e;
+        }
+        RsElementPredefined mEnum;
+        Element * mElement;
+    };
+    Vector<Predefined> mPredefinedList;
+
+    void initPredefined();
+    
+};
+
+
+}
+}
+#endif //ANDROID_STRUCTURED_ELEMENT_H
diff --git a/libs/rs/rsLight.cpp b/libs/rs/rsLight.cpp
new file mode 100644
index 0000000..67d0095
--- /dev/null
+++ b/libs/rs/rsLight.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Light::Light(bool isLocal, bool isMono)
+{
+    mIsLocal = isLocal;
+    mIsMono = isMono;
+
+    mX = 0;
+    mY = 0;
+    mZ = 0;
+
+    mR = 1.f;
+    mG = 1.f;
+    mB = 1.f;
+}
+
+Light::~Light()
+{
+}
+
+void Light::setPosition(float x, float y, float z)
+{
+    mX = x;
+    mY = y;
+    mZ = z;
+}
+
+void Light::setColor(float r, float g, float b)
+{
+    mR = r;
+    mG = g;
+    mB = b;
+}
+
+////////////////////////////////////////////
+
+LightState::LightState()
+{
+    clear();
+}
+
+LightState::~LightState()
+{
+}
+
+void LightState::clear()
+{
+    mIsLocal = false;
+    mIsMono = false;
+}
+
+
+////////////////////////////////////////////////////
+// 
+
+namespace android {
+namespace renderscript {
+
+void rsi_LightBegin(Context *rsc)
+{
+    rsc->mStateLight.clear();
+}
+
+void rsi_LightSetLocal(Context *rsc, bool isLocal)
+{
+    rsc->mStateLight.mIsLocal = isLocal;
+}
+
+void rsi_LightSetMonochromatic(Context *rsc, bool isMono)
+{
+    rsc->mStateLight.mIsMono = isMono;
+}
+
+RsLight rsi_LightCreate(Context *rsc)
+{
+    Light *l = new Light(rsc->mStateLight.mIsLocal,
+                         rsc->mStateLight.mIsMono);
+    l->incRef();
+    return l;
+}
+
+void rsi_LightDestroy(Context *rsc, RsLight vl)
+{
+    Light *l = static_cast<Light *>(vl);
+    l->decRef();
+}
+
+void rsi_LightSetColor(Context *rsc, RsLight vl, float r, float g, float b)
+{
+    Light *l = static_cast<Light *>(vl);
+    l->setColor(r, g, b);
+}
+
+void rsi_LightSetPosition(Context *rsc, RsLight vl, float x, float y, float z)
+{
+    Light *l = static_cast<Light *>(vl);
+    l->setPosition(x, y, z);
+}
+
+
+
+}
+}
diff --git a/libs/rs/rsLight.h b/libs/rs/rsLight.h
new file mode 100644
index 0000000..76d1ecc
--- /dev/null
+++ b/libs/rs/rsLight.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LIGHT_H
+#define ANDROID_LIGHT_H
+
+
+#include "rsObjectBase.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+// An element is a group of Components that occupies one cell in a structure.
+class Light : public ObjectBase
+{
+public:
+    Light(bool isLocal, bool isMono);
+    virtual ~Light();
+
+    // Values, mutable after creation.
+    void setPosition(float x, float y, float z);
+    void setColor(float r, float g, float b);
+
+protected:
+    float mR, mG, mB;
+    float mX, mY, mZ;
+    bool mIsLocal;
+    bool mIsMono;
+};
+
+
+class LightState {
+public:
+    LightState();
+    ~LightState();
+
+    void clear();
+
+    bool mIsMono;
+    bool mIsLocal;
+};
+
+
+}
+}
+#endif //ANDROID_LIGHT_H
+
diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp
new file mode 100644
index 0000000..c3fee54
--- /dev/null
+++ b/libs/rs/rsLocklessFifo.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsLocklessFifo.h"
+
+using namespace android;
+
+
+LocklessCommandFifo::LocklessCommandFifo()
+{
+}
+
+LocklessCommandFifo::~LocklessCommandFifo()
+{
+}
+
+bool LocklessCommandFifo::init(uint32_t sizeInBytes)
+{
+    // Add room for a buffer reset command
+    mBuffer = static_cast<uint8_t *>(malloc(sizeInBytes + 4));
+    if (!mBuffer) {
+        LOGE("LocklessFifo allocation failure");
+        return false;
+    }
+
+    if (!mSignalToControl.init() || !mSignalToWorker.init()) {
+        LOGE("Signal setup failed");
+        free(mBuffer);
+        return false;
+    }
+
+    mSize = sizeInBytes;
+    mPut = mBuffer;
+    mGet = mBuffer;
+    mEnd = mBuffer + (sizeInBytes) - 1;
+    dumpState("init");
+    return true;
+}
+
+uint32_t LocklessCommandFifo::getFreeSpace() const 
+{
+    int32_t freeSpace = 0;
+    //dumpState("getFreeSpace");
+
+    if (mPut >= mGet) {
+        freeSpace = mEnd - mPut;
+    } else {
+        freeSpace = mGet - mPut;
+    }
+
+    if (freeSpace < 0) {
+        freeSpace = 0;
+    }
+    return freeSpace;
+}
+
+bool LocklessCommandFifo::isEmpty() const
+{
+    return mPut == mGet;
+}
+
+
+void * LocklessCommandFifo::reserve(uint32_t sizeInBytes)
+{
+    // Add space for command header and loop token;
+    sizeInBytes += 8;
+
+    //dumpState("reserve");
+    if (getFreeSpace() < sizeInBytes) {
+        makeSpace(sizeInBytes);
+    }
+
+    return mPut + 4;
+}
+
+void LocklessCommandFifo::commit(uint32_t command, uint32_t sizeInBytes)
+{
+    //dumpState("commit 1");
+    reinterpret_cast<uint16_t *>(mPut)[0] = command;
+    reinterpret_cast<uint16_t *>(mPut)[1] = sizeInBytes;
+    mPut += ((sizeInBytes + 3) & ~3) + 4;
+    //dumpState("commit 2");
+    mSignalToWorker.set();
+}
+
+void LocklessCommandFifo::commitSync(uint32_t command, uint32_t sizeInBytes)
+{
+    commit(command, sizeInBytes);
+    flush();
+}
+
+void LocklessCommandFifo::flush()
+{
+    //dumpState("flush 1");
+    while(mPut != mGet) {
+        mSignalToControl.wait();
+    }
+    //dumpState("flush 2");
+}
+
+const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData)
+{
+    while(1) {
+        //dumpState("get");
+        while(isEmpty()) {
+            mSignalToControl.set();
+            mSignalToWorker.wait();
+        }
+
+        *command = reinterpret_cast<const uint16_t *>(mGet)[0];
+        *bytesData = reinterpret_cast<const uint16_t *>(mGet)[1];
+        if (*command) {
+            // non-zero command is valid
+            return mGet+4;
+        }
+    
+        // zero command means reset to beginning.
+        mGet = mBuffer;
+    }
+}
+
+void LocklessCommandFifo::next()
+{
+    uint32_t bytes = reinterpret_cast<const uint16_t *>(mGet)[1];
+    mGet += ((bytes + 3) & ~3) + 4;
+    if (isEmpty()) {
+        mSignalToControl.set();
+    }
+    //dumpState("next");
+}
+
+void LocklessCommandFifo::makeSpace(uint32_t bytes)
+{
+    //dumpState("make space");
+    if ((mPut+bytes) > mEnd) {
+        // Need to loop regardless of where get is.
+        while((mGet > mPut) && (mBuffer+4 >= mGet)) {
+            sleep(1);
+        }
+
+        // Toss in a reset then the normal wait for space will do the rest.
+        reinterpret_cast<uint16_t *>(mPut)[0] = 0;
+        reinterpret_cast<uint16_t *>(mPut)[1] = 0;
+        mPut = mBuffer;
+    }
+
+    // it will fit here so we just need to wait for space.
+    while(getFreeSpace() < bytes) {
+        sleep(1);
+    }
+    
+}
+
+void LocklessCommandFifo::dumpState(const char *s) const
+{
+    LOGV("%s  put %p, get %p,  buf %p,  end %p", s, mPut, mGet, mBuffer, mEnd);
+}
+
+LocklessCommandFifo::Signal::Signal()
+{
+    mSet = true;
+}
+
+LocklessCommandFifo::Signal::~Signal()
+{
+    pthread_mutex_destroy(&mMutex);
+    pthread_cond_destroy(&mCondition);
+}
+
+bool LocklessCommandFifo::Signal::init()
+{
+    int status = pthread_mutex_init(&mMutex, NULL);
+    if (status) {
+        LOGE("LocklessFifo mutex init failure");
+        return false;
+    }
+
+    status = pthread_cond_init(&mCondition, NULL);
+    if (status) {
+        LOGE("LocklessFifo condition init failure");
+        pthread_mutex_destroy(&mMutex);
+        return false;
+    }
+
+    return true;
+}
+
+void LocklessCommandFifo::Signal::set()
+{
+    int status;
+
+    status = pthread_mutex_lock(&mMutex);
+    if (status) {
+        LOGE("LocklessCommandFifo: error %i locking for set condition.", status);
+        return;
+    }
+
+    mSet = true;
+
+    status = pthread_cond_signal(&mCondition);
+    if (status) {
+        LOGE("LocklessCommandFifo: error %i on set condition.", status);
+    }
+
+    status = pthread_mutex_unlock(&mMutex);
+    if (status) {
+        LOGE("LocklessCommandFifo: error %i unlocking for set condition.", status);
+    }
+}
+
+void LocklessCommandFifo::Signal::wait()
+{
+    int status;
+
+    status = pthread_mutex_lock(&mMutex);
+    if (status) {
+        LOGE("LocklessCommandFifo: error %i locking for condition.", status);
+        return;
+    }
+
+    if (!mSet) {
+        status = pthread_cond_wait(&mCondition, &mMutex);
+        if (status) {
+            LOGE("LocklessCommandFifo: error %i waiting on condition.", status);
+        }
+    }
+    mSet = false;
+
+    status = pthread_mutex_unlock(&mMutex);
+    if (status) {
+        LOGE("LocklessCommandFifo: error %i unlocking for condition.", status);
+    }
+}
+
diff --git a/libs/rs/rsLocklessFifo.h b/libs/rs/rsLocklessFifo.h
new file mode 100644
index 0000000..abeddf7
--- /dev/null
+++ b/libs/rs/rsLocklessFifo.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_LOCKLESS_FIFO_H
+#define ANDROID_RS_LOCKLESS_FIFO_H
+
+
+#include "rsUtils.h"
+
+namespace android {
+
+
+// A simple FIFO to be used as a producer / consumer between two
+// threads.  One is writer and one is reader.  The common cases
+// will not require locking.  It is not threadsafe for multiple 
+// readers or writers by design.
+
+class LocklessCommandFifo 
+{
+public:
+    bool init(uint32_t size);
+
+    LocklessCommandFifo();
+    ~LocklessCommandFifo();
+
+
+protected:
+    class Signal {
+    public:
+        Signal();
+        ~Signal();
+
+        bool init();
+
+        void set();
+        void wait();
+
+    protected:
+        bool mSet;
+        pthread_mutex_t mMutex;
+        pthread_cond_t mCondition;
+    };
+
+    uint8_t * volatile mPut;
+    uint8_t * volatile mGet;
+    uint8_t * mBuffer;
+    uint8_t * mEnd;
+    uint8_t mSize;
+
+    Signal mSignalToWorker;
+    Signal mSignalToControl;
+
+
+
+public:
+    void * reserve(uint32_t bytes);
+    void commit(uint32_t command, uint32_t bytes);
+    void commitSync(uint32_t command, uint32_t bytes);
+
+    void flush();
+    const void * get(uint32_t *command, uint32_t *bytesData);
+    void next();
+
+    void makeSpace(uint32_t bytes);
+
+    bool isEmpty() const;
+    uint32_t getFreeSpace() const;
+
+
+private:
+    void dumpState(const char *) const;
+};
+
+
+}
+#endif
diff --git a/libs/rs/rsMatrix.cpp b/libs/rs/rsMatrix.cpp
new file mode 100644
index 0000000..5f68197
--- /dev/null
+++ b/libs/rs/rsMatrix.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsMatrix.h"
+
+#include "stdlib.h"
+#include "string.h"
+#include "math.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+
+void Matrix::loadIdentity()
+{
+    set(0, 0, 1);
+    set(1, 0, 0);
+    set(2, 0, 0);
+    set(3, 0, 0);
+
+    set(0, 1, 0);
+    set(1, 1, 1);
+    set(2, 1, 0);
+    set(3, 1, 0);
+
+    set(0, 2, 0);
+    set(1, 2, 0);
+    set(2, 2, 1);
+    set(3, 2, 0);
+
+    set(0, 3, 0);
+    set(1, 3, 0);
+    set(2, 3, 0);
+    set(3, 3, 1);
+}
+
+void Matrix::load(const float *v)
+{
+    memcpy(m, v, sizeof(m));
+}
+
+void Matrix::load(const Matrix *v)
+{
+    memcpy(m, v->m, sizeof(m));
+}
+
+void Matrix::loadRotate(float rot, float x, float y, float z)
+{
+    float c, s;
+    m[3] = 0;
+    m[7] = 0;
+    m[11]= 0;
+    m[12]= 0;
+    m[13]= 0;
+    m[14]= 0;
+    m[15]= 1;
+    rot *= float(M_PI / 180.0f);
+    c = cosf(rot);
+    s = sinf(rot);
+
+    const float len = sqrtf(x*x + y*y + z*z);
+    if (!(len != 1)) {
+        const float recipLen = 1.f / len;
+        x *= recipLen;
+        y *= recipLen;
+        z *= recipLen;
+    }
+    const float nc = 1.0f - c;
+    const float xy = x * y;
+    const float yz = y * z;
+    const float zx = z * x;
+    const float xs = x * s;
+    const float ys = y * s;
+    const float zs = z * s;		
+    m[ 0] = x*x*nc +  c;
+    m[ 4] =  xy*nc - zs;
+    m[ 8] =  zx*nc + ys;
+    m[ 1] =  xy*nc + zs;
+    m[ 5] = y*y*nc +  c;
+    m[ 9] =  yz*nc - xs;
+    m[ 2] =  zx*nc - ys;
+    m[ 6] =  yz*nc + xs;
+    m[10] = z*z*nc +  c;
+}
+
+void Matrix::loadScale(float x, float y, float z)
+{
+    loadIdentity();
+    m[0] = x;
+    m[5] = y;
+    m[10] = z;
+}
+
+void Matrix::loadTranslate(float x, float y, float z)
+{
+    loadIdentity();
+    m[12] = x;
+    m[13] = y;
+    m[14] = z;
+}
+
+void Matrix::loadMultiply(const Matrix *lhs, const Matrix *rhs)
+{
+    for (int i=0 ; i<4 ; i++) {
+        float ri0 = 0;
+        float ri1 = 0;
+        float ri2 = 0;
+        float ri3 = 0;
+        for (int j=0 ; j<4 ; j++) {
+            const float rhs_ij = rhs->get(i,j);
+            ri0 += lhs->get(j,0) * rhs_ij;
+            ri1 += lhs->get(j,1) * rhs_ij;
+            ri2 += lhs->get(j,2) * rhs_ij;
+            ri3 += lhs->get(j,3) * rhs_ij;
+        }
+        set(i,0, ri0);
+        set(i,1, ri1);
+        set(i,2, ri2);
+        set(i,3, ri3);
+    }
+}
+
+void Matrix::loadOrtho(float l, float r, float b, float t, float n, float f) {
+    loadIdentity();
+    m[0] = 2 / (r - l);
+    m[5] = 2 / (t - b);
+    m[10]= -2 / (f - n);
+    m[12]= -(r + l) / (r - l);
+    m[13]= -(t + b) / (t - b);
+    m[14]= -(f + n) / (f - n);
+}
+
+void Matrix::loadFrustum(float l, float r, float b, float t, float n, float f) {
+    loadIdentity();
+    m[0] = 2 * n / (r - l);
+    m[5] = 2 * n / (t - b);
+    m[8] = (r + l) / (r - l);
+    m[9] = (t + b) / (t - b);
+    m[10]= -(f + n) / (f - n);
+    m[11]= -1;
+    m[14]= -2*f*n / (f - n);
+    m[15]= 0;
+}
+
+
diff --git a/libs/rs/rsMatrix.h b/libs/rs/rsMatrix.h
new file mode 100644
index 0000000..7dc4165
--- /dev/null
+++ b/libs/rs/rsMatrix.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_MATRIX_H
+#define ANDROID_RS_MATRIX_H
+
+
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+struct Matrix 
+{
+    float m[16];
+
+    inline float get(int i, int j) const {
+        return m[i*4 + j];
+    }
+
+    inline void set(int i, int j, float v) {
+        m[i*4 + j] = v;
+    }
+
+    void loadIdentity();
+    void load(const float *);
+    void load(const Matrix *);
+
+    void loadRotate(float rot, float x, float y, float z);
+    void loadScale(float x, float y, float z);
+    void loadTranslate(float x, float y, float z);
+    void loadMultiply(const Matrix *lhs, const Matrix *rhs);
+
+    void loadOrtho(float l, float r, float b, float t, float n, float f);
+    void loadFrustum(float l, float r, float b, float t, float n, float f);
+
+    void multiply(const Matrix *rhs) {
+        Matrix tmp;
+        tmp.loadMultiply(this, rhs);
+        load(&tmp);
+    }
+    void rotate(float rot, float x, float y, float z) {
+        Matrix tmp;
+        tmp.loadRotate(rot, x, y, z);
+        multiply(&tmp);
+    }
+    void scale(float x, float y, float z) {
+        Matrix tmp;
+        tmp.loadScale(x, y, z);
+        multiply(&tmp);
+    }
+    void translate(float x, float y, float z) {
+        Matrix tmp;
+        tmp.loadTranslate(x, y, z);
+        multiply(&tmp);
+    }
+
+
+
+};
+    
+
+
+}
+}
+
+
+
+
+#endif
+
+
+
+
diff --git a/libs/utils/executablepath_linux.cpp b/libs/rs/rsMesh.cpp
similarity index 61%
rename from libs/utils/executablepath_linux.cpp
rename to libs/rs/rsMesh.cpp
index b8d2a3d..6eb95fc 100644
--- a/libs/utils/executablepath_linux.cpp
+++ b/libs/rs/rsMesh.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,17 +14,32 @@
  * limitations under the License.
  */
 
-#include <utils/executablepath.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <limits.h>
-#include <stdio.h>
+#include "rsContext.h"
 
-void executablepath(char exe[PATH_MAX])
+using namespace android;
+using namespace android::renderscript;
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+Mesh::Mesh()
 {
-    char proc[100];
-    sprintf(proc, "/proc/%d/exe", getpid());
-    
-    int err = readlink(proc, exe, PATH_MAX);
+    mSources = NULL;
+    mPrimitives = NULL;
+    mPrimitiveCount = 0;
+}
+
+Mesh::~Mesh()
+{
+}
+
+
+
+MeshContext::MeshContext()
+{
+}
+
+MeshContext::~MeshContext()
+{
 }
 
diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h
new file mode 100644
index 0000000..c6d3bc9
--- /dev/null
+++ b/libs/rs/rsMesh.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_MESH_H
+#define ANDROID_RS_MESH_H
+
+
+#include "RenderScript.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+// An element is a group of Components that occupies one cell in a structure.
+class Mesh : public ObjectBase
+{
+public:
+    Mesh();
+    ~Mesh();
+
+    struct VertexSource_t
+    {
+        const Element * mVertexElement;
+        void * mVertexData;
+        size_t mVertexDataSize;
+
+        size_t mOffsetCoord;
+        size_t mOffsetTex;
+        size_t mOffsetNorm;
+    
+        size_t mSizeCoord;
+        size_t mSizeTex;
+        size_t mSizeNorm;
+
+        uint32_t mBufferObject;
+    };
+
+    struct Primitive_t
+    {
+        RsPrimitive mType;
+        const Element * mIndexElement;
+        void * mVertexData;
+        size_t mIndexDataSize;
+
+        uint32_t mBufferObject;
+    };
+
+    VertexSource_t * mSources;
+    Primitive_t * mPrimitives;
+    uint32_t mPrimitiveCount;
+
+    void analyzeElement();
+protected:
+};
+
+class MeshContext
+{
+public:
+    MeshContext();
+    ~MeshContext();
+
+};
+
+
+}
+}
+#endif //ANDROID_RS_TRIANGLE_MESH_H
+
+
diff --git a/libs/rs/rsObjectBase.cpp b/libs/rs/rsObjectBase.cpp
new file mode 100644
index 0000000..3219c39
--- /dev/null
+++ b/libs/rs/rsObjectBase.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsObjectBase.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+ObjectBase::ObjectBase()
+{
+    mRefCount = 0;
+    mName = NULL;
+}
+
+ObjectBase::~ObjectBase()
+{
+    rsAssert(!mRefCount);
+}
+
+void ObjectBase::incRef() const
+{
+    mRefCount ++;
+    //LOGV("ObjectBase %p inc ref %i", this, mRefCount);
+}
+
+void ObjectBase::decRef() const
+{
+    rsAssert(mRefCount > 0);
+    mRefCount --;
+    //LOGV("ObjectBase %p dec ref %i", this, mRefCount);
+    if (!mRefCount) {
+        delete this;
+    }
+}
+
+void ObjectBase::setName(const char *name)
+{
+    delete mName;
+    mName = NULL;
+    if (name) {
+        mName = new char[strlen(name) +1];
+        strcpy(mName, name);
+    }
+}
+
+void ObjectBase::setName(const char *name, uint32_t len)
+{
+    delete mName;
+    mName = NULL;
+    if (name) {
+        mName = new char[len + 1];
+        memcpy(mName, name, len);
+        mName[len] = 0;
+    }
+}
+
diff --git a/libs/rs/rsObjectBase.h b/libs/rs/rsObjectBase.h
new file mode 100644
index 0000000..b2c3338
--- /dev/null
+++ b/libs/rs/rsObjectBase.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_OBJECT_BASE_H
+#define ANDROID_RS_OBJECT_BASE_H
+
+#include "rsUtils.h"
+
+
+namespace android {
+namespace renderscript {
+
+// An element is a group of Components that occupies one cell in a structure.
+class ObjectBase
+{
+public:
+    ObjectBase();
+    virtual ~ObjectBase();
+
+    void incRef() const;
+    void decRef() const;
+
+    const char * getName() const {
+        return mName;
+    }
+    void setName(const char *);
+    void setName(const char *, uint32_t len);
+
+private:
+    char * mName;
+    mutable int32_t mRefCount;
+
+
+};
+
+template<class T> 
+class ObjectBaseRef 
+{
+public:
+    ObjectBaseRef() {
+        mRef = NULL;
+    }
+
+    ObjectBaseRef(const ObjectBaseRef &ref) {
+        mRef = ref.get();
+        if (mRef) {
+            mRef->incRef();
+        }
+    }
+
+    ObjectBaseRef(T *ref) {
+        mRef = ref;
+        if (mRef) {
+            ref->incRef();
+        }
+    }
+
+    ~ObjectBaseRef() {
+        clear();
+    }
+
+    void set(T *ref) {
+        if (mRef != ref) {
+            clear();
+            mRef = ref;
+            if (mRef) {
+                ref->incRef();
+            }
+        }
+    }
+
+    void set(const ObjectBaseRef &ref) {
+        set(ref.mRef);
+    }
+
+    void clear() {
+        if (mRef) {
+            mRef->decRef();
+        }
+        mRef = NULL;
+    }
+
+    inline T * get() const {
+        return mRef;
+    }
+
+    inline T * operator-> () const { 
+        return mRef;  
+    }
+
+protected:
+    T * mRef;
+
+};
+
+
+}
+}
+
+#endif //ANDROID_RS_OBJECT_BASE_H
+
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
new file mode 100644
index 0000000..5a83fb7
--- /dev/null
+++ b/libs/rs/rsProgram.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+#include "rsProgram.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Program::Program(Element *in, Element *out)
+{
+    mElementIn.set(in);
+    mElementOut.set(out);
+
+
+}
+
+Program::~Program()
+{
+}
+
+
+void Program::setAllocation(Allocation *alloc)
+{
+    mConstants.set(alloc);
+    mDirty = true;
+}
+
+void Program::setupGL()
+{
+
+}
+
+
diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h
new file mode 100644
index 0000000..913fdd2
--- /dev/null
+++ b/libs/rs/rsProgram.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_PROGRAM_H
+#define ANDROID_RS_PROGRAM_H
+
+#include "rsObjectBase.h"
+#include "rsElement.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+
+class Program : public ObjectBase
+{
+public:
+    Program(Element *in, Element *out);
+    virtual ~Program();
+
+
+    void setAllocation(Allocation *);
+
+    virtual void setupGL();
+
+protected:
+    // Components not listed in "in" will be passed though
+    // unless overwritten by components in out.
+    ObjectBaseRef<Element> mElementIn;
+    ObjectBaseRef<Element> mElementOut;
+
+    ObjectBaseRef<Allocation> mConstants;
+
+    bool mDirty;
+
+};
+
+
+
+}
+}
+#endif
+
+
+
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
new file mode 100644
index 0000000..f2e7095
--- /dev/null
+++ b/libs/rs/rsProgramFragment.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+#include "rsProgramFragment.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+
+ProgramFragment::ProgramFragment(Element *in, Element *out) :
+    Program(in, out)
+{
+    for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) {
+        mEnvModes[ct] = RS_TEX_ENV_MODE_REPLACE;
+        mTextureDimensions[ct] = 2;
+    }
+    mTextureEnableMask = 0;
+}
+
+ProgramFragment::~ProgramFragment()
+{
+}
+
+void ProgramFragment::setupGL()
+{
+    for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) {
+        glActiveTexture(GL_TEXTURE0 + ct);
+        if (!(mTextureEnableMask & (1 << ct)) ||
+            //!mSamplers[ct].get() ||
+            !mTextures[ct].get()) {
+
+            glDisable(GL_TEXTURE_2D);
+            continue;
+        }
+
+        glEnable(GL_TEXTURE_2D);
+        glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID());
+
+        switch(mEnvModes[ct]) {
+        case RS_TEX_ENV_MODE_REPLACE:
+            glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+            break;
+        case RS_TEX_ENV_MODE_MODULATE:
+            glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+            break;
+        case RS_TEX_ENV_MODE_DECAL:
+            glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_DECAL);
+            break;
+        }
+
+        if (mSamplers[ct].get()) {
+            mSamplers[ct]->setupGL();
+        } else {
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+        }
+    }
+    glActiveTexture(GL_TEXTURE0);
+}
+
+
+void ProgramFragment::bindTexture(uint32_t slot, Allocation *a)
+{
+    if (slot >= MAX_TEXTURE) {
+        LOGE("Attempt to bind a texture to a slot > MAX_TEXTURE");
+        return;
+    }
+
+    mTextures[slot].set(a);
+}
+
+void ProgramFragment::bindSampler(uint32_t slot, Sampler *s)
+{
+    if (slot >= MAX_TEXTURE) {
+        LOGE("Attempt to bind a Sampler to a slot > MAX_TEXTURE");
+        return;
+    }
+
+    mSamplers[slot].set(s);
+}
+
+void ProgramFragment::setType(uint32_t slot, const Element *e, uint32_t dim)
+{
+    if (slot >= MAX_TEXTURE) {
+        LOGE("Attempt to setType to a slot > MAX_TEXTURE");
+        return;
+    }
+
+    if (dim >= 4) {
+        LOGE("Attempt to setType to a dimension > 3");
+        return;
+    }
+
+    mTextureFormats[slot].set(e);
+    mTextureDimensions[slot] = dim;
+}
+
+void ProgramFragment::setEnvMode(uint32_t slot, RsTexEnvMode env)
+{
+    if (slot >= MAX_TEXTURE) {
+        LOGE("Attempt to setEnvMode to a slot > MAX_TEXTURE");
+        return;
+    }
+
+    mEnvModes[slot] = env;
+}
+
+void ProgramFragment::setTexEnable(uint32_t slot, bool enable)
+{
+    if (slot >= MAX_TEXTURE) {
+        LOGE("Attempt to setEnvMode to a slot > MAX_TEXTURE");
+        return;
+    }
+
+    uint32_t bit = 1 << slot;
+    mTextureEnableMask &= ~bit;
+    if (enable) {
+        mTextureEnableMask |= bit;
+    }
+}
+
+
+
+ProgramFragmentState::ProgramFragmentState()
+{
+    mPF = NULL;
+}
+
+ProgramFragmentState::~ProgramFragmentState()
+{
+    delete mPF;
+
+}
+
+void ProgramFragmentState::init(Context *rsc, int32_t w, int32_t h)
+{
+    ProgramFragment *pf = new ProgramFragment(NULL, NULL);
+    mDefault.set(pf);
+}
+
+
+namespace android {
+namespace renderscript {
+
+void rsi_ProgramFragmentBegin(Context * rsc, RsElement in, RsElement out)
+{
+    delete rsc->mStateFragment.mPF;
+    rsc->mStateFragment.mPF = new ProgramFragment((Element *)in, (Element *)out);
+}
+
+void rsi_ProgramFragmentBindTexture(Context *rsc, RsProgramFragment vpf, uint32_t slot, RsAllocation a)
+{
+    ProgramFragment *pf = static_cast<ProgramFragment *>(vpf);
+    pf->bindTexture(slot, static_cast<Allocation *>(a));
+    if (pf == rsc->getFragment()) {
+        pf->setupGL();
+    }
+}
+
+void rsi_ProgramFragmentBindSampler(Context *rsc, RsProgramFragment vpf, uint32_t slot, RsSampler s)
+{
+    ProgramFragment *pf = static_cast<ProgramFragment *>(vpf);
+    pf->bindSampler(slot, static_cast<Sampler *>(s));
+
+    if (pf == rsc->getFragment()) {
+        pf->setupGL();
+    }
+}
+
+void rsi_ProgramFragmentSetType(Context *rsc, uint32_t slot, RsType vt)
+{
+    const Type *t = static_cast<const Type *>(vt);
+    uint32_t dim = 1;
+    if (t->getDimY()) {
+        dim ++;
+        if (t->getDimZ()) {
+            dim ++;
+        }
+    }
+
+    rsc->mStateFragment.mPF->setType(slot, t->getElement(), dim);
+}
+
+void rsi_ProgramFragmentSetEnvMode(Context *rsc, uint32_t slot, RsTexEnvMode env)
+{
+    rsc->mStateFragment.mPF->setEnvMode(slot, env);
+}
+
+void rsi_ProgramFragmentSetTexEnable(Context *rsc, uint32_t slot, bool enable)
+{
+    rsc->mStateFragment.mPF->setTexEnable(slot, enable);
+}
+
+RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc)
+{
+    ProgramFragment *pf = rsc->mStateFragment.mPF;
+    pf->incRef();
+    rsc->mStateFragment.mPF = 0;
+    return pf;
+}
+
+void rsi_ProgramFragmentDestroy(Context *rsc, RsProgramFragment vpf)
+{
+    ProgramFragment *pf = (ProgramFragment *)vpf;
+    if (pf->getName()) {
+        rsc->removeName(pf);
+    }
+    pf->decRef();
+}
+
+
+}
+}
+
diff --git a/libs/rs/rsProgramFragment.h b/libs/rs/rsProgramFragment.h
new file mode 100644
index 0000000..896d8dd
--- /dev/null
+++ b/libs/rs/rsProgramFragment.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_PROGRAM_FRAGMENT_H
+#define ANDROID_RS_PROGRAM_FRAGMENT_H
+
+#include "rsProgram.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+class ProgramFragment : public Program
+{
+public:
+    const static uint32_t MAX_TEXTURE = 2;
+    const static uint32_t MAX_CONSTANTS = 2;
+
+
+
+    ProgramFragment(Element *in, Element *out);
+    virtual ~ProgramFragment();
+
+    virtual void setupGL();
+
+
+
+    void bindTexture(uint32_t slot, Allocation *);
+    void bindSampler(uint32_t slot, Sampler *);
+    void setType(uint32_t slot, const Element *, uint32_t dim);
+
+    void setEnvMode(uint32_t slot, RsTexEnvMode);
+    void setTexEnable(uint32_t slot, bool);
+
+
+
+protected:
+    // The difference between Textures and Constants is how they are accessed
+    // Texture lookups go though a sampler which in effect converts normalized
+    // coordinates into type specific.  Multiple samples may also be taken
+    // and filtered.
+    // 
+    // Constants are strictly accessed by programetic loads.
+    ObjectBaseRef<Allocation> mTextures[MAX_TEXTURE];
+    ObjectBaseRef<Sampler> mSamplers[MAX_TEXTURE];
+    ObjectBaseRef<const Element> mTextureFormats[MAX_TEXTURE];
+    uint32_t mTextureDimensions[MAX_TEXTURE];
+
+
+    ObjectBaseRef<Allocation> mConstants[MAX_CONSTANTS];
+    ObjectBaseRef<Type> mConstantTypes[MAX_CONSTANTS];
+
+
+    // Hacks to create a program for now
+    RsTexEnvMode mEnvModes[MAX_TEXTURE];
+    uint32_t mTextureEnableMask;
+
+
+
+
+
+};
+
+class ProgramFragmentState 
+{
+public:
+    ProgramFragmentState();
+    ~ProgramFragmentState();
+
+    ProgramFragment *mPF;
+    void init(Context *rsc, int32_t w, int32_t h);
+
+    ObjectBaseRef<Type> mTextureTypes[ProgramFragment::MAX_TEXTURE];
+    ObjectBaseRef<ProgramFragment> mDefault;
+    Vector<ProgramFragment *> mPrograms;
+};
+
+
+}
+}
+#endif
+
+
+
+
diff --git a/libs/rs/rsProgramFragmentStore.cpp b/libs/rs/rsProgramFragmentStore.cpp
new file mode 100644
index 0000000..0b65680
--- /dev/null
+++ b/libs/rs/rsProgramFragmentStore.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+#include "rsProgramFragmentStore.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+
+ProgramFragmentStore::ProgramFragmentStore(Element *in, Element *out) :
+    Program(in, out)
+{
+    mDitherEnable = true;
+    mBlendEnable = false;
+    mColorRWriteEnable = true;
+    mColorGWriteEnable = true;
+    mColorBWriteEnable = true;
+    mColorAWriteEnable = true;
+    mBlendSrc = GL_ONE;
+    mBlendDst = GL_ZERO;
+
+
+    mDepthTestEnable = false;
+    mDepthWriteEnable = true;
+    mDepthFunc = GL_LESS;
+
+
+}
+
+ProgramFragmentStore::~ProgramFragmentStore()
+{
+}
+
+void ProgramFragmentStore::setupGL()
+{
+    glColorMask(mColorRWriteEnable,
+                mColorGWriteEnable,
+                mColorBWriteEnable,
+                mColorAWriteEnable);
+    if (mBlendEnable) {
+        glEnable(GL_BLEND);
+        glBlendFunc(mBlendSrc, mBlendDst);
+    } else {
+        glDisable(GL_BLEND);
+    }
+
+    glDepthMask(mDepthWriteEnable);
+    if(mDepthTestEnable) {
+        glEnable(GL_DEPTH_TEST);
+        glDepthFunc(mDepthFunc);
+    } else {
+        glDisable(GL_DEPTH_TEST);
+    }
+
+    if (mDitherEnable) {
+        glEnable(GL_DITHER);
+    } else {
+        glDisable(GL_DITHER);
+    }
+
+
+}
+
+void ProgramFragmentStore::setDitherEnable(bool enable)
+{
+    mDitherEnable = enable;
+}
+
+void ProgramFragmentStore::setDepthFunc(RsDepthFunc func)
+{
+    mDepthTestEnable = true;
+
+    switch(func) {
+    case RS_DEPTH_FUNC_ALWAYS:
+        mDepthTestEnable = false;
+        mDepthFunc = GL_ALWAYS;
+        break;
+    case RS_DEPTH_FUNC_LESS:
+        mDepthFunc = GL_LESS;
+        break;
+    case RS_DEPTH_FUNC_LEQUAL:
+        mDepthFunc = GL_LEQUAL;
+        break;
+    case RS_DEPTH_FUNC_GREATER:
+        mDepthFunc = GL_GREATER;
+        break;
+    case RS_DEPTH_FUNC_GEQUAL:
+        mDepthFunc = GL_GEQUAL;
+        break;
+    case RS_DEPTH_FUNC_EQUAL:
+        mDepthFunc = GL_EQUAL;
+        break;
+    case RS_DEPTH_FUNC_NOTEQUAL:
+        mDepthFunc = GL_NOTEQUAL;
+        break;
+    }
+}
+
+void ProgramFragmentStore::setDepthMask(bool mask)
+{
+    mDepthWriteEnable = mask;
+}
+
+void ProgramFragmentStore::setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst)
+{
+    mBlendEnable = true;
+    if ((src == RS_BLEND_SRC_ONE) && 
+        (dst == RS_BLEND_DST_ZERO)) {
+        mBlendEnable = false;
+    }
+
+    switch(src) {
+    case RS_BLEND_SRC_ZERO:
+        mBlendSrc = GL_ZERO;
+        break;
+    case RS_BLEND_SRC_ONE:
+        mBlendSrc = GL_ONE;
+        break;
+    case RS_BLEND_SRC_DST_COLOR:
+        mBlendSrc = GL_DST_COLOR;
+        break;
+    case RS_BLEND_SRC_ONE_MINUS_DST_COLOR:
+        mBlendSrc = GL_ONE_MINUS_DST_COLOR;
+        break;
+    case RS_BLEND_SRC_SRC_ALPHA:
+        mBlendSrc = GL_SRC_ALPHA;
+        break;
+    case RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA:
+        mBlendSrc = GL_ONE_MINUS_SRC_ALPHA;
+        break;
+    case RS_BLEND_SRC_DST_ALPHA:
+        mBlendSrc = GL_DST_ALPHA;
+        break;
+    case RS_BLEND_SRC_ONE_MINUS_DST_ALPHA:
+        mBlendSrc = GL_ONE_MINUS_DST_ALPHA;
+        break;
+    case RS_BLEND_SRC_SRC_ALPHA_SATURATE:
+        mBlendSrc = GL_SRC_ALPHA_SATURATE;
+        break;
+    }
+
+    switch(dst) {
+    case RS_BLEND_DST_ZERO:
+        mBlendDst = GL_ZERO;
+        break;
+    case RS_BLEND_DST_ONE:
+        mBlendDst = GL_ONE;
+        break;
+    case RS_BLEND_DST_SRC_COLOR:
+        mBlendDst = GL_SRC_COLOR;
+        break;
+    case RS_BLEND_DST_ONE_MINUS_SRC_COLOR:
+        mBlendDst = GL_ONE_MINUS_SRC_COLOR;
+        break;
+    case RS_BLEND_DST_SRC_ALPHA:
+        mBlendDst = GL_SRC_ALPHA;
+        break;
+    case RS_BLEND_DST_ONE_MINUS_SRC_ALPHA:
+        mBlendDst = GL_ONE_MINUS_SRC_ALPHA;
+        break;
+    case RS_BLEND_DST_DST_ALPHA:
+        mBlendDst = GL_DST_ALPHA;
+        break;
+    case RS_BLEND_DST_ONE_MINUS_DST_ALPHA:
+        mBlendDst = GL_ONE_MINUS_DST_ALPHA;
+        break;
+    }
+}
+
+void ProgramFragmentStore::setColorMask(bool r, bool g, bool b, bool a)
+{
+    mColorRWriteEnable = r;
+    mColorGWriteEnable = g;
+    mColorBWriteEnable = b;
+    mColorAWriteEnable = a;
+}
+
+
+ProgramFragmentStoreState::ProgramFragmentStoreState()
+{
+    mPFS = NULL;
+}
+
+ProgramFragmentStoreState::~ProgramFragmentStoreState()
+{
+    delete mPFS;
+
+}
+
+void ProgramFragmentStoreState::init(Context *rsc, int32_t w, int32_t h)
+{
+    ProgramFragmentStore *pfs = new ProgramFragmentStore(NULL, NULL);
+    mDefault.set(pfs);
+}
+
+
+namespace android {
+namespace renderscript {
+
+void rsi_ProgramFragmentStoreBegin(Context * rsc, RsElement in, RsElement out)
+{
+    delete rsc->mStateFragmentStore.mPFS;
+    rsc->mStateFragmentStore.mPFS = new ProgramFragmentStore((Element *)in, (Element *)out);
+
+}
+
+void rsi_ProgramFragmentStoreDepthFunc(Context *rsc, RsDepthFunc func)
+{
+    rsc->mStateFragmentStore.mPFS->setDepthFunc(func);
+}
+
+void rsi_ProgramFragmentStoreDepthMask(Context *rsc, bool mask)
+{
+    rsc->mStateFragmentStore.mPFS->setDepthMask(mask);
+}
+
+void rsi_ProgramFragmentStoreColorMask(Context *rsc, bool r, bool g, bool b, bool a)
+{
+    rsc->mStateFragmentStore.mPFS->setColorMask(r, g, b, a);
+}
+
+void rsi_ProgramFragmentStoreBlendFunc(Context *rsc, RsBlendSrcFunc src, RsBlendDstFunc dst)
+{
+    rsc->mStateFragmentStore.mPFS->setBlendFunc(src, dst);
+}
+
+RsProgramFragmentStore rsi_ProgramFragmentStoreCreate(Context *rsc)
+{
+    ProgramFragmentStore *pfs = rsc->mStateFragmentStore.mPFS;
+    pfs->incRef();
+    rsc->mStateFragmentStore.mPFS = 0;
+    return pfs;
+}
+
+void rsi_ProgramFragmentStoreDither(Context *rsc, bool enable)
+{
+    rsc->mStateFragmentStore.mPFS->setDitherEnable(enable);
+}
+
+void rsi_ProgramFragmentStoreDestroy(Context *rsc, RsProgramFragmentStore vpfs)
+{
+    ProgramFragmentStore *pfs = (ProgramFragmentStore *)vpfs;
+    if (pfs->getName()) {
+        rsc->removeName(pfs);
+    }
+    pfs->decRef();
+}
+
+
+
+
+}
+}
diff --git a/libs/rs/rsProgramFragmentStore.h b/libs/rs/rsProgramFragmentStore.h
new file mode 100644
index 0000000..bd3a9f4
--- /dev/null
+++ b/libs/rs/rsProgramFragmentStore.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_PROGRAM_FRAGMENT_STORE_H
+#define ANDROID_RS_PROGRAM_FRAGMENT_STORE_H
+
+#include "rsProgram.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+class ProgramFragmentStore : public Program
+{
+public:
+
+
+
+    ProgramFragmentStore(Element *in, Element *out);
+    virtual ~ProgramFragmentStore();
+
+    virtual void setupGL();
+
+
+    void setDepthFunc(RsDepthFunc);
+    void setDepthMask(bool);
+
+    void setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst);
+    void setColorMask(bool, bool, bool, bool);
+
+    void setDitherEnable(bool);
+
+protected:
+    bool mDitherEnable;
+
+    bool mBlendEnable;
+    bool mColorRWriteEnable;
+    bool mColorGWriteEnable;
+    bool mColorBWriteEnable;
+    bool mColorAWriteEnable;
+    int32_t mBlendSrc;
+    int32_t mBlendDst;
+
+
+
+    bool mDepthTestEnable;
+    bool mDepthWriteEnable;
+    int32_t mDepthFunc;
+
+
+
+    bool mStencilTestEnable;
+
+
+
+};
+
+class ProgramFragmentStoreState 
+{
+public:
+    ProgramFragmentStoreState();
+    ~ProgramFragmentStoreState();
+    void init(Context *rsc, int32_t w, int32_t h);
+
+    ObjectBaseRef<ProgramFragmentStore> mDefault;
+    ProgramFragmentStore *mPFS;
+};
+
+
+}
+}
+#endif
+
+
+
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
new file mode 100644
index 0000000..4089507
--- /dev/null
+++ b/libs/rs/rsProgramVertex.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+#include "rsProgramVertex.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+
+ProgramVertex::ProgramVertex(Element *in, Element *out) :
+    Program(in, out)
+{
+    mTextureMatrixEnable = false;
+}
+
+ProgramVertex::~ProgramVertex()
+{
+}
+
+static void logMatrix(const char *txt, const float *f)
+{
+    LOGV("Matrix %s, %p", txt, f);
+    LOGV("%6.2f, %6.2f, %6.2f, %6.2f", f[0], f[4], f[8], f[12]);
+    LOGV("%6.2f, %6.2f, %6.2f, %6.2f", f[1], f[5], f[9], f[13]);
+    LOGV("%6.2f, %6.2f, %6.2f, %6.2f", f[2], f[6], f[10], f[14]);
+    LOGV("%6.2f, %6.2f, %6.2f, %6.2f", f[3], f[7], f[11], f[15]);
+}
+
+void ProgramVertex::setupGL()
+{
+    const float *f = static_cast<const float *>(mConstants[0]->getPtr());
+
+    glMatrixMode(GL_TEXTURE);
+    if (mTextureMatrixEnable) {
+        glLoadMatrixf(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]);
+    } else {
+        glLoadIdentity();
+    }
+
+    //logMatrix("prog", &f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
+    //logMatrix("model", &f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
+
+    glMatrixMode(GL_PROJECTION);
+    glLoadMatrixf(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadMatrixf(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
+}
+
+void ProgramVertex::setConstantType(uint32_t slot, const Type *t)
+{
+    mConstantTypes[slot].set(t);
+}
+
+void ProgramVertex::bindAllocation(uint32_t slot, Allocation *a)
+{
+    mConstants[slot].set(a);
+}
+
+
+ProgramVertexState::ProgramVertexState()
+{
+    mPV = NULL;
+}
+
+ProgramVertexState::~ProgramVertexState()
+{
+    delete mPV;
+}
+
+void ProgramVertexState::init(Context *rsc, int32_t w, int32_t h)
+{
+    ProgramVertex *pv = new ProgramVertex(NULL, NULL);
+    Allocation *alloc = (Allocation *)
+        rsi_AllocationCreatePredefSized(rsc, RS_ELEMENT_USER_FLOAT, 48);
+    mDefaultAlloc.set(alloc);
+    mDefault.set(pv);
+
+    pv->bindAllocation(0, alloc);
+    
+    Matrix m;
+    m.loadOrtho(0,w, h,0, -1,1);
+    alloc->subData(RS_PROGRAM_VERTEX_PROJECTION_OFFSET, 16, &m.m[0]);
+
+    m.loadIdentity();
+    alloc->subData(RS_PROGRAM_VERTEX_MODELVIEW_OFFSET, 16, &m.m[0]);
+}
+
+
+namespace android {
+namespace renderscript {
+
+void rsi_ProgramVertexBegin(Context *rsc, RsElement in, RsElement out)
+{
+    delete rsc->mStateVertex.mPV;
+    rsc->mStateVertex.mPV = new ProgramVertex((Element *)in, (Element *)out);
+}
+
+RsProgramVertex rsi_ProgramVertexCreate(Context *rsc)
+{
+    ProgramVertex *pv = rsc->mStateVertex.mPV;
+    pv->incRef();
+    rsc->mStateVertex.mPV = 0;
+    return pv;
+}
+
+void rsi_ProgramVertexBindAllocation(Context *rsc, RsProgramVertex vpgm, uint32_t slot, RsAllocation constants)
+{
+    ProgramVertex *pv = static_cast<ProgramVertex *>(vpgm);
+    pv->bindAllocation(slot, static_cast<Allocation *>(constants));
+}
+
+void rsi_ProgramVertexSetType(Context *rsc, uint32_t slot, RsType constants)
+{
+    rsc->mStateVertex.mPV->setConstantType(slot, static_cast<const Type *>(constants));
+}
+
+void rsi_ProgramVertexSetTextureMatrixEnable(Context *rsc, bool enable)
+{
+    rsc->mStateVertex.mPV->setTextureMatrixEnable(enable);
+}
+
+
+
+}
+}
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
new file mode 100644
index 0000000..1a92f01
--- /dev/null
+++ b/libs/rs/rsProgramVertex.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_PROGRAM_VERTEX_H
+#define ANDROID_RS_PROGRAM_VERTEX_H
+
+#include "rsProgram.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+class ProgramVertex : public Program
+{
+public:
+    const static uint32_t MAX_CONSTANTS = 2;
+
+    ProgramVertex(Element *in, Element *out);
+    virtual ~ProgramVertex();
+
+    virtual void setupGL();
+
+
+    void setConstantType(uint32_t slot, const Type *);
+    void bindAllocation(uint32_t slot, Allocation *);
+    void setTextureMatrixEnable(bool e) {mTextureMatrixEnable = e;}
+
+protected:
+    bool mDirty;
+
+    ObjectBaseRef<Allocation> mConstants[MAX_CONSTANTS];
+    ObjectBaseRef<const Type> mConstantTypes[MAX_CONSTANTS];
+
+    // Hacks to create a program for now
+    bool mTextureMatrixEnable;
+
+};
+
+
+class ProgramVertexState 
+{
+public:
+    ProgramVertexState();
+    ~ProgramVertexState();
+
+    void init(Context *rsc, int32_t w, int32_t h);
+
+    ObjectBaseRef<ProgramVertex> mDefault;
+    ObjectBaseRef<Allocation> mDefaultAlloc;
+
+    ProgramVertex *mPV;
+
+    //ObjectBaseRef<Type> mTextureTypes[ProgramFragment::MAX_TEXTURE];
+
+
+};
+
+
+}
+}
+#endif
+
+
diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp
new file mode 100644
index 0000000..418f127
--- /dev/null
+++ b/libs/rs/rsSampler.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include "rsContext.h"
+#include "rsSampler.h"
+
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Sampler::Sampler()
+{
+    // Should not get called.
+    rsAssert(0);
+}
+
+Sampler::Sampler(RsSamplerValue magFilter,
+                 RsSamplerValue minFilter,
+                 RsSamplerValue wrapS,
+                 RsSamplerValue wrapT,
+                 RsSamplerValue wrapR)
+{
+    mMagFilter = magFilter;
+    mMinFilter = minFilter;
+    mWrapS = wrapS;
+    mWrapT = wrapT;
+    mWrapR = wrapR;
+}
+
+Sampler::~Sampler()
+{
+}
+
+void Sampler::setupGL()
+{
+    GLenum trans[] = {
+        GL_NEAREST, //RS_SAMPLER_NEAREST,
+        GL_LINEAR, //RS_SAMPLER_LINEAR,
+        GL_LINEAR_MIPMAP_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR,
+        GL_REPEAT, //RS_SAMPLER_WRAP,
+        GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP
+
+    };
+
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, trans[mWrapS]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, trans[mWrapT]);
+
+}
+
+void Sampler::bindToContext(SamplerState *ss, uint32_t slot)
+{
+    ss->mSamplers[slot].set(this);
+    mBoundSlot = slot;
+}
+
+void Sampler::unbindFromContext(SamplerState *ss)
+{
+    int32_t slot = mBoundSlot;
+    mBoundSlot = -1;
+    ss->mSamplers[slot].clear();
+}
+
+void SamplerState::setupGL()
+{
+    for (uint32_t ct=0; ct < RS_MAX_SAMPLER_SLOT; ct++) {
+        Sampler *s = mSamplers[ct].get();
+        if (s) {
+            s->setupGL();
+        } else {
+            glBindTexture(GL_TEXTURE_2D, 0);
+        }
+    }
+}
+
+////////////////////////////////
+
+namespace android {
+namespace renderscript {
+
+
+void rsi_SamplerBegin(Context *rsc)
+{
+    SamplerState * ss = &rsc->mStateSampler;
+
+    ss->mMagFilter = RS_SAMPLER_LINEAR;
+    ss->mMinFilter = RS_SAMPLER_LINEAR;
+    ss->mWrapS = RS_SAMPLER_WRAP;
+    ss->mWrapT = RS_SAMPLER_WRAP;
+    ss->mWrapR = RS_SAMPLER_WRAP;
+}
+
+void rsi_SamplerSet(Context *rsc, RsSamplerParam param, RsSamplerValue value)
+{
+    SamplerState * ss = &rsc->mStateSampler;
+
+    switch(param) {
+    case RS_SAMPLER_MAG_FILTER:
+        ss->mMagFilter = value;
+        break;
+    case RS_SAMPLER_MIN_FILTER:
+        ss->mMinFilter = value;
+        break;
+    case RS_SAMPLER_WRAP_S:
+        ss->mWrapS = value;
+        break;
+    case RS_SAMPLER_WRAP_T:
+        ss->mWrapT = value;
+        break;
+    case RS_SAMPLER_WRAP_R:
+        ss->mWrapR = value;
+        break;
+    }
+
+}
+
+RsSampler rsi_SamplerCreate(Context *rsc)
+{
+    SamplerState * ss = &rsc->mStateSampler;
+
+
+    Sampler * s = new Sampler(ss->mMagFilter, 
+                              ss->mMinFilter, 
+                              ss->mWrapS, 
+                              ss->mWrapT,
+                              ss->mWrapR);
+    return s;
+}
+
+void rsi_SamplerDestroy(Context *rsc, RsSampler vs)
+{
+    Sampler * s = static_cast<Sampler *>(vs);
+    s->decRef();
+
+}
+
+
+}}
diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h
new file mode 100644
index 0000000..4b504f6
--- /dev/null
+++ b/libs/rs/rsSampler.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_SAMPLER_H
+#define ANDROID_RS_SAMPLER_H
+
+#include "rsAllocation.h"
+#include "RenderScript.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+const static uint32_t RS_MAX_SAMPLER_SLOT = 16;
+
+class SamplerState;
+
+class Sampler : public ObjectBase
+{
+public:
+    Sampler(RsSamplerValue magFilter,
+            RsSamplerValue minFilter,
+            RsSamplerValue wrapS,
+            RsSamplerValue wrapT,
+            RsSamplerValue wrapR);
+
+    virtual ~Sampler();
+
+    void bind(Allocation *);
+    void setupGL();
+
+    void bindToContext(SamplerState *, uint32_t slot);
+    void unbindFromContext(SamplerState *);
+
+protected:
+    RsSamplerValue mMagFilter;
+    RsSamplerValue mMinFilter;
+    RsSamplerValue mWrapS;
+    RsSamplerValue mWrapT;
+    RsSamplerValue mWrapR;
+
+    int32_t mBoundSlot;
+
+private:
+    Sampler();
+
+};
+
+
+class SamplerState 
+{
+public:
+
+    RsSamplerValue mMagFilter;
+    RsSamplerValue mMinFilter;
+    RsSamplerValue mWrapS;
+    RsSamplerValue mWrapT;
+    RsSamplerValue mWrapR;
+
+
+    ObjectBaseRef<Sampler> mSamplers[RS_MAX_SAMPLER_SLOT];
+
+    void setupGL();
+
+};
+
+
+
+}
+}
+#endif //ANDROID_RS_SAMPLER_H
+
+
+
diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp
new file mode 100644
index 0000000..ae85c9c
--- /dev/null
+++ b/libs/rs/rsScript.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+Script::Script()
+{
+    memset(&mEnviroment, 0, sizeof(mEnviroment));
+    mEnviroment.mClearColor[0] = 0;
+    mEnviroment.mClearColor[1] = 0;
+    mEnviroment.mClearColor[2] = 0;
+    mEnviroment.mClearColor[3] = 1;
+    mEnviroment.mClearDepth = 1;
+}
+
+Script::~Script()
+{
+}
+
+namespace android {
+namespace renderscript {
+
+
+void rsi_ScriptDestroy(Context * rsc, RsScript vs)
+{
+    Script *s = static_cast<Script *>(vs);
+    s->decRef();
+}
+
+void rsi_ScriptBindAllocation(Context * rsc, RsScript vs, RsAllocation va, uint32_t slot)
+{
+    Script *s = static_cast<Script *>(vs);
+    s->mSlots[slot].set(static_cast<Allocation *>(va));
+}
+
+
+}
+}
+
diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h
new file mode 100644
index 0000000..7dd2b61
--- /dev/null
+++ b/libs/rs/rsScript.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_SCRIPT_H
+#define ANDROID_RS_SCRIPT_H
+
+#include "rsAllocation.h"
+
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class ProgramVertex;
+class ProgramFragment;
+class ProgramRaster;
+class ProgramFragmentStore;
+
+class Script : public ObjectBase
+{
+public:
+
+    Script();
+    virtual ~Script();
+
+
+    struct Enviroment_t {
+        bool mIsRoot;
+        float mClearColor[4];
+        float mClearDepth;
+        uint32_t mClearStencil;
+
+        ObjectBaseRef<ProgramVertex> mVertex;
+        ObjectBaseRef<ProgramFragment> mFragment;
+        //ObjectBaseRef<ProgramRaster> mRaster;
+        ObjectBaseRef<ProgramFragmentStore> mFragmentStore;
+
+    };
+    Enviroment_t mEnviroment;
+
+    const Type * mConstantBufferTypes;
+    uint32_t mCounstantBufferCount;
+
+    ObjectBaseRef<Allocation> mSlots[16];
+
+    virtual bool run(Context *, uint32_t launchID) = 0;
+};
+
+
+
+}
+}
+#endif
+
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
new file mode 100644
index 0000000..55daf7e
--- /dev/null
+++ b/libs/rs/rsScriptC.cpp
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+#include "rsScriptC.h"
+#include "rsMatrix.h"
+
+#include "acc/acc.h"
+#include "utils/String8.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+#define GET_TLS()  Context::ScriptTLSStruct * tls = \
+    (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \
+    Context * rsc = tls->mContext; \
+    ScriptC * sc = (ScriptC *) tls->mScript
+
+
+ScriptC::ScriptC()
+{
+    mAccScript = NULL;
+    memset(&mProgram, 0, sizeof(mProgram));
+}
+
+ScriptC::~ScriptC()
+{
+    if (mAccScript) {
+        accDeleteScript(mAccScript);
+    }
+}
+
+extern "C" float fixedToFloat(int32_t f)
+{
+    return ((float)f) / 0x10000;
+}
+
+extern "C" float intToFloat(int32_t f)
+{
+    return (float)f;
+}
+
+extern "C" void matrixLoadIdentity(rsc_Matrix *mat)
+{
+    Matrix *m = reinterpret_cast<Matrix *>(mat);
+    m->loadIdentity();
+}
+
+extern "C" void matrixLoadFloat(rsc_Matrix *mat, const float *f)
+{
+    Matrix *m = reinterpret_cast<Matrix *>(mat);
+    m->load(f);
+}
+
+extern "C" void matrixLoadMat(rsc_Matrix *mat, const rsc_Matrix *newmat)
+{
+    Matrix *m = reinterpret_cast<Matrix *>(mat);
+    m->load(reinterpret_cast<const Matrix *>(newmat));
+}
+
+extern "C" void matrixLoadRotate(rsc_Matrix *mat, float rot, float x, float y, float z)
+{
+    Matrix *m = reinterpret_cast<Matrix *>(mat);
+    m->loadRotate(rot, x, y, z);
+}
+
+extern "C" void matrixLoadScale(rsc_Matrix *mat, float x, float y, float z)
+{
+    Matrix *m = reinterpret_cast<Matrix *>(mat);
+    m->loadScale(x, y, z);
+}
+
+extern "C" void matrixLoadTranslate(rsc_Matrix *mat, float x, float y, float z)
+{
+    Matrix *m = reinterpret_cast<Matrix *>(mat);
+    m->loadTranslate(x, y, z);
+}
+
+extern "C" void matrixLoadMultiply(rsc_Matrix *mat, const rsc_Matrix *lhs, const rsc_Matrix *rhs)
+{
+    Matrix *m = reinterpret_cast<Matrix *>(mat);
+    m->loadMultiply(reinterpret_cast<const Matrix *>(lhs),
+                    reinterpret_cast<const Matrix *>(rhs));
+}
+
+extern "C" void matrixMultiply(rsc_Matrix *mat, const rsc_Matrix *rhs)
+{
+    Matrix *m = reinterpret_cast<Matrix *>(mat);
+    m->multiply(reinterpret_cast<const Matrix *>(rhs));
+}
+
+extern "C" void matrixRotate(rsc_Matrix *mat, float rot, float x, float y, float z)
+{
+    Matrix *m = reinterpret_cast<Matrix *>(mat);
+    m->rotate(rot, x, y, z);
+}
+
+extern "C" void matrixScale(rsc_Matrix *mat, float x, float y, float z)
+{
+    Matrix *m = reinterpret_cast<Matrix *>(mat);
+    m->scale(x, y, z);
+}
+
+extern "C" void matrixTranslate(rsc_Matrix *mat, float x, float y, float z)
+{
+    Matrix *m = reinterpret_cast<Matrix *>(mat);
+    m->translate(x, y, z);
+}
+
+
+extern "C" const void * loadVp(uint32_t bank, uint32_t offset)
+{
+    GET_TLS();
+    return &static_cast<const uint8_t *>(sc->mSlots[bank]->getPtr())[offset];
+}
+
+extern "C" float loadF(uint32_t bank, uint32_t offset)
+{
+    GET_TLS();
+    return static_cast<const float *>(sc->mSlots[bank]->getPtr())[offset];
+}
+
+extern "C" int32_t loadI32(uint32_t bank, uint32_t offset)
+{
+    GET_TLS();
+    return static_cast<const int32_t *>(sc->mSlots[bank]->getPtr())[offset];
+}
+
+extern "C" uint32_t loadU32(uint32_t bank, uint32_t offset)
+{
+    GET_TLS();
+    return static_cast<const uint32_t *>(sc->mSlots[bank]->getPtr())[offset];
+}
+
+extern "C" void loadEnvVec4(uint32_t bank, uint32_t offset, rsc_Vector4 *v)
+{
+    GET_TLS();
+    memcpy(v, &static_cast<const float *>(sc->mSlots[bank]->getPtr())[offset], sizeof(rsc_Vector4));
+}
+
+extern "C" void loadEnvMatrix(uint32_t bank, uint32_t offset, rsc_Matrix *m)
+{
+    GET_TLS();
+    memcpy(m, &static_cast<const float *>(sc->mSlots[bank]->getPtr())[offset], sizeof(rsc_Matrix));
+}
+
+
+extern "C" void storeF(uint32_t bank, uint32_t offset, float v)
+{
+    GET_TLS();
+    static_cast<float *>(sc->mSlots[bank]->getPtr())[offset] = v;
+}
+
+extern "C" void storeI32(uint32_t bank, uint32_t offset, int32_t v)
+{
+    GET_TLS();
+    static_cast<int32_t *>(sc->mSlots[bank]->getPtr())[offset] = v;
+}
+
+extern "C" void storeU32(uint32_t bank, uint32_t offset, uint32_t v)
+{
+    GET_TLS();
+    static_cast<uint32_t *>(sc->mSlots[bank]->getPtr())[offset] = v;
+}
+
+extern "C" void storeEnvVec4(uint32_t bank, uint32_t offset, const rsc_Vector4 *v)
+{
+    GET_TLS();
+    memcpy(&static_cast<float *>(sc->mSlots[bank]->getPtr())[offset], v, sizeof(rsc_Vector4));
+}
+
+extern "C" void storeEnvMatrix(uint32_t bank, uint32_t offset, const rsc_Matrix *m)
+{
+    GET_TLS();
+    memcpy(&static_cast<float *>(sc->mSlots[bank]->getPtr())[offset], m, sizeof(rsc_Matrix));
+}
+
+
+extern "C" void color(float r, float g, float b, float a)
+{
+    glColor4f(r, g, b, a);
+}
+
+extern "C" void renderTriangleMesh(RsTriangleMesh mesh)
+{
+    GET_TLS();
+    rsi_TriangleMeshRender(rsc, mesh);
+}
+
+extern "C" void renderTriangleMeshRange(RsTriangleMesh mesh, uint32_t start, uint32_t count)
+{
+    GET_TLS();
+    rsi_TriangleMeshRenderRange(rsc, mesh, start, count);
+}
+
+extern "C" void materialDiffuse(float r, float g, float b, float a)
+{
+    float v[] = {r, g, b, a};
+    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, v);
+}
+
+extern "C" void materialSpecular(float r, float g, float b, float a)
+{
+    float v[] = {r, g, b, a};
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, v);
+}
+
+extern "C" void lightPosition(float x, float y, float z, float w)
+{
+    float v[] = {x, y, z, w};
+    glLightfv(GL_LIGHT0, GL_POSITION, v);
+}
+
+extern "C" void materialShininess(float s)
+{
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &s);
+}
+
+extern "C" void uploadToTexture(RsAllocation va, uint32_t baseMipLevel)
+{
+    GET_TLS();
+    rsi_AllocationUploadToTexture(rsc, va, baseMipLevel);
+}
+
+extern "C" void enable(uint32_t p)
+{
+    glEnable(p);
+}
+
+extern "C" void disable(uint32_t p)
+{
+    glDisable(p);
+}
+
+extern "C" uint32_t scriptRand(uint32_t max)
+{
+    return (uint32_t)(((float)rand()) * max / RAND_MAX);
+}
+
+// Assumes (GL_FIXED) x,y,z (GL_UNSIGNED_BYTE)r,g,b,a
+extern "C" void drawTriangleArray(RsAllocation alloc, uint32_t count)
+{
+    GET_TLS();
+
+    const Allocation *a = (const Allocation *)alloc;
+    const uint32_t *ptr = (const uint32_t *)a->getPtr();
+
+    rsc->setupCheck();
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glDisableClientState(GL_NORMAL_ARRAY);
+    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    glEnableClientState(GL_COLOR_ARRAY);
+
+    glVertexPointer(2, GL_FIXED, 12, ptr + 1);
+    //glTexCoordPointer(2, GL_FIXED, 24, ptr + 1);
+    glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr);
+
+    glDrawArrays(GL_TRIANGLES, 0, count * 3);
+}
+
+extern "C" void drawRect(int32_t x1, int32_t x2, int32_t y1, int32_t y2)
+{
+    GET_TLS();
+    x1 = (x1 << 16);
+    x2 = (x2 << 16);
+    y1 = (y1 << 16);
+    y2 = (y2 << 16);
+
+    int32_t vtx[] = {x1,y1, x1,y2, x2,y1, x2,y2};
+    static const int32_t tex[] = {0,0, 0,0x10000, 0x10000,0, 0x10000,0x10000};
+
+
+    rsc->setupCheck();
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+    glDisableClientState(GL_NORMAL_ARRAY);
+    glDisableClientState(GL_COLOR_ARRAY);
+
+    glVertexPointer(2, GL_FIXED, 8, vtx);
+    glTexCoordPointer(2, GL_FIXED, 8, tex);
+    //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr);
+
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+extern "C" void drawQuad(int32_t x1, int32_t y1, int32_t z1,
+                         int32_t x2, int32_t y2, int32_t z2,
+                         int32_t x3, int32_t y3, int32_t z3,
+                         int32_t x4, int32_t y4, int32_t z4)
+{
+    GET_TLS();
+    //x1 = (x1 << 16);
+    //x2 = (x2 << 16);
+    //y1 = (y1 << 16);
+    //y2 = (y2 << 16);
+
+    //LOGE("Quad");
+    //LOGE("0x%08x, 0x%08x, 0x%08x", x1, y1, z1);
+    //LOGE("0x%08x, 0x%08x, 0x%08x", x2, y2, z2);
+    //LOGE("0x%08x, 0x%08x, 0x%08x", x3, y3, z3);
+    //LOGE("0x%08x, 0x%08x, 0x%08x", x4, y4, z4);
+
+    int32_t vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4};
+    static const int32_t tex[] = {0,0, 0,0x10000, 0x10000,0x10000, 0x10000,0};
+
+
+    rsc->setupCheck();
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+    glDisableClientState(GL_NORMAL_ARRAY);
+    glDisableClientState(GL_COLOR_ARRAY);
+
+    glVertexPointer(3, GL_FIXED, 0, vtx);
+    glTexCoordPointer(2, GL_FIXED, 0, tex);
+    //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr);
+
+    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+}
+
+extern "C" int32_t sinx(int32_t angle)
+{
+    float a = ((float)angle) / 0x10000;
+    a *= 3.14f / 180.f;
+    float s = (float)sin(a);
+    return int32_t(s * 0x10000);
+}
+
+extern "C" int32_t cosx(int32_t angle)
+{
+    float a = ((float)angle) / 0x10000;
+    a *= 3.14f / 180.f;
+    float s = (float)cos(a);
+    return int32_t(s * 0x10000);
+}
+
+extern "C" void pfBindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va)
+{
+    GET_TLS();
+    rsi_ProgramFragmentBindTexture(rsc,
+                                   static_cast<ProgramFragment *>(vpf),
+                                   slot,
+                                   static_cast<Allocation *>(va));
+
+}
+
+extern "C" void pfBindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs)
+{
+    GET_TLS();
+    rsi_ProgramFragmentBindSampler(rsc,
+                                   static_cast<ProgramFragment *>(vpf),
+                                   slot,
+                                   static_cast<Sampler *>(vs));
+
+}
+
+extern "C" void contextBindProgramFragmentStore(RsProgramFragmentStore pfs)
+{
+    GET_TLS();
+    rsi_ContextBindProgramFragmentStore(rsc, pfs);
+
+}
+
+extern "C" void contextBindProgramFragment(RsProgramFragment pf)
+{
+    GET_TLS();
+    rsi_ContextBindProgramFragment(rsc, pf);
+
+}
+
+
+static rsc_FunctionTable scriptCPtrTable = {
+    loadVp,
+    loadF,
+    loadI32,
+    loadU32,
+    loadEnvVec4,
+    loadEnvMatrix,
+
+    storeF,
+    storeI32,
+    storeU32,
+    storeEnvVec4,
+    storeEnvMatrix,
+
+    matrixLoadIdentity,
+    matrixLoadFloat,
+    matrixLoadMat,
+    matrixLoadRotate,
+    matrixLoadScale,
+    matrixLoadTranslate,
+    matrixLoadMultiply,
+    matrixMultiply,
+    matrixRotate,
+    matrixScale,
+    matrixTranslate,
+
+    color,
+
+    pfBindTexture,
+    pfBindSampler,
+
+    materialDiffuse,
+    materialSpecular,
+    lightPosition,
+    materialShininess,
+    uploadToTexture,
+    enable,
+    disable,
+
+    scriptRand,
+    contextBindProgramFragment,
+    contextBindProgramFragmentStore,
+
+
+    renderTriangleMesh,
+    renderTriangleMeshRange,
+
+    drawTriangleArray,
+    drawRect
+
+};
+
+
+bool ScriptC::run(Context *rsc, uint32_t launchIndex)
+{
+    Context::ScriptTLSStruct * tls = 
+    (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey);
+
+    if (mEnviroment.mFragmentStore.get()) {
+        rsc->setFragmentStore(mEnviroment.mFragmentStore.get());
+    }
+    if (mEnviroment.mFragment.get()) {
+        rsc->setFragment(mEnviroment.mFragment.get());
+    }
+    if (mEnviroment.mVertex.get()) {
+        rsc->setVertex(mEnviroment.mVertex.get());
+    }
+
+    tls->mScript = this;
+    return mProgram.mScript(launchIndex, &scriptCPtrTable) != 0;
+    tls->mScript = NULL;
+}
+
+ScriptCState::ScriptCState()
+{
+    clear();
+}
+
+ScriptCState::~ScriptCState()
+{
+    if (mAccScript) {
+        accDeleteScript(mAccScript);
+    }
+}
+
+void ScriptCState::clear()
+{
+    memset(&mProgram, 0, sizeof(mProgram));
+
+    mConstantBufferTypes.clear();
+
+    memset(&mEnviroment, 0, sizeof(mEnviroment));
+    mEnviroment.mClearColor[0] = 0;
+    mEnviroment.mClearColor[1] = 0;
+    mEnviroment.mClearColor[2] = 0;
+    mEnviroment.mClearColor[3] = 1;
+    mEnviroment.mClearDepth = 1;
+    mEnviroment.mClearStencil = 0;
+    mEnviroment.mIsRoot = false;
+
+    mAccScript = NULL;
+
+}
+
+
+void ScriptCState::runCompiler(Context *rsc)
+{
+    mAccScript = accCreateScript();
+    String8 tmp;
+
+    rsc->appendNameDefines(&tmp);
+
+    const char* scriptSource[] = {tmp.string(), mProgram.mScriptText};
+    int scriptLength[] = {tmp.length(), mProgram.mScriptTextLength} ;
+    accScriptSource(mAccScript, sizeof(scriptLength) / sizeof(int), scriptSource, scriptLength);
+    accCompileScript(mAccScript);
+    accGetScriptLabel(mAccScript, "main", (ACCvoid**) &mProgram.mScript);
+    rsAssert(mProgram.mScript);
+
+    mEnviroment.mFragment.set(rsc->getDefaultProgramFragment());
+    mEnviroment.mVertex.set(rsc->getDefaultProgramVertex());
+    mEnviroment.mFragmentStore.set(rsc->getDefaultProgramFragmentStore());
+
+    if (mProgram.mScript) {
+        const static int pragmaMax = 16;
+        ACCsizei pragmaCount;
+        ACCchar * str[pragmaMax];
+        accGetPragmas(mAccScript, &pragmaCount, pragmaMax, &str[0]);
+
+        for (int ct=0; ct < pragmaCount; ct+=2) {
+            if (!strcmp(str[ct], "version")) {
+                continue;
+            }
+
+            if (!strcmp(str[ct], "stateVertex")) {
+                if (!strcmp(str[ct+1], "default")) {
+                    continue;
+                }
+                if (!strcmp(str[ct+1], "parent")) {
+                    mEnviroment.mVertex.clear();
+                    continue;
+                }
+                ProgramVertex * pv = (ProgramVertex *)rsc->lookupName(str[ct+1]);
+                if (pv != NULL) {
+                    mEnviroment.mVertex.set(pv);
+                    continue;
+                }
+                LOGE("Unreconized value %s passed to stateVertex", str[ct+1]);
+            }
+
+            if (!strcmp(str[ct], "stateRaster")) {
+                LOGE("Unreconized value %s passed to stateRaster", str[ct+1]);
+            }
+
+            if (!strcmp(str[ct], "stateFragment")) {
+                if (!strcmp(str[ct+1], "default")) {
+                    continue;
+                }
+                if (!strcmp(str[ct+1], "parent")) {
+                    mEnviroment.mFragment.clear();
+                    continue;
+                }
+                ProgramFragment * pf = (ProgramFragment *)rsc->lookupName(str[ct+1]);
+                if (pf != NULL) {
+                    mEnviroment.mFragment.set(pf);
+                    continue;
+                }
+                LOGE("Unreconized value %s passed to stateFragment", str[ct+1]);
+            }
+
+            if (!strcmp(str[ct], "stateFragmentStore")) {
+                if (!strcmp(str[ct+1], "default")) {
+                    continue;
+                }
+                if (!strcmp(str[ct+1], "parent")) {
+                    mEnviroment.mFragmentStore.clear();
+                    continue;
+                }
+                ProgramFragmentStore * pfs = 
+                    (ProgramFragmentStore *)rsc->lookupName(str[ct+1]);
+                if (pfs != NULL) {
+                    mEnviroment.mFragmentStore.set(pfs);
+                    continue;
+                }
+                LOGE("Unreconized value %s passed to stateFragmentStore", str[ct+1]);
+            }
+
+        }
+
+            
+    } else {
+        // Deal with an error.
+    }
+
+}
+
+namespace android {
+namespace renderscript {
+
+void rsi_ScriptCBegin(Context * rsc)
+{
+    ScriptCState *ss = &rsc->mScriptC;
+    ss->clear();
+}
+
+void rsi_ScriptCSetClearColor(Context * rsc, float r, float g, float b, float a)
+{
+    ScriptCState *ss = &rsc->mScriptC;
+    ss->mEnviroment.mClearColor[0] = r;
+    ss->mEnviroment.mClearColor[1] = g;
+    ss->mEnviroment.mClearColor[2] = b;
+    ss->mEnviroment.mClearColor[3] = a;
+}
+
+void rsi_ScriptCSetClearDepth(Context * rsc, float v)
+{
+    ScriptCState *ss = &rsc->mScriptC;
+    ss->mEnviroment.mClearDepth = v;
+}
+
+void rsi_ScriptCSetClearStencil(Context * rsc, uint32_t v)
+{
+    ScriptCState *ss = &rsc->mScriptC;
+    ss->mEnviroment.mClearStencil = v;
+}
+
+void rsi_ScriptCAddType(Context * rsc, RsType vt)
+{
+    ScriptCState *ss = &rsc->mScriptC;
+    ss->mConstantBufferTypes.add(static_cast<const Type *>(vt));
+}
+
+void rsi_ScriptCSetScript(Context * rsc, void *vp)
+{
+    ScriptCState *ss = &rsc->mScriptC;
+    ss->mProgram.mScript = reinterpret_cast<rsc_RunScript>(vp);
+}
+
+void rsi_ScriptCSetRoot(Context * rsc, bool isRoot)
+{
+    ScriptCState *ss = &rsc->mScriptC;
+    ss->mEnviroment.mIsRoot = isRoot;
+}
+
+void rsi_ScriptCSetText(Context *rsc, const char *text, uint32_t len)
+{
+    ScriptCState *ss = &rsc->mScriptC;
+    ss->mProgram.mScriptText = text;
+    ss->mProgram.mScriptTextLength = len;
+}
+
+
+RsScript rsi_ScriptCCreate(Context * rsc)
+{
+    ScriptCState *ss = &rsc->mScriptC;
+
+    ss->runCompiler(rsc);
+
+    ScriptC *s = new ScriptC();
+    s->incRef();
+    s->mAccScript = ss->mAccScript;
+    ss->mAccScript = NULL;
+    s->mEnviroment = ss->mEnviroment;
+    s->mProgram = ss->mProgram;
+    ss->clear();
+
+    return s;
+}
+
+}
+}
+
+
diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h
new file mode 100644
index 0000000..c46901b1
--- /dev/null
+++ b/libs/rs/rsScriptC.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_SCRIPT_C_H
+#define ANDROID_RS_SCRIPT_C_H
+
+#include "rsScript.h"
+
+#include "RenderScriptEnv.h"
+
+struct ACCscript;
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+
+class ScriptC : public Script
+{
+public:
+
+    ScriptC();
+    virtual ~ScriptC();
+
+    struct Program_t {
+        const char * mScriptText;
+        uint32_t mScriptTextLength;
+
+
+        int mVersionMajor;
+        int mVersionMinor;
+
+        rsc_RunScript mScript;
+    };
+
+    Program_t mProgram;
+
+    ACCscript*    mAccScript;
+
+    virtual bool run(Context *, uint32_t launchID);
+};
+
+class ScriptCState
+{
+public:
+    ScriptCState();
+    ~ScriptCState();
+
+    ACCscript* mAccScript;
+
+    ScriptC::Program_t mProgram;
+    Script::Enviroment_t mEnviroment;
+
+    Vector<const Type *> mConstantBufferTypes;
+
+    void clear();
+    void runCompiler(Context *rsc);
+};
+
+
+}
+}
+#endif
+
+
+
diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp
new file mode 100644
index 0000000..89df59d
--- /dev/null
+++ b/libs/rs/rsThreadIO.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+#include "rsThreadIO.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+ThreadIO *android::renderscript::gIO = NULL;
+
+ThreadIO::ThreadIO()
+{
+    mToCore.init(16 * 1024);
+}
+
+ThreadIO::~ThreadIO()
+{
+}
+
+bool ThreadIO::playCoreCommands(Context *con, bool waitForCommand)
+{
+    uint32_t cmdID = 0;
+    uint32_t cmdSize = 0;
+    bool ret = false;
+    while(!mToCore.isEmpty() || waitForCommand) {
+        ret = true;
+        const void * data = mToCore.get(&cmdID, &cmdSize);
+        waitForCommand = false;
+        //LOGV("playCoreCommands 3 %i %i", cmdID, cmdSize);
+
+        gPlaybackFuncs[cmdID](con, data);
+        mToCore.next();
+    }
+    return ret;
+}
+
+
diff --git a/libs/rs/rsThreadIO.h b/libs/rs/rsThreadIO.h
new file mode 100644
index 0000000..72746c5
--- /dev/null
+++ b/libs/rs/rsThreadIO.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_THREAD_IO_H
+#define ANDROID_RS_THREAD_IO_H
+
+#include "rsUtils.h"
+#include "rsLocklessFifo.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class Context;
+
+class ThreadIO {
+public:
+    ThreadIO();
+    ~ThreadIO();
+
+    // Plays back commands from the client.
+    // Returns true if any commands were processed.
+    bool playCoreCommands(Context *con, bool waitForCommand);
+
+
+    LocklessCommandFifo mToCore;
+    //LocklessCommandFifo mToClient;
+
+    intptr_t mToCoreRet;
+
+};
+
+extern ThreadIO *gIO;
+
+
+
+}
+}
+#endif
+
diff --git a/libs/rs/rsTriangleMesh.cpp b/libs/rs/rsTriangleMesh.cpp
new file mode 100644
index 0000000..8c7bc92
--- /dev/null
+++ b/libs/rs/rsTriangleMesh.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+TriangleMesh::TriangleMesh()
+{
+    mVertexElement = NULL;
+    mIndexElement = NULL;
+    mVertexData = NULL;
+    mIndexData = NULL;
+    mTriangleCount = 0;
+    mVertexDataSize = 0;
+    mIndexDataSize = 0;
+
+    mBufferObjects[0] = 0;
+    mBufferObjects[1] = 0;
+
+    mOffsetCoord = 0;
+    mOffsetTex = 0;
+    mOffsetNorm = 0;
+
+    mSizeCoord = 0;
+    mSizeTex = 0;
+    mSizeNorm = 0;
+
+}
+
+TriangleMesh::~TriangleMesh()
+{
+    free(mVertexData);
+    free(mIndexData);
+}
+
+
+
+TriangleMeshContext::TriangleMeshContext()
+{
+    clear();
+}
+
+TriangleMeshContext::~TriangleMeshContext()
+{
+}
+
+void TriangleMeshContext::clear()
+{
+    mVertexElement = NULL;
+    mVertexSizeBits = 0;
+    mIndexElement = NULL;
+    mIndexSizeBits = 0;
+    mTriangleCount = 0;
+    mVertexData.clear();
+    mIndexData.clear();
+}
+
+void TriangleMesh::analyzeElement()
+{
+    for (uint32_t ct=0; ct < mVertexElement->getComponentCount(); ct++) {
+        const Component *c = mVertexElement->getComponent(ct);
+
+        if (c->getKind() == Component::X) {
+            rsAssert(mSizeCoord == 0);
+            mSizeCoord = 1;
+            mOffsetCoord = ct;
+        }
+        if (c->getKind() == Component::Y) {
+            rsAssert(mSizeCoord == 1);
+            mSizeCoord = 2;
+        }
+        if (c->getKind() == Component::Z) {
+            rsAssert(mSizeCoord == 2);
+            mSizeCoord = 3;
+        }
+        if (c->getKind() == Component::W) {
+            rsAssert(mSizeCoord == 4);
+            mSizeCoord = 4;
+        }
+
+        if (c->getKind() == Component::NX) {
+            rsAssert(mSizeNorm == 0);
+            mSizeNorm = 1;
+            mOffsetNorm = ct;
+        }
+        if (c->getKind() == Component::NY) {
+            rsAssert(mSizeNorm == 1);
+            mSizeNorm = 2;
+        }
+        if (c->getKind() == Component::NZ) {
+            rsAssert(mSizeNorm == 2);
+            mSizeNorm = 3;
+        }
+
+        if (c->getKind() == Component::S) {
+            rsAssert(mSizeTex == 0);
+            mSizeTex = 1;
+            mOffsetTex = ct;
+        }
+        if (c->getKind() == Component::T) {
+            rsAssert(mSizeTex == 1);
+            mSizeTex = 2;
+        }
+    }
+    LOGV("TriangleMesh %i,%i  %i,%i  %i,%i", mSizeCoord, mOffsetCoord, mSizeNorm, mOffsetNorm, mSizeTex, mOffsetTex);
+
+}
+
+
+namespace android {
+namespace renderscript {
+
+void rsi_TriangleMeshBegin(Context *rsc, RsElement vertex, RsElement index)
+{
+    TriangleMeshContext *tmc = &rsc->mStateTriangleMesh;
+
+    tmc->clear();
+    tmc->mVertexElement = static_cast<Element *>(vertex);
+    tmc->mVertexSizeBits = tmc->mVertexElement->getSizeBits();
+    tmc->mIndexElement = static_cast<Element *>(index);
+    tmc->mIndexSizeBits = tmc->mIndexElement->getSizeBits();
+
+    assert(!(tmc->mVertexSizeBits & 0x7));
+    assert(!(tmc->mIndexSizeBits & 0x7));
+}
+
+void rsi_TriangleMeshAddVertex(Context *rsc, const void *data)
+{
+    TriangleMeshContext *tmc = &rsc->mStateTriangleMesh;
+
+    // todo: Make this efficient.
+    for (uint32_t ct = 0; (ct * 8) < tmc->mVertexSizeBits; ct++) {
+        tmc->mVertexData.add(static_cast<const uint8_t *>(data) [ct]);
+    }
+}
+
+void rsi_TriangleMeshAddTriangle(Context *rsc, uint32_t idx1, uint32_t idx2, uint32_t idx3)
+{
+    TriangleMeshContext *tmc = &rsc->mStateTriangleMesh;
+
+    // todo: Make this efficient.
+    switch(tmc->mIndexSizeBits) {
+    case 16:
+        tmc->mIndexData.add(idx1);
+        tmc->mIndexData.add(idx2);
+        tmc->mIndexData.add(idx3);
+        break;
+    default:
+        assert(0);
+    }
+
+    tmc->mTriangleCount++;
+}
+
+RsTriangleMesh rsi_TriangleMeshCreate(Context *rsc)
+{
+    TriangleMeshContext *tmc = &rsc->mStateTriangleMesh;
+
+    TriangleMesh * tm = new TriangleMesh();
+    if (!tm) {
+        LOGE("rsTriangleMeshCreate: Error OUT OF MEMORY");
+        // error
+        return 0;
+    }
+
+    tm->mTriangleCount = tmc->mTriangleCount;
+    tm->mIndexDataSize = tmc->mIndexData.size() * tmc->mIndexSizeBits >> 3;
+    tm->mVertexDataSize = tmc->mVertexData.size();
+    tm->mIndexElement = tmc->mIndexElement;
+    tm->mVertexElement = tmc->mVertexElement;
+
+    tm->mIndexData = malloc(tm->mIndexDataSize);
+    tm->mVertexData = malloc(tm->mVertexDataSize);
+    if (!tm->mIndexData || !tm->mVertexData) {
+        LOGE("rsTriangleMeshCreate: Error OUT OF MEMORY");
+        delete tm;
+        return 0;
+    }
+
+    memcpy(tm->mVertexData, tmc->mVertexData.array(), tm->mVertexDataSize);
+    memcpy(tm->mIndexData, tmc->mIndexData.array(), tm->mIndexDataSize);
+    tm->analyzeElement();
+
+    return tm;
+}
+
+void rsi_TriangleMeshDestroy(Context *rsc, RsTriangleMesh vtm)
+{
+    TriangleMeshContext *tmc = &rsc->mStateTriangleMesh;
+    TriangleMesh * tm = static_cast<TriangleMesh *>(vtm);
+
+    free(tm->mIndexData);
+    free(tm->mVertexData);
+    delete tm;
+}
+
+
+
+void rsi_TriangleMeshRenderRange(Context *rsc, RsTriangleMesh vtm, uint32_t first, uint32_t count)
+{
+    TriangleMesh * tm = static_cast<TriangleMesh *>(vtm);
+
+    rsc->setupCheck();
+
+    if (!tm->mBufferObjects[0]) {
+        glGenBuffers(2, &tm->mBufferObjects[0]);
+
+        glBindBuffer(GL_ARRAY_BUFFER, tm->mBufferObjects[0]);
+        glBufferData(GL_ARRAY_BUFFER, tm->mVertexDataSize, tm->mVertexData, GL_STATIC_DRAW);
+        glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, tm->mIndexDataSize, tm->mIndexData, GL_STATIC_DRAW);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+    }
+
+    if (first >= tm->mTriangleCount) {
+        return;
+    }
+    if (count >= (tm->mTriangleCount - first)) {
+        count = tm->mTriangleCount - first;
+    }
+    if (!count) {
+        return;
+    }
+
+    const float *f = (const float *)tm->mVertexData;
+
+    glBindBuffer(GL_ARRAY_BUFFER, tm->mBufferObjects[0]);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glVertexPointer(tm->mSizeCoord, 
+                    GL_FLOAT, 
+                    tm->mVertexElement->getSizeBytes(), 
+                    (void *)tm->mVertexElement->getComponentOffsetBytes(tm->mOffsetCoord));
+
+    if (tm->mSizeTex) {
+        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+        glTexCoordPointer(tm->mSizeTex, 
+                          GL_FLOAT, 
+                          tm->mVertexElement->getSizeBytes(), 
+                          (void *)tm->mVertexElement->getComponentOffsetBytes(tm->mOffsetTex));
+    } else {
+        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    }
+
+    if (tm->mSizeNorm) {
+        glEnableClientState(GL_NORMAL_ARRAY);
+        glNormalPointer(GL_FLOAT, 
+                        tm->mVertexElement->getSizeBytes(), 
+                        (void *)tm->mVertexElement->getComponentOffsetBytes(tm->mOffsetNorm));
+    } else {
+        glDisableClientState(GL_NORMAL_ARRAY);
+    }
+
+    glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_SHORT, (GLvoid *)(first * 3 * 2));
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+void rsi_TriangleMeshRender(Context *rsc, RsTriangleMesh vtm)
+{
+    rsi_TriangleMeshRenderRange(rsc, vtm, 0, 0xffffff);
+}
+
+}}
diff --git a/libs/rs/rsTriangleMesh.h b/libs/rs/rsTriangleMesh.h
new file mode 100644
index 0000000..e56c7c2
--- /dev/null
+++ b/libs/rs/rsTriangleMesh.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_TRIANGLE_MESH_H
+#define ANDROID_RS_TRIANGLE_MESH_H
+
+
+#include "RenderScript.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+// An element is a group of Components that occupies one cell in a structure.
+class TriangleMesh : public ObjectBase
+{
+public:
+    TriangleMesh();
+    ~TriangleMesh();
+
+    const Element * mVertexElement;
+    const Element * mIndexElement;
+
+    void * mVertexData;
+    void * mIndexData;
+
+    size_t mVertexDataSize;
+    size_t mIndexDataSize;
+    uint32_t mTriangleCount;
+
+    size_t mOffsetCoord;
+    size_t mOffsetTex;
+    size_t mOffsetNorm;
+
+    size_t mSizeCoord;
+    size_t mSizeTex;
+    size_t mSizeNorm;
+
+    // GL buffer info
+    uint32_t mBufferObjects[2];
+
+    void analyzeElement();
+protected:
+};
+
+class TriangleMeshContext
+{
+public:
+    TriangleMeshContext();
+    ~TriangleMeshContext();
+
+    const Element * mVertexElement;
+    const Element * mIndexElement;
+    size_t mVertexSizeBits;
+    size_t mIndexSizeBits;
+
+    Vector<uint8_t> mVertexData; 
+    Vector<uint16_t> mIndexData; 
+
+    uint32_t mTriangleCount;
+
+    void clear();
+};
+
+
+}
+}
+#endif //ANDROID_RS_TRIANGLE_MESH_H
+
diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp
new file mode 100644
index 0000000..bbe9720
--- /dev/null
+++ b/libs/rs/rsType.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+Type::Type()
+{
+    mLODs = 0;
+    mLODCount = 0;
+    clear();
+}
+
+Type::~Type()
+{
+    if (mLODs) {
+        delete [] mLODs;
+    }
+}
+
+void Type::clear()
+{
+    if (mLODs) {
+        delete [] mLODs;
+        mLODs = NULL;
+    }
+    mDimX = 0;
+    mDimY = 0;
+    mDimZ = 0;
+    mDimLOD = 0;
+    mFaces = false;
+    mElement.clear();
+}
+
+TypeState::TypeState()
+{
+}
+
+TypeState::~TypeState()
+{
+}
+
+size_t Type::getOffsetForFace(uint32_t face) const
+{
+    rsAssert(mFaces);
+    return 0;
+}
+
+void Type::compute()
+{
+    uint32_t oldLODCount = mLODCount;
+    if (mDimLOD) {
+        uint32_t l2x = rsFindHighBit(mDimX) + 1;
+        uint32_t l2y = rsFindHighBit(mDimY) + 1;
+        uint32_t l2z = rsFindHighBit(mDimZ) + 1;
+
+        mLODCount = rsMax(l2x, l2y);
+        mLODCount = rsMax(mLODCount, l2z);
+    } else {
+        mLODCount = 1;
+    }
+    if (mLODCount != oldLODCount) {
+        delete [] mLODs;
+        mLODs = new LOD[mLODCount];
+    }
+
+    uint32_t tx = mDimX;
+    uint32_t ty = mDimY;
+    uint32_t tz = mDimZ;
+    size_t offset = 0;
+    for (uint32_t lod=0; lod < mLODCount; lod++) {
+        mLODs[lod].mX = tx;
+        mLODs[lod].mY = ty;
+        mLODs[lod].mZ = tz;
+        mLODs[lod].mOffset = offset;
+        offset += tx * rsMax(ty, 1u) * rsMax(tz, 1u) * mElement->getSizeBytes();
+        tx = (tx + 1) >> 1;
+        ty = (ty + 1) >> 1;
+        tz = (tz + 1) >> 1;
+    }
+
+    // At this point the offset is the size of a mipmap chain;
+    mMipChainSizeBytes = offset;
+
+    if (mFaces) {
+        offset *= 6;
+    }
+    mTotalSizeBytes = offset;
+
+}
+
+uint32_t Type::getLODOffset(uint32_t lod, uint32_t x) const
+{
+    uint32_t offset = mLODs[lod].mOffset;
+    offset += x * mElement->getSizeBytes();
+    return offset;
+}
+
+uint32_t Type::getLODOffset(uint32_t lod, uint32_t x, uint32_t y) const
+{
+    uint32_t offset = mLODs[lod].mOffset;
+    offset += (x + y * mLODs[lod].mX) * mElement->getSizeBytes();
+    return offset;
+}
+
+uint32_t Type::getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const
+{
+    uint32_t offset = mLODs[lod].mOffset;
+    offset += (x + y*mLODs[lod].mX + z*mLODs[lod].mX*mLODs[lod].mY) * mElement->getSizeBytes();
+    return offset;
+}
+
+
+//////////////////////////////////////////////////
+// 
+namespace android {
+namespace renderscript {
+
+void rsi_TypeBegin(Context *rsc, RsElement vse)
+{
+    TypeState * stc = &rsc->mStateType;
+
+    stc->mX = 0;
+    stc->mY = 0;
+    stc->mZ = 0;
+    stc->mLOD = false;
+    stc->mFaces = false;
+    stc->mElement.set(static_cast<const Element *>(vse));
+}
+
+void rsi_TypeAdd(Context *rsc, RsDimension dim, size_t value)
+{
+    TypeState * stc = &rsc->mStateType;
+
+    if (dim < 0) {
+        //error
+        return;
+    }
+
+
+    switch (dim) {
+    case RS_DIMENSION_X:
+        stc->mX = value;
+        return;
+    case RS_DIMENSION_Y:
+        stc->mY = value;
+        return;
+    case RS_DIMENSION_Z:
+        stc->mZ = value;
+        return;
+    case RS_DIMENSION_FACE:
+        stc->mFaces = (value != 0);
+        return;
+    case RS_DIMENSION_LOD:
+        stc->mLOD = (value != 0);
+        return;
+    default:
+        break;
+    }
+
+
+    int32_t arrayNum = dim - RS_DIMENSION_ARRAY_0;
+    if ((dim < 0) || (dim > RS_DIMENSION_MAX)) {
+        LOGE("rsTypeAdd: Bad dimension");
+        //error
+        return;
+    }
+
+    // todo: implement array support
+
+}
+
+RsType rsi_TypeCreate(Context *rsc)
+{
+    TypeState * stc = &rsc->mStateType;
+
+    Type * st = new Type();
+    st->setDimX(stc->mX);
+    st->setDimY(stc->mY);
+    st->setDimZ(stc->mZ);
+    st->setElement(stc->mElement.get());
+    st->setDimLOD(stc->mLOD);
+    st->setDimFaces(stc->mFaces);
+    st->compute();
+
+    stc->mAllTypes.add(st);
+
+    return st;
+}
+
+void rsi_TypeDestroy(Context *rsc, RsType vst)
+{
+    TypeState * stc = &rsc->mStateType;
+    Type * st = static_cast<Type *>(vst);
+
+    for (size_t ct = 0; ct < stc->mAllTypes.size(); ct++) {
+        if (stc->mAllTypes[ct] == st) {
+            stc->mAllTypes.removeAt(ct);
+            break;
+        }
+    }
+    delete st;
+}
+
+}
+}
+
diff --git a/libs/rs/rsType.h b/libs/rs/rsType.h
new file mode 100644
index 0000000..a717893
--- /dev/null
+++ b/libs/rs/rsType.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STRUCTURED_TYPE_H
+#define ANDROID_STRUCTURED_TYPE_H
+
+#include "rsElement.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+    
+class Type : public ObjectBase
+{
+public:
+    Type();
+    virtual ~Type();
+
+    Type * createTex2D(const Element *, size_t w, size_t h, bool mip);
+
+
+    size_t getOffsetForFace(uint32_t face) const;
+
+    size_t getSizeBytes() const {return mTotalSizeBytes;}
+    size_t getElementSizeBytes() const {return mElement->getSizeBytes();}
+    const Element * getElement() const {return mElement.get();}
+
+    uint32_t getDimX() const {return mDimX;}
+    uint32_t getDimY() const {return mDimY;}
+    uint32_t getDimZ() const {return mDimZ;}
+    uint32_t getDimLOD() const {return mDimLOD;}
+    bool getDimFaces() const {return mFaces;}
+
+    uint32_t getLODDimX(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mX;}
+    uint32_t getLODDimY(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mY;}
+    uint32_t getLODDimZ(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mZ;}
+    uint32_t getLODOffset(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mOffset;}
+
+    uint32_t getLODOffset(uint32_t lod, uint32_t x) const;
+    uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y) const;
+    uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const;
+
+    uint32_t getLODCount() const {return mLODCount;}
+
+
+    void setElement(const Element *e) {mElement.set(e);}
+    void setDimX(uint32_t v) {mDimX = v;}
+    void setDimY(uint32_t v) {mDimY = v;}
+    void setDimZ(uint32_t v) {mDimZ = v;}
+    void setDimFaces(bool v) {mFaces = v;}
+    void setDimLOD(bool v) {mDimLOD = v;}
+
+    void clear();
+    void compute();
+
+
+protected:
+    struct LOD {
+        size_t mX;
+        size_t mY;
+        size_t mZ;
+        size_t mOffset;
+    };
+
+    void makeLODTable();
+
+    // Internal structure from most to least significant.
+    // * Array dimensions
+    // * Faces
+    // * Mipmaps
+    // * xyz
+
+    ObjectBaseRef<const Element> mElement;
+
+    // Size of the structure in the various dimensions.  A missing Dimension is
+    // specified as a 0 and not a 1.
+    size_t mDimX;
+    size_t mDimY;
+    size_t mDimZ;
+    bool mDimLOD;
+    bool mFaces;
+
+    // A list of array dimensions.  The count is the number of array dimensions and the 
+    // sizes is a per array size.
+    //Vector<size_t> mDimArraysSizes;
+
+    // count of mipmap levels, 0 indicates no mipmapping
+
+    size_t mMipChainSizeBytes;
+    size_t mTotalSizeBytes;
+    LOD *mLODs;
+    uint32_t mLODCount;
+
+private:
+    Type(const Type &);
+};
+
+
+class TypeState {
+public:
+    TypeState();
+    ~TypeState();
+
+    Vector<Type *> mAllTypes;
+
+    size_t mX;
+    size_t mY;
+    size_t mZ;
+    uint32_t mLOD;
+    bool mFaces;
+    ObjectBaseRef<const Element> mElement;
+
+
+    
+};
+
+
+}
+}
+#endif //ANDROID_STRUCTURED_TYPE
diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h
new file mode 100644
index 0000000..3d3437b
--- /dev/null
+++ b/libs/rs/rsUtils.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_UTILS_H
+#define ANDROID_RS_UTILS_H
+
+#define LOG_NDEBUG 0 
+#define LOG_TAG "rs"
+#include <utils/Log.h>
+#include <utils/Vector.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#include "RenderScript.h"
+
+namespace android {
+namespace renderscript {
+
+#if 1
+#define rsAssert(v) do {if(!(v)) LOGE("rsAssert failed: %s, in %s at %i", #v, __FILE__, __LINE__);} while(0)
+#else
+#define rsAssert(v) while(0)
+#endif
+
+template<typename T>
+T rsMin(T in1, T in2)
+{
+    if (in1 > in2) {
+        return in2;
+    }
+    return in1;
+}
+
+template<typename T>
+T rsMax(T in1, T in2)
+{
+    if (in1 < in2) {
+        return in2;
+    }
+    return in1;
+}
+
+template<typename T>
+T rsFindHighBit(T val)
+{
+    uint32_t bit = 0;
+    while(val > 1) {
+        bit++;
+        val>>=1;
+    }
+    return bit;
+}
+
+template<typename T>
+bool rsIsPow2(T val)
+{
+    return (val & (val-1)) == 0;
+}
+
+template<typename T>
+T rsHigherPow2(T v)
+{
+    if (rsIsPow2(v)) {
+        return v;
+    }
+    return 1 << (rsFindHighBit(v) + 1);
+}
+
+template<typename T>
+T rsLowerPow2(T v)
+{
+    if (rsIsPow2(v)) {
+        return v;
+    }
+    return 1 << rsFindHighBit(v);
+}
+
+
+static inline uint16_t rs888to565(uint32_t r, uint32_t g, uint32_t b)
+{
+    uint16_t t = 0;
+    t |= b >> 3;
+    t |= (g >> 2) << 5;
+    t |= (r >> 3) << 11;
+    return t;
+}
+
+static inline uint16_t rsBoxFilter565(uint16_t i1, uint16_t i2, uint16_t i3, uint16_t i4)
+{
+    uint32_t r = ((i1 & 0x1f) + (i2 & 0x1f) + (i3 & 0x1f) + (i4 & 0x1f));
+    uint32_t g = ((i1 >> 5) & 0x3f) + ((i2 >> 5) & 0x3f) + ((i3 >> 5) & 0x3f) + ((i4 >> 5) & 0x3f);
+    uint32_t b = ((i1 >> 11) + (i2 >> 11) + (i3 >> 11) + (i4 >> 11));
+    return (r >> 2) | ((g >> 2) << 5) | ((b >> 2) << 11);
+}
+
+static inline uint32_t rsBoxFilter8888(uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4)
+{
+    uint32_t r = (i1 & 0xff) +         (i2 & 0xff) +         (i3 & 0xff) +         (i4 & 0xff);
+    uint32_t g = ((i1 >> 8) & 0xff) +  ((i2 >> 8) & 0xff) +  ((i3 >> 8) & 0xff) +  ((i4 >> 8) & 0xff);
+    uint32_t b = ((i1 >> 16) & 0xff) + ((i2 >> 16) & 0xff) + ((i3 >> 16) & 0xff) + ((i4 >> 16) & 0xff);
+    uint32_t a = ((i1 >> 24) & 0xff) + ((i2 >> 24) & 0xff) + ((i3 >> 24) & 0xff) + ((i4 >> 24) & 0xff);
+    return (r >> 2) | ((g >> 2) << 8) | ((b >> 2) << 16) | ((a >> 2) << 24);
+}
+
+
+
+}
+}
+
+#endif //ANDROID_RS_OBJECT_BASE_H
+
+
diff --git a/libs/rs/rsgApi.cpp.rsg b/libs/rs/rsgApi.cpp.rsg
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/libs/rs/rsgApi.cpp.rsg
@@ -0,0 +1 @@
+2
diff --git a/libs/rs/rsgApiFuncDecl.h.rsg b/libs/rs/rsgApiFuncDecl.h.rsg
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/libs/rs/rsgApiFuncDecl.h.rsg
@@ -0,0 +1 @@
+1
diff --git a/libs/rs/rsgApiReplay.cpp.rsg b/libs/rs/rsgApiReplay.cpp.rsg
new file mode 100644
index 0000000..00750ed
--- /dev/null
+++ b/libs/rs/rsgApiReplay.cpp.rsg
@@ -0,0 +1 @@
+3
diff --git a/libs/rs/rsgApiStructs.h.rsg b/libs/rs/rsgApiStructs.h.rsg
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/libs/rs/rsgApiStructs.h.rsg
@@ -0,0 +1 @@
+0
diff --git a/libs/rs/rsg_generator.c b/libs/rs/rsg_generator.c
new file mode 100644
index 0000000..a4d659d
--- /dev/null
+++ b/libs/rs/rsg_generator.c
@@ -0,0 +1,291 @@
+
+
+#include "lex.yy.c"
+
+void printFileHeader(FILE *f)
+{
+    fprintf(f, "/*\n");
+    fprintf(f, " * Copyright (C) 2009 The Android Open Source Project\n");
+    fprintf(f, " *\n");
+    fprintf(f, " * Licensed under the Apache License, Version 2.0 (the \"License\");\n");
+    fprintf(f, " * you may not use this file except in compliance with the License.\n");
+    fprintf(f, " * You may obtain a copy of the License at\n");
+    fprintf(f, " *\n");
+    fprintf(f, " *      http://www.apache.org/licenses/LICENSE-2.0\n");
+    fprintf(f, " *\n");
+    fprintf(f, " * Unless required by applicable law or agreed to in writing, software\n");
+    fprintf(f, " * distributed under the License is distributed on an \"AS IS\" BASIS,\n");
+    fprintf(f, " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n");
+    fprintf(f, " * See the License for the specific language governing permissions and\n");
+    fprintf(f, " * limitations under the License.\n");
+    fprintf(f, " */\n\n");
+}
+
+void printVarType(FILE *f, const VarType *vt)
+{
+    int ct;
+    if (vt->isConst) {
+        fprintf(f, "const ");
+    }
+
+    switch(vt->type) {
+    case 0:
+        fprintf(f, "void");
+        break;
+    case 1:
+        fprintf(f, "int%i_t", vt->bits);
+        break;
+    case 2:
+        fprintf(f, "uint%i_t", vt->bits);
+        break;
+    case 3:
+        if (vt->bits == 32)
+            fprintf(f, "float");
+        else
+            fprintf(f, "double");
+        break;
+    case 4:
+        fprintf(f, "%s", vt->typename);
+        break;
+    }
+
+    if(vt->ptrLevel) {
+        fprintf(f, " ");
+        for(ct=0; ct < vt->ptrLevel; ct++) {
+            fprintf(f, "*");
+        }
+    }
+
+    if(vt->name[0]) {
+        fprintf(f, " %s", vt->name);
+    }
+}
+
+void printArgList(FILE *f, const ApiEntry * api, int assumePrevious)
+{
+    int ct;
+    for(ct=0; ct < api->paramCount; ct++) {
+        if (ct || assumePrevious) {
+            fprintf(f, ", ");
+        }
+        printVarType(f, &api->params[ct]);
+    }
+}
+
+void printStructures(FILE *f)
+{
+    int ct;
+    int ct2;
+
+    for(ct=0; ct < apiCount; ct++) {
+        fprintf(f, "typedef struct RS_CMD_%s_rec RS_CMD_%s;\n", apis[ct].name, apis[ct].name);
+    }
+    fprintf(f, "\n");
+
+    for(ct=0; ct < apiCount; ct++) {
+        const ApiEntry * api = &apis[ct];
+        fprintf(f, "#define RS_CMD_ID_%s %i\n", api->name, ct+1);
+        fprintf(f, "struct RS_CMD_%s_rec {\n", api->name);
+        //fprintf(f, "    RsCommandHeader _hdr;\n");
+
+        for(ct2=0; ct2 < api->paramCount; ct2++) {
+            fprintf(f, "    ");
+            printVarType(f, &api->params[ct2]);
+            fprintf(f, ";\n");
+        }
+        fprintf(f, "};\n\n");
+    }
+}
+
+void printFuncDecl(FILE *f, const ApiEntry *api, const char *prefix, int addContext)
+{
+    printVarType(f, &api->ret);
+    fprintf(f, " %s%s (", prefix, api->name);
+    if (addContext) {
+        fprintf(f, "Context *");
+    }
+    printArgList(f, api, addContext);
+    fprintf(f, ")");
+}
+
+void printFuncDecls(FILE *f, const char *prefix, int addContext)
+{
+    int ct;
+    for(ct=0; ct < apiCount; ct++) {
+        printFuncDecl(f, &apis[ct], prefix, addContext);
+        fprintf(f, ";\n");
+    }
+    fprintf(f, "\n\n");
+}
+
+void printPlaybackFuncs(FILE *f, const char *prefix)
+{
+    int ct;
+    for(ct=0; ct < apiCount; ct++) {
+        fprintf(f, "void %s%s (Context *, const void *);\n", prefix, apis[ct].name);
+    }
+}
+
+void printApiCpp(FILE *f)
+{
+    int ct;
+    int ct2;
+
+    fprintf(f, "#include \"rsDevice.h\"\n");
+    fprintf(f, "#include \"rsContext.h\"\n");
+    fprintf(f, "#include \"rsThreadIO.h\"\n");
+    //fprintf(f, "#include \"rsgApiStructs.h\"\n");
+    fprintf(f, "#include \"rsgApiFuncDecl.h\"\n");
+    fprintf(f, "\n");
+    fprintf(f, "using namespace android;\n");
+    fprintf(f, "using namespace android::renderscript;\n");
+    fprintf(f, "\n");
+
+    for(ct=0; ct < apiCount; ct++) {
+        int needFlush = 0;
+        const ApiEntry * api = &apis[ct];
+
+        printFuncDecl(f, api, "rs", 0);
+        fprintf(f, "\n{\n");
+        fprintf(f, "    ThreadIO *io = gIO;\n");
+        //fprintf(f, "    LOGE(\"add command %s\\n\");\n", api->name);
+        fprintf(f, "    RS_CMD_%s *cmd = static_cast<RS_CMD_%s *>(io->mToCore.reserve(sizeof(RS_CMD_%s)));\n", api->name, api->name, api->name);
+        fprintf(f, "    uint32_t size = sizeof(RS_CMD_%s);\n", api->name);
+
+        for(ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
+            needFlush += vt->ptrLevel;
+            fprintf(f, "    cmd->%s = %s;\n", vt->name, vt->name);
+        }
+        if (api->ret.typename[0]) {
+            needFlush = 1;
+        }
+
+        fprintf(f, "    io->mToCore.commit");
+        if (needFlush) {
+            fprintf(f, "Sync");
+        }
+        fprintf(f, "(RS_CMD_ID_%s, size);\n", api->name);
+
+        if (api->ret.typename[0]) {
+            fprintf(f, "    return reinterpret_cast<");
+            printVarType(f, &api->ret);
+            fprintf(f, ">(io->mToCoreRet);\n");
+        }
+        fprintf(f, "};\n\n");
+    }
+}
+
+void printPlaybackCpp(FILE *f)
+{
+    int ct;
+    int ct2;
+
+    fprintf(f, "#include \"rsDevice.h\"\n");
+    fprintf(f, "#include \"rsContext.h\"\n");
+    fprintf(f, "#include \"rsThreadIO.h\"\n");
+    //fprintf(f, "#include \"rsgApiStructs.h\"\n");
+    fprintf(f, "#include \"rsgApiFuncDecl.h\"\n");
+    fprintf(f, "\n");
+    fprintf(f, "namespace android {\n");
+    fprintf(f, "namespace renderscript {\n");
+    fprintf(f, "\n");
+
+    for(ct=0; ct < apiCount; ct++) {
+        const ApiEntry * api = &apis[ct];
+
+        fprintf(f, "void rsp_%s(Context *con, const void *vp)\n", api->name);
+        fprintf(f, "{\n");
+        //fprintf(f, "    LOGE(\"play command %s\\n\");\n", api->name);
+        fprintf(f, "    const RS_CMD_%s *cmd = static_cast<const RS_CMD_%s *>(vp);\n", api->name, api->name);
+        fprintf(f, "    ");
+        if (api->ret.typename[0]) {
+            fprintf(f, "gIO->mToCoreRet = (intptr_t)");
+        }
+        fprintf(f, "rsi_%s(con", api->name);
+        for(ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
+            fprintf(f, ",");
+            fprintf(f, "\n           cmd->%s", vt->name);
+        }
+        fprintf(f, ");\n");
+
+        fprintf(f, "};\n\n");
+    }
+
+    fprintf(f, "RsPlaybackFunc gPlaybackFuncs[] = {\n");
+    fprintf(f, "    NULL,\n");
+    for(ct=0; ct < apiCount; ct++) {
+        fprintf(f, "    %s%s,\n", "rsp_", apis[ct].name);
+    }
+    fprintf(f, "};\n");
+
+    fprintf(f, "};\n");
+    fprintf(f, "};\n");
+}
+
+int main(int argc, char **argv)
+{
+    if (argc != 3) {
+        fprintf(stderr, "usage: %s commandFile outFile\n", argv[0]);
+        return 1;
+    }
+    const char* rsgFile = argv[1];
+    const char* outFile = argv[2];
+    FILE* input = fopen(rsgFile, "r");
+
+    char choice = fgetc(input);
+    fclose(input);
+
+    if (choice < '0' || choice > '3') {
+        fprintf(stderr, "Uknown command: \'%c\'\n", choice);
+        return -2;
+    }
+
+    yylex();
+    // printf("# of lines = %d\n", num_lines);
+
+    FILE *f = fopen(outFile, "w");
+
+    printFileHeader(f);
+    switch(choice) {
+        case '0': // rsgApiStructs.h
+        {
+            fprintf(f, "\n");
+            fprintf(f, "#include \"rsContext.h\"\n");
+            fprintf(f, "\n");
+            fprintf(f, "namespace android {\n");
+            fprintf(f, "namespace renderscript {\n");
+            printStructures(f);
+            printFuncDecls(f, "rsi_", 1);
+            printPlaybackFuncs(f, "rsp_");
+            fprintf(f, "\n\ntypedef void (*RsPlaybackFunc)(Context *, const void *);\n");
+            fprintf(f, "extern RsPlaybackFunc gPlaybackFuncs[];\n");
+
+            fprintf(f, "}\n");
+            fprintf(f, "}\n");
+        }
+        break;
+
+        case '1': // rsgApiFuncDecl.h
+        {
+            printFuncDecls(f, "rs", 0);
+        }
+        break;
+
+        case '2': // rsgApi.cpp
+        {
+            printApiCpp(f);
+        }
+        break;
+
+        case '3': // rsgApiReplay.cpp
+        {
+            printFileHeader(f);
+            printPlaybackCpp(f);
+        }
+        break;
+    }
+    fclose(f);
+    return 0;
+}
diff --git a/libs/rs/spec.lex b/libs/rs/spec.lex
new file mode 100644
index 0000000..0f8e9ab
--- /dev/null
+++ b/libs/rs/spec.lex
@@ -0,0 +1,171 @@
+%option stack
+
+%x comment
+%x api_entry
+%x api_entry2
+%x api_entry_param
+%x var_type
+
+DIGIT    [0-9]
+ID       [a-zA-Z_][a-zA-Z0-9_]*
+
+
+   int num_lines = 0;
+
+   typedef struct {
+      int isConst;
+      int type;
+      int bits;
+      int ptrLevel;
+      char name[256];
+      char typename[256];
+   } VarType;
+
+   VarType *currType = 0;
+
+   typedef struct {
+      char name[256];
+      int sync;
+      int paramCount;
+      VarType ret;
+      VarType params[16];
+   } ApiEntry;
+
+   ApiEntry apis[128];
+   int apiCount = 0;
+
+   int typeNextState;
+
+%%
+
+"/*"         BEGIN(comment);
+<comment>[^*\n]*        /* eat anything that's not a '*' */
+<comment>"*"+[^*/\n]*   /* eat up '*'s not followed by '/'s */
+<comment>\n             ++num_lines;
+<comment>"*"+"/"        BEGIN(INITIAL);
+
+<*>" "   //printf("found ' '\n");
+<*>"\n"  ++num_lines; //printf("found lf \n");
+
+{ID} {
+    memset(&apis[apiCount], 0, sizeof(ApiEntry));
+    memcpy(apis[apiCount].name, yytext, yyleng);
+    BEGIN(api_entry);
+    }
+
+<api_entry>"{" {
+    BEGIN(api_entry2);
+    }
+
+<api_entry2>"sync" {
+    apis[apiCount].sync = 1;
+    }
+
+<api_entry2>"ret" {
+    currType = &apis[apiCount].ret;
+    typeNextState = api_entry2;
+    BEGIN(var_type);
+    }
+
+<api_entry2>"param" {
+    currType = &apis[apiCount].params[apis[apiCount].paramCount];
+    apis[apiCount].paramCount++;
+    typeNextState = api_entry_param;
+    BEGIN(var_type);
+    }
+
+<var_type>"const" {
+    currType->isConst = 1;
+    }
+
+<var_type>"i8" {
+    currType->type = 1;
+    currType->bits = 8;
+    BEGIN(typeNextState);
+    }
+
+<var_type>"i16" {
+    currType->type = 1;
+    currType->bits = 16;
+    BEGIN(typeNextState);
+    }
+
+<var_type>"i32" {
+    currType->type = 1;
+    currType->bits = 32;
+    BEGIN(typeNextState);
+    }
+
+<var_type>"i64" {
+    currType->type = 1;
+    currType->bits = 64;
+    BEGIN(typeNextState);
+    }
+
+<var_type>"u8" {
+    currType->type = 2;
+    currType->bits = 8;
+    BEGIN(typeNextState);
+    }
+
+<var_type>"u16" {
+    currType->type = 2;
+    currType->bits = 16;
+    BEGIN(typeNextState);
+    }
+
+<var_type>"u32" {
+    currType->type = 2;
+    currType->bits = 32;
+    BEGIN(typeNextState);
+    }
+
+<var_type>"u64" {
+    currType->type = 2;
+    currType->bits = 64;
+    BEGIN(typeNextState);
+    }
+
+<var_type>"f" {
+    currType->type = 3;
+    currType->bits = 32;
+    BEGIN(typeNextState);
+    }
+
+<var_type>"d" {
+    currType->type = 3;
+    currType->bits = 64;
+    BEGIN(typeNextState);
+    }
+
+<var_type>{ID} {
+    currType->type = 4;
+    currType->bits = 32;
+    memcpy(currType->typename, yytext, yyleng);
+    BEGIN(typeNextState);
+    }
+
+<api_entry_param>"*" {
+    currType->ptrLevel ++;
+    }
+
+<api_entry_param>{ID} {
+    memcpy(currType->name, yytext, yyleng);
+    BEGIN(api_entry2);
+    }
+
+
+<api_entry2>"}" {
+    apiCount++;
+    BEGIN(INITIAL);
+    }
+
+
+%%
+
+
+int yywrap()
+{
+    return 1;
+}
+
diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk
index 9272983..a5698f2 100644
--- a/libs/surfaceflinger/Android.mk
+++ b/libs/surfaceflinger/Android.mk
@@ -33,6 +33,7 @@
 LOCAL_SHARED_LIBRARIES := \
 	libhardware \
 	libutils \
+	libbinder \
 	libcutils \
 	libui \
 	libcorecg \
diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.cpp b/libs/surfaceflinger/GPUHardware/GPUHardware.cpp
index 7168bf2..2de628b 100644
--- a/libs/surfaceflinger/GPUHardware/GPUHardware.cpp
+++ b/libs/surfaceflinger/GPUHardware/GPUHardware.cpp
@@ -30,12 +30,12 @@
 #include <cutils/log.h>
 #include <cutils/properties.h>
 
-#include <utils/IBinder.h>
-#include <utils/MemoryDealer.h>
-#include <utils/MemoryBase.h>
-#include <utils/MemoryHeapPmem.h>
-#include <utils/MemoryHeapBase.h>
-#include <utils/IPCThreadState.h>
+#include <binder/IBinder.h>
+#include <binder/MemoryDealer.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapPmem.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/IPCThreadState.h>
 #include <utils/StopWatch.h>
 
 #include <ui/ISurfaceComposer.h>
diff --git a/libs/surfaceflinger/LayerBitmap.cpp b/libs/surfaceflinger/LayerBitmap.cpp
index 397ddc8..eb3c3e5 100644
--- a/libs/surfaceflinger/LayerBitmap.cpp
+++ b/libs/surfaceflinger/LayerBitmap.cpp
@@ -23,8 +23,8 @@
 #include <cutils/memory.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
-#include <utils/MemoryDealer.h>
-#include <utils/IMemory.h>
+#include <binder/MemoryDealer.h>
+#include <binder/IMemory.h>
 #include <ui/PixelFormat.h>
 #include <pixelflinger/pixelflinger.h>
 
diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp
index 00fab70..bac544a 100644
--- a/libs/surfaceflinger/LayerBuffer.cpp
+++ b/libs/surfaceflinger/LayerBuffer.cpp
@@ -25,8 +25,8 @@
 #include <utils/Log.h>
 #include <utils/StopWatch.h>
 
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
 
 #include <ui/PixelFormat.h>
 #include <ui/EGLDisplaySurface.h>
diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h
index 2dc77f1..f74d6a1 100644
--- a/libs/surfaceflinger/LayerBuffer.h
+++ b/libs/surfaceflinger/LayerBuffer.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 #include <private/ui/LayerState.h>
 #include <EGL/eglnatives.h>
 
diff --git a/libs/surfaceflinger/LayerOrientationAnim.h b/libs/surfaceflinger/LayerOrientationAnim.h
index 365c6ae..e86156d 100644
--- a/libs/surfaceflinger/LayerOrientationAnim.h
+++ b/libs/surfaceflinger/LayerOrientationAnim.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include <utils/threads.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 
 #include "LayerBase.h"
 #include "LayerBitmap.h"
diff --git a/libs/surfaceflinger/LayerOrientationAnimRotate.h b/libs/surfaceflinger/LayerOrientationAnimRotate.h
index 5fbbd42..3296f45 100644
--- a/libs/surfaceflinger/LayerOrientationAnimRotate.h
+++ b/libs/surfaceflinger/LayerOrientationAnimRotate.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include <utils/threads.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 
 #include "LayerBase.h"
 #include "LayerBitmap.h"
diff --git a/libs/surfaceflinger/LayerScreenshot.cpp b/libs/surfaceflinger/LayerScreenshot.cpp
new file mode 100644
index 0000000..fb7b585
--- /dev/null
+++ b/libs/surfaceflinger/LayerScreenshot.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SurfaceFlinger"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include <core/SkBitmap.h>
+
+#include <ui/EGLDisplaySurface.h>
+
+#include "LayerBase.h"
+#include "LayerScreenshot.h"
+#include "SurfaceFlinger.h"
+#include "DisplayHardware/DisplayHardware.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+const uint32_t LayerScreenshot::typeInfo = LayerBase::typeInfo | 0x20;
+const char* const LayerScreenshot::typeID = "LayerScreenshot";
+
+// ---------------------------------------------------------------------------
+
+LayerScreenshot::LayerScreenshot(SurfaceFlinger* flinger, DisplayID display)
+    : LayerBase(flinger, display), mReply(0)
+{
+}
+
+LayerScreenshot::~LayerScreenshot()
+{
+}
+
+void LayerScreenshot::onDraw(const Region& clip) const
+{
+    const DisplayHardware& hw(graphicPlane(0).displayHardware());
+    copybit_image_t dst;
+    hw.getDisplaySurface(&dst);
+    if (dst.base != 0) {
+        uint8_t const* src = (uint8_t const*)(intptr_t(dst.base) + dst.offset); 
+        const int fbWidth = dst.w;
+        const int fbHeight = dst.h;
+        const int fbFormat = dst.format;
+
+        int x = mTransformedBounds.left;
+        int y = mTransformedBounds.top;
+        int w = mTransformedBounds.width();
+        int h = mTransformedBounds.height();
+        Parcel* const reply = mReply;
+        if (reply) {
+            const size_t Bpp = bytesPerPixel(fbFormat);
+            const size_t size = w * h * Bpp;
+            int32_t cfg = SkBitmap::kNo_Config;
+            switch (fbFormat) {
+                case PIXEL_FORMAT_RGBA_4444: cfg = SkBitmap::kARGB_4444_Config; break;
+                case PIXEL_FORMAT_RGBA_8888: cfg = SkBitmap::kARGB_8888_Config; break;
+                case PIXEL_FORMAT_RGB_565:   cfg = SkBitmap::kRGB_565_Config; break;
+                case PIXEL_FORMAT_A_8:       cfg = SkBitmap::kA8_Config; break;
+            }
+            reply->writeInt32(0);
+            reply->writeInt32(cfg);
+            reply->writeInt32(w);
+            reply->writeInt32(h);
+            reply->writeInt32(w * Bpp);
+            void* data = reply->writeInplace(size);
+            if (data) {
+                uint8_t* d = (uint8_t*)data;
+                uint8_t const* s = src + (x + y*fbWidth) * Bpp;
+                if (w == fbWidth) {
+                    memcpy(d, s, w*h*Bpp);   
+                } else {
+                    for (int y=0 ; y<h ; y++) {
+                        memcpy(d, s, w*Bpp);
+                        d += w*Bpp;
+                        s += fbWidth*Bpp;
+                    }
+                }
+            }
+        }
+    }
+    mCV.broadcast();
+}
+
+void LayerScreenshot::takeScreenshot(Mutex& lock, Parcel* reply)
+{
+    mReply = reply;
+    mCV.wait(lock);
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index ef4a8ea..4dd0150 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -23,6 +23,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <math.h>
+#include <limits.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
@@ -30,10 +31,10 @@
 #include <cutils/log.h>
 #include <cutils/properties.h>
 
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
-#include <utils/MemoryDealer.h>
-#include <utils/MemoryBase.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryDealer.h>
+#include <binder/MemoryBase.h>
 #include <utils/String8.h>
 #include <utils/String16.h>
 #include <utils/StopWatch.h>
diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h
index 15913f2..126bce8 100644
--- a/libs/surfaceflinger/SurfaceFlinger.h
+++ b/libs/surfaceflinger/SurfaceFlinger.h
@@ -25,7 +25,7 @@
 #include <utils/threads.h>
 #include <utils/Atomic.h>
 #include <utils/Errors.h>
-#include <utils/MemoryDealer.h>
+#include <binder/MemoryDealer.h>
 
 #include <ui/PixelFormat.h>
 #include <ui/ISurfaceComposer.h>
diff --git a/libs/surfaceflinger/VRamHeap.cpp b/libs/surfaceflinger/VRamHeap.cpp
index 5f633bd..3b1b152 100644
--- a/libs/surfaceflinger/VRamHeap.cpp
+++ b/libs/surfaceflinger/VRamHeap.cpp
@@ -30,10 +30,10 @@
 #include <cutils/log.h>
 #include <cutils/properties.h>
 
-#include <utils/MemoryDealer.h>
-#include <utils/MemoryBase.h>
-#include <utils/MemoryHeapPmem.h>
-#include <utils/MemoryHeapBase.h>
+#include <binder/MemoryDealer.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapPmem.h>
+#include <binder/MemoryHeapBase.h>
 
 #include <EGL/eglnatives.h>
 
@@ -55,7 +55,7 @@
  * (PMEM is used for 2D acceleration)
  * 8 MB of address space per client should be enough.
  */
-static const int PMEM_SIZE = int(8 * 1024 * 1024);
+static const int PMEM_SIZE = int(16 * 1024 * 1024);
 
 int SurfaceHeapManager::global_pmem_heap = 0;
 
@@ -79,7 +79,11 @@
         mPMemHeap = new PMemHeap(device, PMEM_SIZE);
         if (mPMemHeap->base() == MAP_FAILED) {
             mPMemHeap.clear();
-            global_pmem_heap = 0;
+            mPMemHeap = new PMemHeap(device, PMEM_SIZE/2);
+            if (mPMemHeap->base() == MAP_FAILED) {
+                mPMemHeap.clear();
+                global_pmem_heap = 0;
+            }
         }
     }
 }
diff --git a/libs/surfaceflinger/VRamHeap.h b/libs/surfaceflinger/VRamHeap.h
index 9140167..780a1bc 100644
--- a/libs/surfaceflinger/VRamHeap.h
+++ b/libs/surfaceflinger/VRamHeap.h
@@ -19,7 +19,7 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-#include <utils/MemoryDealer.h>
+#include <binder/MemoryDealer.h>
 
 namespace android {
 
diff --git a/libs/surfaceflinger/tests/overlays/overlays.cpp b/libs/surfaceflinger/tests/overlays/overlays.cpp
index f3c046f..0b9322e 100644
--- a/libs/surfaceflinger/tests/overlays/overlays.cpp
+++ b/libs/surfaceflinger/tests/overlays/overlays.cpp
@@ -1,6 +1,6 @@
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
 #include <utils/Log.h>
 
 #include <ui/Surface.h>
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 7bbe38b..3952c0e 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -30,6 +30,7 @@
 	libcorecg \
 	libcutils \
 	libutils \
+	libbinder \
 	libpixelflinger \
 	libhardware \
 	libhardware_legacy
diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp
index a481ce7..a72d2c9 100644
--- a/libs/ui/Camera.cpp
+++ b/libs/ui/Camera.cpp
@@ -19,9 +19,9 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "Camera"
 #include <utils/Log.h>
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
 #include <utils/threads.h>
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 #include <ui/Surface.h>
 #include <ui/Camera.h>
 #include <ui/ICameraService.h>
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 7c2fc8e..13c30a7 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -19,10 +19,11 @@
 #include <hardware_legacy/power.h>
 
 #include <cutils/properties.h>
-#include <utils/IServiceManager.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
-#include <utils.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
 
 #include <stdlib.h>
 #include <stdio.h>
diff --git a/libs/ui/ICamera.cpp b/libs/ui/ICamera.cpp
index ab0fef1..805c2ca 100644
--- a/libs/ui/ICamera.cpp
+++ b/libs/ui/ICamera.cpp
@@ -20,7 +20,7 @@
 #include <utils/Log.h>
 #include <stdint.h>
 #include <sys/types.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 #include <ui/ICamera.h>
 
 namespace android {
@@ -221,12 +221,6 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnCamera::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/libs/ui/ICameraClient.cpp b/libs/ui/ICameraClient.cpp
index c6cf75c..a88fd48 100644
--- a/libs/ui/ICameraClient.cpp
+++ b/libs/ui/ICameraClient.cpp
@@ -66,12 +66,6 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnCameraClient::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/libs/ui/ICameraService.cpp b/libs/ui/ICameraService.cpp
index e5687fe..84986c6 100644
--- a/libs/ui/ICameraService.cpp
+++ b/libs/ui/ICameraService.cpp
@@ -18,9 +18,9 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Parcel.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
 
 #include <ui/ICameraService.h>
 
@@ -49,12 +49,6 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnCameraService::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/libs/ui/IOverlay.cpp b/libs/ui/IOverlay.cpp
index fed47c2..65e6b4f 100644
--- a/libs/ui/IOverlay.cpp
+++ b/libs/ui/IOverlay.cpp
@@ -18,8 +18,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Parcel.h>
-#include <utils/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
 
 #include <ui/IOverlay.h>
 
@@ -49,12 +49,6 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnOverlay::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/libs/ui/ISurface.cpp b/libs/ui/ISurface.cpp
index d5e9f81..1e60557 100644
--- a/libs/ui/ISurface.cpp
+++ b/libs/ui/ISurface.cpp
@@ -18,8 +18,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Parcel.h>
-#include <utils/IMemory.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
 
 #include <ui/ISurface.h>
 #include <ui/Overlay.h>
@@ -112,12 +112,6 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnSurface::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/libs/ui/ISurfaceComposer.cpp b/libs/ui/ISurfaceComposer.cpp
index 76597e1..5f558a1 100644
--- a/libs/ui/ISurfaceComposer.cpp
+++ b/libs/ui/ISurfaceComposer.cpp
@@ -20,10 +20,10 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Parcel.h>
-#include <utils/IMemory.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
 
 #include <ui/ISurfaceComposer.h>
 #include <ui/DisplayInfo.h>
@@ -156,62 +156,61 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnSurfaceComposer::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
-    status_t err = BnInterface<ISurfaceComposer>::onTransact(code, data, reply, flags);
-    if (err == NO_ERROR)
-        return err;
-
-    CHECK_INTERFACE(ISurfaceComposer, data, reply);
-
     switch(code) {
         case CREATE_CONNECTION: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> b = createConnection()->asBinder();
             reply->writeStrongBinder(b);
         } break;
         case OPEN_GLOBAL_TRANSACTION: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
             openGlobalTransaction();
         } break;
         case CLOSE_GLOBAL_TRANSACTION: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
             closeGlobalTransaction();
         } break;
         case SET_ORIENTATION: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
             DisplayID dpy = data.readInt32();
             int orientation = data.readInt32();
             uint32_t flags = data.readInt32();
             reply->writeInt32( setOrientation(dpy, orientation, flags) );
         } break;
         case FREEZE_DISPLAY: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
             DisplayID dpy = data.readInt32();
             uint32_t flags = data.readInt32();
             reply->writeInt32( freezeDisplay(dpy, flags) );
         } break;
         case UNFREEZE_DISPLAY: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
             DisplayID dpy = data.readInt32();
             uint32_t flags = data.readInt32();
             reply->writeInt32( unfreezeDisplay(dpy, flags) );
         } break;
         case BOOT_FINISHED: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
             bootFinished();
         } break;
         case REVOKE_GPU: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
             reply->writeInt32( revokeGPU() );
         } break;
         case SIGNAL: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
             signal();
         } break;
         case GET_CBLK: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> b = getCblk()->asBinder();
             reply->writeStrongBinder(b);
         } break;
         case REQUEST_GPU: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
             // TODO: this should be protected by a permission
             gpu_info_t info;
             sp<IGPUCallback> callback
@@ -232,7 +231,7 @@
             reply->writeInt32(res);
         } break;
         default:
-            return UNKNOWN_TRANSACTION;
+            return BBinder::onTransact(code, data, reply, flags);
     }
     return NO_ERROR;
 }
diff --git a/libs/ui/ISurfaceFlingerClient.cpp b/libs/ui/ISurfaceFlingerClient.cpp
index dab5f71..329bd6e 100644
--- a/libs/ui/ISurfaceFlingerClient.cpp
+++ b/libs/ui/ISurfaceFlingerClient.cpp
@@ -21,10 +21,10 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Parcel.h>
-#include <utils/IMemory.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
 
 #include <ui/ISurface.h>
 #include <ui/ISurfaceFlingerClient.h>
@@ -118,12 +118,6 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnSurfaceFlingerClient::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/libs/ui/LayerState.cpp b/libs/ui/LayerState.cpp
index 0b6374b..a53ffb7 100644
--- a/libs/ui/LayerState.cpp
+++ b/libs/ui/LayerState.cpp
@@ -15,7 +15,7 @@
  */
 
 #include <utils/Errors.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 #include <private/ui/LayerState.h>
 
 namespace android {
diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp
index 59c6514..a092f8d 100644
--- a/libs/ui/Overlay.cpp
+++ b/libs/ui/Overlay.cpp
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#include <utils/IMemory.h>
-#include <utils/Parcel.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
 #include <utils/Errors.h>
-#include <utils/MemoryHeapBase.h>
+#include <binder/MemoryHeapBase.h>
 
 #include <ui/IOverlay.h>
 #include <ui/Overlay.h>
diff --git a/libs/ui/Surface.cpp b/libs/ui/Surface.cpp
index 4ea9ae2..05cc529 100644
--- a/libs/ui/Surface.cpp
+++ b/libs/ui/Surface.cpp
@@ -26,8 +26,8 @@
 #include <utils/Atomic.h>
 #include <utils/Errors.h>
 #include <utils/threads.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IMemory.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IMemory.h>
 #include <utils/Log.h>
 
 #include <ui/ISurface.h>
diff --git a/libs/ui/SurfaceComposerClient.cpp b/libs/ui/SurfaceComposerClient.cpp
index fe803ff..8acd2ee 100644
--- a/libs/ui/SurfaceComposerClient.cpp
+++ b/libs/ui/SurfaceComposerClient.cpp
@@ -29,9 +29,9 @@
 #include <utils/Errors.h>
 #include <utils/threads.h>
 #include <utils/KeyedVector.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
-#include <utils/IMemory.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IMemory.h>
 #include <utils/Log.h>
 
 #include <ui/ISurfaceComposer.h>
@@ -48,7 +48,7 @@
 
 #include <pixelflinger/pixelflinger.h>
 
-#include <utils/BpBinder.h>
+#include <binder/BpBinder.h>
 
 #define VERBOSE(...)	((void)0)
 //#define VERBOSE			LOGD
diff --git a/libs/ui/SurfaceFlingerSynchro.cpp b/libs/ui/SurfaceFlingerSynchro.cpp
index 5cd9755..2e8fa12 100644
--- a/libs/ui/SurfaceFlingerSynchro.cpp
+++ b/libs/ui/SurfaceFlingerSynchro.cpp
@@ -25,7 +25,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 
-#include <utils/IPCThreadState.h>
+#include <binder/IPCThreadState.h>
 #include <utils/Log.h>
 
 #include <private/ui/SurfaceFlingerSynchro.h>
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 9bdd64a..3f5cb85 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -32,51 +32,30 @@
 	StopWatch.cpp \
 	String8.cpp \
 	String16.cpp \
+	StringArray.cpp \
 	SystemClock.cpp \
 	TextOutput.cpp \
 	Threads.cpp \
-	TimerProbe.cpp \
 	Timers.cpp \
 	VectorImpl.cpp \
     ZipFileCRO.cpp \
 	ZipFileRO.cpp \
 	ZipUtils.cpp \
-	misc.cpp \
-	ported.cpp \
-	LogSocket.cpp
+	misc.cpp
 
-#
-# The cpp files listed here do not belong in the device
-# build.  Consult with the swetland before even thinking about
-# putting them in commonSources.
-#
-# They're used by the simulator runtime and by host-side tools like
-# aapt and the simulator front-end.
-#
-hostSources:= \
-	InetAddress.cpp \
-	Pipe.cpp \
-	Socket.cpp \
-	ZipEntry.cpp \
-	ZipFile.cpp
 
 # For the host
 # =====================================================
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= $(commonSources) $(hostSources)
+LOCAL_SRC_FILES:= $(commonSources)
 
 ifeq ($(HOST_OS),linux)
 # Use the futex based mutex and condition variable
 # implementation from android-arm because it's shared mem safe
 	LOCAL_SRC_FILES += \
-		futex_synchro.c \
-		executablepath_linux.cpp
-endif
-ifeq ($(HOST_OS),darwin)
-	LOCAL_SRC_FILES += \
-		executablepath_darwin.cpp
+		futex_synchro.c
 endif
 
 LOCAL_MODULE:= libutils
@@ -103,27 +82,10 @@
 # we have the common sources, plus some device-specific stuff
 LOCAL_SRC_FILES:= \
 	$(commonSources) \
-	Binder.cpp \
-	BpBinder.cpp \
-	IInterface.cpp \
-	IMemory.cpp \
-	IPCThreadState.cpp \
-	MemoryDealer.cpp \
-    MemoryBase.cpp \
-    MemoryHeapBase.cpp \
-    MemoryHeapPmem.cpp \
-	Parcel.cpp \
-	ProcessState.cpp \
-	IPermissionController.cpp \
-	IServiceManager.cpp \
 	Unicode.cpp \
     BackupData.cpp \
 	BackupHelpers.cpp
 
-ifeq ($(TARGET_SIMULATOR),true)
-LOCAL_SRC_FILES += $(hostSources)
-endif
-
 ifeq ($(TARGET_OS),linux)
 # Use the futex based mutex and condition variable
 # implementation from android-arm because it's shared mem safe
@@ -150,9 +112,5 @@
 endif # sim
 
 LOCAL_MODULE:= libutils
-
-#LOCAL_CFLAGS+=
-#LOCAL_LDFLAGS:=
-
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp
index 2fdaa71..55b6024 100644
--- a/libs/utils/CallStack.cpp
+++ b/libs/utils/CallStack.cpp
@@ -311,7 +311,8 @@
     } else { 
         void const* start = 0;
         name = MapInfo::mapAddressToName(ip, "<unknown>", &start);
-        snprintf(tmp, 256, "pc %08lx  %s", uintptr_t(ip)-uintptr_t(start), name);
+        snprintf(tmp, 256, "pc %08lx  %s", 
+                long(uintptr_t(ip)-uintptr_t(start)), name);
         res.append(tmp);
     }
     res.append("\n");
diff --git a/libs/utils/IDataConnection.cpp b/libs/utils/IDataConnection.cpp
deleted file mode 100644
index c6d49aa..0000000
--- a/libs/utils/IDataConnection.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Parcel.h>
-
-#include <utils/IDataConnection.h>
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-enum
-{
-    CONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
-    DISCONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1
-};
-
-class BpDataConnection : public BpInterface<IDataConnection>
-{
-public:
-    BpDataConnection::BpDataConnection(const sp<IBinder>& impl)
-        : BpInterface<IDataConnection>(impl)
-    {
-    }
-
-	virtual void connect()
-	{
-		Parcel data, reply;
-        data.writeInterfaceToken(IDataConnection::descriptor());
-		remote()->transact(CONNECT_TRANSACTION, data, &reply);
-	}
-	
-	virtual void disconnect()
-	{
-		Parcel data, reply;
-		remote()->transact(DISCONNECT_TRANSACTION, data, &reply);
-	}
-};
-
-IMPLEMENT_META_INTERFACE(DataConnection, "android.utils.IDataConnection");
-
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
-status_t BnDataConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-    switch(code)
-    {
-		case CONNECT_TRANSACTION:
-		{                   
-            CHECK_INTERFACE(IDataConnection, data, reply);
-			connect();
-			return NO_ERROR;
-		}    
-		
-		case DISCONNECT_TRANSACTION:
-		{                   
-            CHECK_INTERFACE(IDataConnection, data, reply);
-			disconnect();
-			return NO_ERROR;
-		}
-       
-		default:
-			return BBinder::onTransact(code, data, reply, flags);
-    }
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp
deleted file mode 100644
index 39a0a68..0000000
--- a/libs/utils/InetAddress.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Internet address class.
-//
-#ifdef HAVE_WINSOCK
-# include <winsock2.h>
-#else
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-//# include <arpa/inet.h>
-# include <netdb.h>
-#endif
-
-#include <utils/inet_address.h>
-#include <utils/threads.h>
-#include <utils/Log.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-using namespace android;
-
-
-/*
- * ===========================================================================
- *      InetAddress
- * ===========================================================================
- */
-
-// lock for the next couple of functions; could tuck into InetAddress
-static Mutex*   gGHBNLock;
-
-/*
- * Lock/unlock access to the hostent struct returned by gethostbyname().
- */
-static inline void lock_gethostbyname(void)
-{
-    if (gGHBNLock == NULL)
-        gGHBNLock = new Mutex;
-    gGHBNLock->lock();
-}
-static inline void unlock_gethostbyname(void)
-{
-    assert(gGHBNLock != NULL);
-    gGHBNLock->unlock();
-}
-
-
-/*
- * Constructor -- just init members.  This is private so that callers
- * are required to use getByName().
- */
-InetAddress::InetAddress(void)
-    : mAddress(NULL), mLength(-1), mName(NULL)
-{
-}
-
-/*
- * Destructor -- free address storage.
- */
-InetAddress::~InetAddress(void)
-{
-    delete[] (char*) mAddress;
-    delete[] mName;
-}
-
-/*
- * Copy constructor.
- */
-InetAddress::InetAddress(const InetAddress& orig)
-{
-    *this = orig;   // use assignment code
-}
-
-/*
- * Assignment operator.
- */
-InetAddress& InetAddress::operator=(const InetAddress& addr)
-{
-    // handle self-assignment
-    if (this == &addr)
-        return *this;
-    // copy mLength and mAddress
-    mLength = addr.mLength;
-    if (mLength > 0) {
-        mAddress = new char[mLength];
-        memcpy(mAddress, addr.mAddress, mLength);
-        LOG(LOG_DEBUG, "socket",
-            "HEY: copied %d bytes in assignment operator\n", mLength);
-    } else {
-        mAddress = NULL;
-    }
-    // copy mName
-    mName = new char[strlen(addr.mName)+1];
-    strcpy(mName, addr.mName);
-
-    return *this;
-}
-
-/*
- * Create a new object from a name or a dotted-number IP notation.
- *
- * Returns NULL on failure.
- */
-InetAddress*
-InetAddress::getByName(const char* host)
-{
-    InetAddress* newAddr = NULL;
-    struct sockaddr_in addr;
-    struct hostent* he;
-    DurationTimer hostTimer, lockTimer;
-
-    // gethostbyname() isn't reentrant, so we need to lock things until
-    // we can copy the data out.
-    lockTimer.start();
-    lock_gethostbyname();
-    hostTimer.start();
-
-    he = gethostbyname(host);
-    if (he == NULL) {
-        LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host);
-        unlock_gethostbyname();
-        return NULL;
-    }
-
-    memcpy(&addr.sin_addr, he->h_addr, he->h_length);
-    addr.sin_family = he->h_addrtype;
-    addr.sin_port = 0;
-
-    // got it, unlock us
-    hostTimer.stop();
-    he = NULL;
-    unlock_gethostbyname();
-
-    lockTimer.stop();
-    if ((long) lockTimer.durationUsecs() > 100000) {
-        long lockTime = (long) lockTimer.durationUsecs();
-        long hostTime = (long) hostTimer.durationUsecs();
-        LOG(LOG_DEBUG, "socket",
-            "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n",
-            host, lockTime / 1000000.0, hostTime / 1000000.0,
-            (lockTime - hostTime) / 1000000.0);
-    }
-
-    // Alloc storage and copy it over.
-    newAddr = new InetAddress();
-    if (newAddr == NULL)
-        return NULL;
-
-    newAddr->mLength = sizeof(struct sockaddr_in);
-    newAddr->mAddress = new char[sizeof(struct sockaddr_in)];
-    if (newAddr->mAddress == NULL) {
-        delete newAddr;
-        return NULL;
-    }
-    memcpy(newAddr->mAddress, &addr, newAddr->mLength);
-
-    // Keep this for debug messages.
-    newAddr->mName = new char[strlen(host)+1];
-    if (newAddr->mName == NULL) {
-        delete newAddr;
-        return NULL;
-    }
-    strcpy(newAddr->mName, host);
-
-    return newAddr;
-}
-
-
-/*
- * ===========================================================================
- *      InetSocketAddress
- * ===========================================================================
- */
-
-/*
- * Create an address with the host wildcard (INADDR_ANY).
- */
-bool InetSocketAddress::create(int port)
-{
-    assert(mAddress == NULL);
-
-    mAddress = InetAddress::getByName("0.0.0.0");
-    if (mAddress == NULL)
-        return false;
-    mPort = port;
-    return true;
-}
-
-/*
- * Create address with host and port specified.
- */
-bool InetSocketAddress::create(const InetAddress* addr, int port)
-{
-    assert(mAddress == NULL);
-
-    mAddress = new InetAddress(*addr);  // make a copy
-    if (mAddress == NULL)
-        return false;
-    mPort = port;
-    return true;
-}
-
-/*
- * Create address with host and port specified.
- */
-bool InetSocketAddress::create(const char* host, int port)
-{
-    assert(mAddress == NULL);
-
-    mAddress = InetAddress::getByName(host);
-    if (mAddress == NULL)
-        return false;
-    mPort = port;
-    return true;
-}
-
diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp
deleted file mode 100644
index 55c1b99..0000000
--- a/libs/utils/LogSocket.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef HAVE_WINSOCK
-//#define SOCKETLOG
-#endif
-
-#ifdef SOCKETLOG
-
-#define LOG_TAG "SOCKETLOG"
-
-#include <string.h>
-#include <cutils/log.h>
-#include "utils/LogSocket.h"
-#include "utils/logger.h"
-#include "cutils/hashmap.h"
-
-// defined in //device/data/etc/event-log-tags
-#define SOCKET_CLOSE_LOG 51000
-
-static Hashmap* statsMap = NULL;
-
-#define LOG_LIST_NUMBER 5
-
-typedef struct SocketStats {
-    int fd;
-    unsigned int send;
-    unsigned int recv;
-    unsigned int ip;
-    unsigned short port;
-    short reason;
-}SocketStats;
-
-SocketStats *get_socket_stats(int fd) {
-    if (statsMap == NULL) {
-        statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals);
-    }
-
-    SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd);
-    if (s == NULL) {
-        // LOGD("create SocketStats for fd %d", fd);
-        s = (SocketStats*) malloc(sizeof(SocketStats));
-        memset(s, 0, sizeof(SocketStats));
-        s->fd = fd;
-        hashmapPut(statsMap, &s->fd, s);
-    }
-    return s;
-}
-
-void log_socket_connect(int fd, unsigned int ip, unsigned short port) {
-    // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port);
-    SocketStats *s = get_socket_stats(fd);
-    s->ip = ip;
-    s->port = port;
-}
-
-void add_send_stats(int fd, int send) {
-    if (send <=0) {
-        LOGE("add_send_stats send %d", send);
-        return;
-    }
-    SocketStats *s = get_socket_stats(fd);
-    s->send += send;
-    // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port);
-}
-
-void add_recv_stats(int fd, int recv) {
-    if (recv <=0) {
-        LOGE("add_recv_stats recv %d", recv);
-        return;
-    }
-    SocketStats *s = get_socket_stats(fd);
-    s->recv += recv;
-    // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port);
-}
-
-char* put_int(char* buf, int value) {
-    *buf = EVENT_TYPE_INT;
-    buf++;
-    memcpy(buf, &value, sizeof(int));
-    return buf + sizeof(int);
-}
-
-void log_socket_close(int fd, short reason) {
-    if (statsMap) {
-        SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd);
-        if (s != NULL) {
-            if (s->send != 0 || s->recv != 0) {
-                s->reason = reason;
-                // 5 int + list type need 2 bytes
-                char buf[LOG_LIST_NUMBER * 5 + 2];
-                buf[0] = EVENT_TYPE_LIST;
-                buf[1] = LOG_LIST_NUMBER;
-                char* writePos = buf + 2;
-                writePos = put_int(writePos, s->send);
-                writePos = put_int(writePos, s->recv);
-                writePos = put_int(writePos, s->ip);
-                writePos = put_int(writePos, s->port);
-                writePos = put_int(writePos, s->reason);
-                
-                android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf));
-                // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason);
-            }
-            hashmapRemove(statsMap, &s->fd);
-            free(s);
-        }
-    }
-}
-
-#else
-void add_send_stats(int fd, int send) {} 
-void add_recv_stats(int fd, int recv) {}
-void log_socket_close(int fd, short reason) {}
-void log_socket_connect(int fd, unsigned int ip, unsigned short port) {}
-#endif
diff --git a/libs/utils/Pipe.cpp b/libs/utils/Pipe.cpp
deleted file mode 100644
index 613906b..0000000
--- a/libs/utils/Pipe.cpp
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Unidirectional pipe.
-//
-
-#include <utils/Pipe.h>
-#include <utils/Log.h>
-
-#if defined(HAVE_WIN32_IPC)
-# include <windows.h>
-#else
-# include <fcntl.h>
-# include <unistd.h>
-# include <errno.h>
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-#include <string.h>
-
-using namespace android;
-
-const unsigned long kInvalidHandle = (unsigned long) -1;
-
-
-/*
- * Constructor.  Do little.
- */
-Pipe::Pipe(void)
-    : mReadNonBlocking(false), mReadHandle(kInvalidHandle),
-      mWriteHandle(kInvalidHandle)
-{
-}
-
-/*
- * Destructor.  Use the system-appropriate close call.
- */
-Pipe::~Pipe(void)
-{
-#if defined(HAVE_WIN32_IPC)
-    if (mReadHandle != kInvalidHandle) {
-        if (!CloseHandle((HANDLE)mReadHandle))
-            LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n",
-                mReadHandle);
-    }
-    if (mWriteHandle != kInvalidHandle) {
-        FlushFileBuffers((HANDLE)mWriteHandle);
-        if (!CloseHandle((HANDLE)mWriteHandle))
-            LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n",
-                mWriteHandle);
-    }
-#else
-    if (mReadHandle != kInvalidHandle) {
-        if (close((int) mReadHandle) != 0)
-            LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n",
-                (int) mReadHandle);
-    }
-    if (mWriteHandle != kInvalidHandle) {
-        if (close((int) mWriteHandle) != 0)
-            LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n",
-                (int) mWriteHandle);
-    }
-#endif
-}
-
-/*
- * Create the pipe.
- *
- * Use the POSIX stuff for everything but Windows.
- */
-bool Pipe::create(void)
-{
-    assert(mReadHandle == kInvalidHandle);
-    assert(mWriteHandle == kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
-    /* we use this across processes, so they need to be inheritable */
-    HANDLE handles[2];
-    SECURITY_ATTRIBUTES saAttr;
-
-    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
-    saAttr.bInheritHandle = TRUE;
-    saAttr.lpSecurityDescriptor = NULL;
-
-    if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) {
-        LOG(LOG_ERROR, "pipe", "unable to create pipe\n");
-        return false;
-    }
-    mReadHandle = (unsigned long) handles[0];
-    mWriteHandle = (unsigned long) handles[1];
-    return true;
-#else
-    int fds[2];
-
-    if (pipe(fds) != 0) {
-        LOG(LOG_ERROR, "pipe", "unable to create pipe\n");
-        return false;
-    }
-    mReadHandle = fds[0];
-    mWriteHandle = fds[1];
-    return true;
-#endif
-}
-
-/*
- * Create a "half pipe".  Please, no Segway riding.
- */
-bool Pipe::createReader(unsigned long handle)
-{
-    mReadHandle = handle;
-    assert(mWriteHandle == kInvalidHandle);
-    return true;
-}
-
-/*
- * Create a "half pipe" for writing.
- */
-bool Pipe::createWriter(unsigned long handle)
-{
-    mWriteHandle = handle;
-    assert(mReadHandle == kInvalidHandle);
-    return true;
-}
-
-/*
- * Return "true" if create() has been called successfully.
- */
-bool Pipe::isCreated(void)
-{
-    // one or the other should be open
-    return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle);
-}
-
-
-/*
- * Read data from the pipe.
- *
- * For Linux and Darwin, just call read().  For Windows, implement
- * non-blocking reads by calling PeekNamedPipe first.
- */
-int Pipe::read(void* buf, int count)
-{
-    assert(mReadHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
-    DWORD totalBytesAvail = count;
-    DWORD bytesRead;
-
-    if (mReadNonBlocking) {
-        // use PeekNamedPipe to adjust read count expectations
-        if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL,
-                &totalBytesAvail, NULL))
-        {
-            LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n");
-            return -1;
-        }
-
-        if (totalBytesAvail == 0)
-            return 0;
-    }
-
-    if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead,
-            NULL))
-    {
-        DWORD err = GetLastError();
-        if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE)
-            return 0;
-        LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err);
-        return -1;
-    }
-
-    return (int) bytesRead;
-#else
-    int cc;
-    cc = ::read(mReadHandle, buf, count);
-    if (cc < 0 && errno == EAGAIN)
-        return 0;
-    return cc;
-#endif
-}
-
-/*
- * Write data to the pipe.
- *
- * POSIX systems are trivial, Windows uses a different call and doesn't
- * handle non-blocking writes.
- *
- * If we add non-blocking support here, we probably want to make it an
- * all-or-nothing write.
- *
- * DO NOT use LOG() here, we could be writing a log message.
- */
-int Pipe::write(const void* buf, int count)
-{
-    assert(mWriteHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
-    DWORD bytesWritten;
-
-    if (mWriteNonBlocking) {
-        // BUG: can't use PeekNamedPipe() to get the amount of space
-        // left.  Looks like we need to use "overlapped I/O" functions.
-        // I just don't care that much.
-    }
-
-    if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) {
-        // can't LOG, use stderr
-        fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError());
-        return -1;
-    }
-
-    return (int) bytesWritten;
-#else
-    int cc;
-    cc = ::write(mWriteHandle, buf, count);
-    if (cc < 0 && errno == EAGAIN)
-        return 0;
-    return cc;
-#endif
-}
-
-/*
- * Figure out if there is data available on the read fd.
- *
- * We return "true" on error because we want the caller to try to read
- * from the pipe.  They'll notice the read failure and do something
- * appropriate.
- */
-bool Pipe::readReady(void)
-{
-    assert(mReadHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
-    DWORD totalBytesAvail;
-
-    if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL,
-            &totalBytesAvail, NULL))
-    {
-        LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n");
-        return true;
-    }
-
-    return (totalBytesAvail != 0);
-#else
-    errno = 0;
-    fd_set readfds;
-    struct timeval tv = { 0, 0 };
-    int cc;
-
-    FD_ZERO(&readfds);
-    FD_SET(mReadHandle, &readfds);
-
-    cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv);
-    if (cc < 0) {
-        LOG(LOG_ERROR, "pipe", "select() failed\n");
-        return true;
-    } else if (cc == 0) {
-        /* timed out, nothing available */
-        return false;
-    } else if (cc == 1) {
-        /* our fd is ready */
-        return true;
-    } else {
-        LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n");
-        return true;
-    }
-#endif
-}
-
-/*
- * Enable or disable non-blocking mode for the read descriptor.
- *
- * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to
- * actually be in non-blocking mode.  If this matters -- i.e. you're not
- * using a select() call -- put a call to readReady() in front of the
- * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for
- * Darwin.
- */
-bool Pipe::setReadNonBlocking(bool val)
-{
-    assert(mReadHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
-    // nothing to do
-#else
-    int flags;
-
-    if (fcntl(mReadHandle, F_GETFL, &flags) == -1) {
-        LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n");
-        return false;
-    }
-    if (val)
-        flags |= O_NONBLOCK;
-    else
-        flags &= ~(O_NONBLOCK);
-    if (fcntl(mReadHandle, F_SETFL, &flags) == -1) {
-        LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n");
-        return false;
-    }
-#endif
-
-    mReadNonBlocking = val;
-    return true;
-}
-
-/*
- * Enable or disable non-blocking mode for the write descriptor.
- *
- * As with setReadNonBlocking(), this does not work on the Mac.
- */
-bool Pipe::setWriteNonBlocking(bool val)
-{
-    assert(mWriteHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
-    // nothing to do
-#else
-    int flags;
-
-    if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) {
-        LOG(LOG_WARN, "pipe",
-            "Warning: couldn't get flags for pipe write fd (errno=%d)\n",
-            errno);
-        return false;
-    }
-    if (val)
-        flags |= O_NONBLOCK;
-    else
-        flags &= ~(O_NONBLOCK);
-    if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) {
-        LOG(LOG_WARN, "pipe",
-            "Warning: couldn't set flags for pipe write fd (errno=%d)\n",
-            errno);
-        return false;
-    }
-#endif
-
-    mWriteNonBlocking = val;
-    return true;
-}
-
-/*
- * Specify whether a file descriptor can be inherited by a child process.
- * Under Linux this means setting the close-on-exec flag, under Windows
- * this is SetHandleInformation(HANDLE_FLAG_INHERIT).
- */
-bool Pipe::disallowReadInherit(void)
-{
-    if (mReadHandle == kInvalidHandle)
-        return false;
-
-#if defined(HAVE_WIN32_IPC)
-    if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0)
-        return false;
-#else
-    if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0)
-        return false;
-#endif
-    return true;
-}
-bool Pipe::disallowWriteInherit(void)
-{
-    if (mWriteHandle == kInvalidHandle)
-        return false;
-
-#if defined(HAVE_WIN32_IPC)
-    if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0)
-        return false;
-#else
-    if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0)
-        return false;
-#endif
-    return true;
-}
-
-/*
- * Close read descriptor.
- */
-bool Pipe::closeRead(void)
-{
-    if (mReadHandle == kInvalidHandle)
-        return false;
-
-#if defined(HAVE_WIN32_IPC)
-    if (mReadHandle != kInvalidHandle) {
-        if (!CloseHandle((HANDLE)mReadHandle)) {
-            LOG(LOG_WARN, "pipe", "failed closing read handle\n");
-            return false;
-        }
-    }
-#else
-    if (mReadHandle != kInvalidHandle) {
-        if (close((int) mReadHandle) != 0) {
-            LOG(LOG_WARN, "pipe", "failed closing read fd\n");
-            return false;
-        }
-    }
-#endif
-    mReadHandle = kInvalidHandle;
-    return true;
-}
-
-/*
- * Close write descriptor.
- */
-bool Pipe::closeWrite(void)
-{
-    if (mWriteHandle == kInvalidHandle)
-        return false;
-
-#if defined(HAVE_WIN32_IPC)
-    if (mWriteHandle != kInvalidHandle) {
-        if (!CloseHandle((HANDLE)mWriteHandle)) {
-            LOG(LOG_WARN, "pipe", "failed closing write handle\n");
-            return false;
-        }
-    }
-#else
-    if (mWriteHandle != kInvalidHandle) {
-        if (close((int) mWriteHandle) != 0) {
-            LOG(LOG_WARN, "pipe", "failed closing write fd\n");
-            return false;
-        }
-    }
-#endif
-    mWriteHandle = kInvalidHandle;
-    return true;
-}
-
-/*
- * Get the read handle.
- */
-unsigned long Pipe::getReadHandle(void)
-{
-    assert(mReadHandle != kInvalidHandle);
-
-    return mReadHandle;
-}
-
-/*
- * Get the write handle.
- */
-unsigned long Pipe::getWriteHandle(void)
-{
-    assert(mWriteHandle != kInvalidHandle);
-
-    return mWriteHandle;
-}
-
diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp
deleted file mode 100644
index 51509a3..0000000
--- a/libs/utils/Socket.cpp
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Internet address class.
-//
-
-#ifdef HAVE_WINSOCK
-// This needs to come first, or Cygwin gets concerned about a potential
-// clash between WinSock and <sys/types.h>.
-# include <winsock2.h>
-#endif
-
-#include <utils/Socket.h>
-#include <utils/inet_address.h>
-#include <utils/Log.h>
-#include <utils/Timers.h>
-
-#ifndef HAVE_WINSOCK
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <arpa/inet.h>
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-using namespace android;
-
-
-/*
- * ===========================================================================
- *      Socket
- * ===========================================================================
- */
-
-#ifndef INVALID_SOCKET
-# define INVALID_SOCKET (-1)
-#endif
-#define UNDEF_SOCKET   ((unsigned long) INVALID_SOCKET)
-
-/*static*/ bool Socket::mBootInitialized = false;
-
-/*
- * Extract system-dependent error code.
- */
-static inline int getSocketError(void) {
-#ifdef HAVE_WINSOCK
-    return WSAGetLastError();
-#else
-    return errno;
-#endif
-}
-
-/*
- * One-time initialization for socket code.
- */
-/*static*/ bool Socket::bootInit(void)
-{
-#ifdef HAVE_WINSOCK
-    WSADATA wsaData;
-    int err;
-
-    err = WSAStartup(MAKEWORD(2, 0), &wsaData);
-    if (err != 0) {
-        LOG(LOG_ERROR, "socket", "Unable to start WinSock\n");
-        return false;
-    }
-
-    LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n",
-        LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
-#endif
-
-    mBootInitialized = true;
-    return true;
-}
-
-/*
- * One-time shutdown for socket code.
- */
-/*static*/ void Socket::finalShutdown(void)
-{
-#ifdef HAVE_WINSOCK
-    WSACleanup();
-#endif
-    mBootInitialized = false;
-}
-
-
-/*
- * Simple constructor.  Allow the application to create us and then make
- * bind/connect calls.
- */
-Socket::Socket(void)
-    : mSock(UNDEF_SOCKET)
-{
-    if (!mBootInitialized)
-        LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n");
-}
-
-/*
- * Destructor.  Closes the socket and resets our storage.
- */
-Socket::~Socket(void)
-{
-    close();
-}
-
-
-/*
- * Create a socket and connect to the specified host and port.
- */
-int Socket::connect(const char* host, int port)
-{
-    if (mSock != UNDEF_SOCKET) {
-        LOG(LOG_WARN, "socket", "Socket already connected\n");
-        return -1;
-    }
-
-    InetSocketAddress sockAddr;
-    if (!sockAddr.create(host, port))
-        return -1;
-
-    //return doConnect(sockAddr);
-    int foo;
-    foo = doConnect(sockAddr);
-    return foo;
-}
-
-/*
- * Create a socket and connect to the specified host and port.
- */
-int Socket::connect(const InetAddress* addr, int port)
-{
-    if (mSock != UNDEF_SOCKET) {
-        LOG(LOG_WARN, "socket", "Socket already connected\n");
-        return -1;
-    }
-
-    InetSocketAddress sockAddr;
-    if (!sockAddr.create(addr, port))
-        return -1;
-
-    return doConnect(sockAddr);
-}
-
-/*
- * Finish creating a socket by connecting to the remote host.
- *
- * Returns 0 on success.
- */
-int Socket::doConnect(const InetSocketAddress& sockAddr)
-{
-#ifdef HAVE_WINSOCK
-    SOCKET sock;
-#else
-    int sock;
-#endif
-    const InetAddress* addr = sockAddr.getAddress();
-    int port = sockAddr.getPort();
-    struct sockaddr_in inaddr;
-    DurationTimer connectTimer;
-
-    assert(sizeof(struct sockaddr_in) == addr->getAddressLength());
-    memcpy(&inaddr, addr->getAddress(), addr->getAddressLength());
-    inaddr.sin_port = htons(port);
-
-    //fprintf(stderr, "--- connecting to %s:%d\n",
-    //    sockAddr.getHostName(), port);
-
-    sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if (sock == INVALID_SOCKET) {
-        int err = getSocketError();
-        LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err);
-        return (err != 0) ? err : -1;
-    }
-
-    connectTimer.start();
-
-    if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) {
-        int err = getSocketError();
-        LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n",
-            sockAddr.getHostName(), port, err);
-        return (err != 0) ? err : -1;
-    }
-
-    connectTimer.stop();
-    if ((long) connectTimer.durationUsecs() > 100000) {
-        LOG(LOG_INFO, "socket",
-            "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(),
-            port, ((long) connectTimer.durationUsecs()) / 1000000.0);
-    }
-
-    mSock = (unsigned long) sock;
-    LOG(LOG_VERBOSE, "socket",
-        "--- connected to %s:%d\n", sockAddr.getHostName(), port);
-    return 0;
-}
-
-
-/*
- * Close the socket if it needs closing.
- */
-bool Socket::close(void)
-{
-    if (mSock != UNDEF_SOCKET) {
-        //fprintf(stderr, "--- closing socket %lu\n", mSock);
-#ifdef HAVE_WINSOCK
-        if (::closesocket((SOCKET) mSock) != 0)
-            return false;
-#else
-        if (::close((int) mSock) != 0)
-            return false;
-#endif
-    }
-
-    mSock = UNDEF_SOCKET;
-
-    return true;
-}
-
-/*
- * Read data from socket.
- *
- * Standard semantics: read up to "len" bytes into "buf".  Returns the
- * number of bytes read, or less than zero on error.
- */
-int Socket::read(void* buf, ssize_t len) const
-{
-    if (mSock == UNDEF_SOCKET) {
-        LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n");
-        return -500;
-    }
-
-#ifdef HAVE_WINSOCK
-    SOCKET sock = (SOCKET) mSock;
-#else
-    int sock = (int) mSock;
-#endif
-    int cc;
-
-    cc = recv(sock, (char*)buf, len, 0);
-    if (cc < 0) {
-        int err = getSocketError();
-        return (err > 0) ? -err : -1;
-    }
-
-    return cc;
-}
-
-/*
- * Write data to a socket.
- *
- * Standard semantics: write up to "len" bytes into "buf".  Returns the
- * number of bytes written, or less than zero on error.
- */
-int Socket::write(const void* buf, ssize_t len) const
-{
-    if (mSock == UNDEF_SOCKET) {
-        LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n");
-        return -500;
-    }
-
-#ifdef HAVE_WINSOCK
-    SOCKET sock = (SOCKET) mSock;
-#else
-    int sock = (int) mSock;
-#endif
-    int cc;
-
-    cc = send(sock, (const char*)buf, len, 0);
-    if (cc < 0) {
-        int err = getSocketError();
-        return (err > 0) ? -err : -1;
-    }
-
-    return cc;
-}
-
-
-/*
- * ===========================================================================
- *      Socket tests
- * ===========================================================================
- */
-
-/*
- * Read all data from the socket.  The data is read into a buffer that
- * expands as needed.
- *
- * On exit, the buffer is returned, and the length of the data is stored
- * in "*sz".  A null byte is added to the end, but is not included in
- * the length.
- */
-static char* socketReadAll(const Socket& s, int *sz)
-{
-    int max, r;
-    char *data, *ptr, *tmp;
-
-    data = (char*) malloc(max = 32768);
-    if (data == NULL)
-        return NULL;
-
-    ptr = data;
-    
-    for (;;) {
-        if ((ptr - data) == max) {
-            tmp = (char*) realloc(data, max *= 2);
-            if(tmp == 0) {
-                free(data);
-                return 0;
-            }
-        }
-        r = s.read(ptr, max - (ptr - data));
-        if (r == 0)
-            break;
-        if (r < 0) {
-            LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r);
-            break;
-        }
-        ptr += r;
-    }
-
-    if ((ptr - data) == max) {
-        tmp = (char*) realloc(data, max + 1);
-        if (tmp == NULL) {
-            free(data);
-            return NULL;
-        }
-    }
-    *ptr = '\0';
-    *sz = (ptr - data);
-    return data;
-}
-
-/*
- * Exercise the Socket class.
- */
-void android::TestSockets(void)
-{
-    printf("----- SOCKET TEST ------\n");
-    Socket::bootInit();
-
-    char* buf = NULL;
-    int len, cc;
-    const char* kTestStr =
-        "GET / HTTP/1.0\n"
-        "Connection: close\n"
-        "\n";
-
-    Socket sock;
-    if (sock.connect("www.google.com", 80) != 0) {
-        fprintf(stderr, "socket connected failed\n");
-        goto bail;
-    }
-
-    cc = sock.write(kTestStr, strlen(kTestStr));
-    if (cc != (int) strlen(kTestStr)) {
-        fprintf(stderr, "write failed, res=%d\n", cc);
-        goto bail;
-    }
-    buf = socketReadAll(sock, &len);
-
-    printf("GOT '%s'\n", buf);
-
-bail:
-    sock.close();
-    free(buf);
-}
-
diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp
index 93f7e4f..4dfa578 100644
--- a/libs/utils/Static.cpp
+++ b/libs/utils/Static.cpp
@@ -20,7 +20,6 @@
 #include <private/utils/Static.h>
 
 #include <utils/BufferedTextOutput.h>
-#include <utils/IPCThreadState.h>
 #include <utils/Log.h>
 
 namespace android {
@@ -87,34 +86,4 @@
 TextOutput& aout(gStdoutTextOutput);
 TextOutput& aerr(gStderrTextOutput);
 
-#ifndef LIBUTILS_NATIVE
-
-// ------------ ProcessState.cpp
-
-Mutex gProcessMutex;
-sp<ProcessState> gProcess;
-
-class LibUtilsIPCtStatics
-{
-public:
-    LibUtilsIPCtStatics()
-    {
-    }
-    
-    ~LibUtilsIPCtStatics()
-    {
-        IPCThreadState::shutdown();
-    }
-};
-
-static LibUtilsIPCtStatics gIPCStatics;
-
-// ------------ ServiceManager.cpp
-
-Mutex gDefaultServiceManagerLock;
-sp<IServiceManager> gDefaultServiceManager;
-sp<IPermissionController> gPermissionController;
-
-#endif
-
 }   // namespace android
diff --git a/libs/utils/StringArray.cpp b/libs/utils/StringArray.cpp
new file mode 100644
index 0000000..aa42d68
--- /dev/null
+++ b/libs/utils/StringArray.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Sortable array of strings.  STL-ish, but STL-free.
+//  
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/StringArray.h>
+
+namespace android {
+
+//
+// An expanding array of strings.  Add, get, sort, delete.
+//
+StringArray::StringArray()
+    : mMax(0), mCurrent(0), mArray(NULL)
+{
+}
+
+StringArray:: ~StringArray() {
+    for (int i = 0; i < mCurrent; i++)
+        delete[] mArray[i];
+    delete[] mArray;
+}
+
+//
+// Add a string.  A copy of the string is made.
+//
+bool StringArray::push_back(const char* str) {
+    if (mCurrent >= mMax) {
+        char** tmp;
+
+        if (mMax == 0)
+            mMax = 16;      // initial storage
+        else
+            mMax *= 2;
+
+        tmp = new char*[mMax];
+        if (tmp == NULL)
+            return false;
+
+        memcpy(tmp, mArray, mCurrent * sizeof(char*));
+        delete[] mArray;
+        mArray = tmp;
+    }
+
+    int len = strlen(str);
+    mArray[mCurrent] = new char[len+1];
+    memcpy(mArray[mCurrent], str, len+1);
+    mCurrent++;
+
+    return true;
+}
+
+//
+// Delete an entry.
+//
+void StringArray::erase(int idx) {
+    if (idx < 0 || idx >= mCurrent)
+        return;
+    delete[] mArray[idx];
+    if (idx < mCurrent-1) {
+        memmove(&mArray[idx], &mArray[idx+1],
+                (mCurrent-1 - idx) * sizeof(char*));
+    }
+    mCurrent--;
+}
+
+//
+// Sort the array.
+//
+void StringArray::sort(int (*compare)(const void*, const void*)) {
+    qsort(mArray, mCurrent, sizeof(char*), compare);
+}
+
+//
+// Pass this to the sort routine to do an ascending alphabetical sort.
+//
+int StringArray::cmpAscendingAlpha(const void* pstr1, const void* pstr2) {
+    return strcmp(*(const char**)pstr1, *(const char**)pstr2);
+}
+
+//
+// Set entry N to specified string.
+// [should use operator[] here]
+//
+void StringArray::setEntry(int idx, const char* str) {
+    if (idx < 0 || idx >= mCurrent)
+        return;
+    delete[] mArray[idx];
+    int len = strlen(str);
+    mArray[idx] = new char[len+1];
+    memcpy(mArray[idx], str, len+1);
+}
+
+
+}; // namespace android
diff --git a/libs/utils/TextOutput.cpp b/libs/utils/TextOutput.cpp
index cebee99..e04823d 100644
--- a/libs/utils/TextOutput.cpp
+++ b/libs/utils/TextOutput.cpp
@@ -22,9 +22,17 @@
 #include <stdlib.h>
 #include <string.h>
 
+namespace android {
+
 // ---------------------------------------------------------------------------
 
-namespace android {
+TextOutput::TextOutput() { 
+}
+
+TextOutput::~TextOutput() { 
+}
+
+// ---------------------------------------------------------------------------
 
 TextOutput& operator<<(TextOutput& to, bool val)
 {
diff --git a/libs/utils/TimerProbe.cpp b/libs/utils/TimerProbe.cpp
deleted file mode 100644
index 835480d..0000000
--- a/libs/utils/TimerProbe.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/TimerProbe.h>
- 
-#if ENABLE_TIMER_PROBE
-
-#ifdef LOG_TAG
-#undef LOG_TAG
-#endif
-#define LOG_TAG "time"
-
-namespace android {
-
-Vector<TimerProbe::Bucket> TimerProbe::gBuckets;
-TimerProbe* TimerProbe::gExecuteChain;
-int TimerProbe::gIndent;
-timespec TimerProbe::gRealBase;
-
-TimerProbe::TimerProbe(const char tag[], int* slot) : mTag(tag)
-{
-    mNext = gExecuteChain;
-    gExecuteChain = this;
-    mIndent = gIndent;
-    gIndent += 1;
-    if (mIndent > 0) {
-        if (*slot == 0) {
-            int count = gBuckets.add();
-            *slot = count;
-            Bucket& bucket = gBuckets.editItemAt(count);
-            memset(&bucket, 0, sizeof(Bucket));
-            bucket.mTag = tag;
-            bucket.mSlotPtr = slot;
-            bucket.mIndent = mIndent;
-        }
-        mBucket = *slot;
-    }
-    clock_gettime(CLOCK_REALTIME, &mRealStart);
-    if (gRealBase.tv_sec == 0)
-        gRealBase = mRealStart;
-    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mPStart);
-    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mTStart);
-}
-
-void TimerProbe::end()
-{
-    timespec realEnd, pEnd, tEnd;
-    clock_gettime(CLOCK_REALTIME, &realEnd);
-    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &pEnd);
-    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tEnd);
-    print(realEnd, pEnd, tEnd);
-    mTag = NULL;
-}
-
-TimerProbe::~TimerProbe()
-{
-    if (mTag != NULL)
-        end();
-    gExecuteChain = mNext;
-    gIndent--;
-}
-
-
-uint32_t TimerProbe::ElapsedTime(const timespec& start, const timespec& end)
-{
-    int sec = end.tv_sec - start.tv_sec;
-    int nsec = end.tv_nsec - start.tv_nsec;
-    if (nsec < 0) {
-        sec--;
-        nsec += 1000000000;
-    }
-    return sec * 1000000 + nsec / 1000;
-}
-
-void TimerProbe::print(const timespec& r, const timespec& p,
-        const timespec& t) const
-{
-    uint32_t es = ElapsedTime(gRealBase, mRealStart);
-    uint32_t er = ElapsedTime(mRealStart, r);
-    uint32_t ep = ElapsedTime(mPStart, p);
-    uint32_t et = ElapsedTime(mTStart, t);
-    if (mIndent > 0) {
-        Bucket& bucket = gBuckets.editItemAt(mBucket);
-        if (bucket.mStart == 0)
-            bucket.mStart = es;
-        bucket.mReal += er;
-        bucket.mProcess += ep;
-        bucket.mThread += et;
-        bucket.mCount++;
-        return;
-    }
-    int index = 0;
-    int buckets = gBuckets.size();
-    int count = 1;
-    const char* tag = mTag;
-    int indent = mIndent;
-    do {
-        LOGD("%-30.30s: (%3d) %-5.*s time=%-10.3f real=%7dus process=%7dus (%3d%%) thread=%7dus (%3d%%)\n", 
-            tag, count, indent > 5 ? 5 : indent, "+++++", es / 1000000.0,
-            er, ep, ep * 100 / er, et, et * 100 / er);
-        if (index >= buckets)
-            break;
-        Bucket& bucket = gBuckets.editItemAt(index);
-        count = bucket.mCount;
-        es = bucket.mStart;
-        er = bucket.mReal;
-        ep = bucket.mProcess;
-        et = bucket.mThread;
-        tag = bucket.mTag;
-        indent = bucket.mIndent;
-        *bucket.mSlotPtr = 0;
-    } while (++index); // always true
-    gBuckets.clear();
-}
-
-}; // namespace android
-
-#endif
diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp
index 2abc811..784f035 100644
--- a/libs/utils/Timers.cpp
+++ b/libs/utils/Timers.cpp
@@ -18,7 +18,6 @@
 // Timer functions.
 //
 #include <utils/Timers.h>
-#include <utils/ported.h>     // may need usleep
 #include <utils/Log.h>
 
 #include <stdlib.h>
@@ -54,130 +53,6 @@
 #endif
 }
 
-//#define MONITOR_USLEEP
-
-/*
- * Sleep long enough that we'll wake up "interval" milliseconds after
- * the previous snooze.
- *
- * The "nextTick" argument is updated on each call, and should be passed
- * in every time.  Set its fields to zero on the first call.
- *
- * Returns the #of intervals we have overslept, which will be zero if we're
- * on time.  [Currently just returns 0 or 1.]
- */
-int sleepForInterval(long interval, struct timeval* pNextTick)
-{
-    struct timeval now;
-    long long timeBeforeNext;
-    long sleepTime = 0;
-    bool overSlept = false;
-    //int usleepBias = 0;
-
-#ifdef USLEEP_BIAS
-    /*
-     * Linux likes to add 9000ms or so.
-     * [not using this for now]
-     */
-    //usleepBias = USLEEP_BIAS;
-#endif
-
-    gettimeofday(&now, NULL);
-
-    if (pNextTick->tv_sec == 0) {
-        /* special-case for first time through */
-        *pNextTick = now;
-        sleepTime = interval;
-        android::DurationTimer::addToTimeval(pNextTick, interval);
-    } else {
-        /*
-         * Compute how much time there is before the next tick.  If this
-         * value is negative, we've run over.  If we've run over a little
-         * bit we can shorten the next frame to keep the pace steady, but
-         * if we've dramatically overshot we need to re-sync.
-         */
-        timeBeforeNext = android::DurationTimer::subtractTimevals(pNextTick, &now);
-        //printf("TOP: now=%ld.%ld next=%ld.%ld diff=%ld\n",
-        //    now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec,
-        //    (long) timeBeforeNext);
-        if (timeBeforeNext < -interval) {
-            /* way over */
-            overSlept = true;
-            sleepTime = 0;
-            *pNextTick = now;
-        } else if (timeBeforeNext <= 0) {
-            /* slightly over, keep the pace steady */
-            overSlept = true;
-            sleepTime = 0;
-        } else if (timeBeforeNext <= interval) {
-            /* right on schedule */
-            sleepTime = timeBeforeNext;
-        } else if (timeBeforeNext > interval && timeBeforeNext <= 2*interval) {
-            /* sleep call returned early; do a longer sleep this time */
-            sleepTime = timeBeforeNext;
-        } else if (timeBeforeNext > interval) {
-            /* we went back in time -- somebody updated system clock? */
-            /* (could also be a *seriously* broken usleep()) */
-            LOG(LOG_DEBUG, "",
-                " Impossible: timeBeforeNext = %ld\n", (long)timeBeforeNext);
-            sleepTime = 0;
-            *pNextTick = now;
-        }
-        android::DurationTimer::addToTimeval(pNextTick, interval);
-    }
-    //printf(" Before sleep: now=%ld.%ld next=%ld.%ld sleepTime=%ld\n",
-    //    now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec,
-    //    sleepTime);
-
-    /*
-     * Sleep for the designated period of time.
-     *
-     * Linux tends to sleep for longer than requested, often by 17-18ms.
-     * MinGW tends to sleep for less than requested, by as much as 14ms,
-     * but occasionally oversleeps for 40+ms (looks like some external
-     * factors plus round-off on a 64Hz clock).  Cygwin is pretty steady.
-     *
-     * If you start the MinGW version, and then launch the Cygwin version,
-     * the MinGW clock becomes more erratic.  Not entirely sure why.
-     *
-     * (There's a lot of stuff here; it's really just a usleep() call with
-     * a bunch of instrumentation.)
-     */
-    if (sleepTime > 0) {
-#if defined(MONITOR_USLEEP)
-        struct timeval before, after;
-        long long actual;
-
-        gettimeofday(&before, NULL);
-        usleep((long) sleepTime);
-        gettimeofday(&after, NULL);
-
-        /* check usleep() accuracy; default Linux threads are pretty sloppy */
-        actual = android::DurationTimer::subtractTimevals(&after, &before);
-        if ((long) actual < sleepTime - 14000 /*(sleepTime/10)*/ ||
-            (long) actual > sleepTime + 20000 /*(sleepTime/10)*/)
-        {
-            LOG(LOG_DEBUG, "", " Odd usleep: req=%ld, actual=%ld\n", sleepTime,
-                (long) actual);
-        }
-#else
-#ifdef HAVE_WIN32_THREADS
-        Sleep( sleepTime/1000 );
-#else        
-        usleep((long) sleepTime);
-#endif        
-#endif
-    }
-
-    //printf("slept %d\n", sleepTime);
-
-    if (overSlept)
-        return 1;       // close enough
-    else
-        return 0;
-}
-
-
 /*
  * ===========================================================================
  *      DurationTimer
diff --git a/libs/utils/executablepath_darwin.cpp b/libs/utils/executablepath_darwin.cpp
deleted file mode 100644
index 2e3c3a0..0000000
--- a/libs/utils/executablepath_darwin.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/executablepath.h>
-#import <Carbon/Carbon.h>
-#include <unistd.h>
-
-void executablepath(char s[PATH_MAX])
-{
-    ProcessSerialNumber psn;
-    GetCurrentProcess(&psn);
-    CFDictionaryRef dict;
-    dict = ProcessInformationCopyDictionary(&psn, 0xffffffff);
-    CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict,
-                CFSTR("CFBundleExecutable"));
-    CFStringGetCString(value, s, PATH_MAX+1, kCFStringEncodingUTF8);
-}
-
diff --git a/libs/utils/ported.cpp b/libs/utils/ported.cpp
deleted file mode 100644
index 656e46f..0000000
--- a/libs/utils/ported.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Ports of standard functions that don't exist on a specific platform.
-//
-// Note these are NOT in the "android" namespace.
-//
-#include <utils/ported.h>
-
-#if defined(NEED_GETTIMEOFDAY) || defined(NEED_USLEEP)
-# include <sys/time.h>
-# include <windows.h>
-#endif
-
-
-#if defined(NEED_GETTIMEOFDAY)
-/*
- * Replacement gettimeofday() for Windows environments (primarily MinGW).
- *
- * Ignores "tz".
- */
-int gettimeofday(struct timeval* ptv, struct timezone* tz)
-{
-    long long nsTime;   // time in 100ns units since Jan 1 1601
-    FILETIME ft;
-
-    if (tz != NULL) {
-        // oh well
-    }
-
-    ::GetSystemTimeAsFileTime(&ft);
-    nsTime = (long long) ft.dwHighDateTime << 32 |
-             (long long) ft.dwLowDateTime;
-    // convert to time in usec since Jan 1 1970
-    ptv->tv_usec = (long) ((nsTime / 10LL) % 1000000LL);
-    ptv->tv_sec = (long) ((nsTime - 116444736000000000LL) / 10000000LL);
-
-    return 0;
-}
-#endif
-
-#if defined(NEED_USLEEP)
-//
-// Replacement usleep for Windows environments (primarily MinGW).
-//
-void usleep(unsigned long usec)
-{
-    // Win32 API function Sleep() takes milliseconds
-    ::Sleep((usec + 500) / 1000);
-}
-#endif
-
-#if 0 //defined(NEED_PIPE)
-//
-// Replacement pipe() command for MinGW
-//
-// The _O_NOINHERIT flag sets bInheritHandle to FALSE in the
-// SecurityAttributes argument to CreatePipe().  This means the handles
-// aren't inherited when a new process is created.  The examples I've seen
-// use it, possibly because there's a lot of junk going on behind the
-// scenes.  (I'm assuming "process" and "thread" are different here, so
-// we should be okay spinning up a thread.)  The recommended practice is
-// to dup() the descriptor you want the child to have.
-//
-// It appears that unnamed pipes can't do non-blocking ("overlapped") I/O.
-// You can't use select() either, since that only works on sockets.  The
-// Windows API calls that are useful here all operate on a HANDLE, not
-// an integer file descriptor, and I don't think you can get there from
-// here.  The "named pipe" stuff is insane.
-//
-int pipe(int filedes[2])
-{
-    return _pipe(filedes, 0, _O_BINARY | _O_NOINHERIT);
-}
-#endif
-
-#if defined(NEED_SETENV)
-/*
- * MinGW lacks these.  For now, just stub them out so the code compiles.
- */
-int setenv(const char* name, const char* value, int overwrite)
-{
-    return 0;
-}
-void unsetenv(const char* name)
-{
-}
-char* getenv(const char* name)
-{
-    return NULL;
-}
-#endif
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java
index 53e46b7..2ce1273 100644
--- a/location/java/android/location/Geocoder.java
+++ b/location/java/android/location/Geocoder.java
@@ -36,11 +36,11 @@
  * coordinate into a (partial) address.  The amount of detail in a
  * reverse geocoded location description may vary, for example one
  * might contain the full street address of the closest building, while
- * another might contain only a city name and postal code. 
+ * another might contain only a city name and postal code.
  *
  * The Geocoder class requires a backend service that is not included in
- * the core android framework. The Geocoder query methods will return an
- * empty list if there no backend service in the platform. 
+ * the core android framework.  The Geocoder query methods will return an
+ * empty list if there no backend service in the platform.
  */
 public final class Geocoder {
     private static final String TAG = "Geocoder";
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index edd1ea0..883e5f5 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.util.Config;
 import android.util.Log;
 import android.util.SparseIntArray;
@@ -183,7 +184,7 @@
     // number of fixes we have received since we started navigating
     private int mFixCount;
 
-    private int mPositionMode = GPS_POSITION_MODE_STANDALONE;
+    private boolean mAgpsConfigured;
 
     // true if we started navigation
     private boolean mStarted;
@@ -355,8 +356,7 @@
                 try {
                     int port = Integer.parseInt(portString);
                     native_set_agps_server(AGPS_TYPE_SUPL, host, port);
-                    // use MS-Based position mode if SUPL support is enabled
-                    mPositionMode = GPS_POSITION_MODE_MS_BASED;
+                    mAgpsConfigured = true;
                 } catch (NumberFormatException e) {
                     Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
                 }
@@ -368,8 +368,7 @@
                 try {
                     int port = Integer.parseInt(portString);
                     native_set_agps_server(AGPS_TYPE_C2K, host, port);
-                    // use MS-Based position mode if SUPL support is enabled
-                    mPositionMode = GPS_POSITION_MODE_MS_BASED;
+                    mAgpsConfigured = true;
                 } catch (NumberFormatException e) {
                     Log.e(TAG, "unable to parse C2K_PORT: " + portString);
                 }
@@ -719,7 +718,15 @@
         if (!mStarted) {
             if (DEBUG) Log.d(TAG, "startNavigating");
             mStarted = true;
-            if (!native_start(mPositionMode, false, mFixInterval)) {
+            int positionMode;
+            if (mAgpsConfigured && Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ASSISTED_GPS_ENABLED, 0) != 0) {
+                positionMode = GPS_POSITION_MODE_MS_BASED;
+            } else {
+                positionMode = GPS_POSITION_MODE_STANDALONE;
+            }
+
+            if (!native_start(positionMode, false, mFixInterval)) {
                 mStarted = false;
                 Log.e(TAG, "native_start failed in startNavigating()");
                 return;
diff --git a/location/java/com/android/internal/location/GpsXtraDownloader.java b/location/java/com/android/internal/location/GpsXtraDownloader.java
index 2a8be57..33ebce7 100644
--- a/location/java/com/android/internal/location/GpsXtraDownloader.java
+++ b/location/java/com/android/internal/location/GpsXtraDownloader.java
@@ -64,6 +64,7 @@
         
         if (count == 0) {
             Log.e(TAG, "No XTRA servers were specified in the GPS configuration");
+            return;
         } else {
             mXtraServers = new String[count];
             count = 0;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 3b46d69..298cce9 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -23,6 +23,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.util.Log;
@@ -435,6 +436,10 @@
     }
 
     private final static String TAG = "MediaPlayer";
+    // Name of the remote interface for the media player. Must be kept
+    // in sync with the 2nd parameter of the IMPLEMENT_META_INTERFACE
+    // macro invocation in IMediaPlayer.cpp
+    private final static String IMEDIA_PLAYER = "android.media.IMediaPlayer";
 
     private int mNativeContext; // accessed by native methods
     private int mListenerContext; // accessed by native methods
@@ -475,6 +480,43 @@
     private native void _setVideoSurface();
 
     /**
+     * Create a request parcel which can be routed to the native media
+     * player using {@link #invoke(Parcel, Parcel)}. The Parcel
+     * returned has the proper InterfaceToken set. The caller should
+     * not overwrite that token, i.e it can only append data to the
+     * Parcel.
+     *
+     * @return A parcel suitable to hold a request for the native
+     * player.
+     */
+    public Parcel newRequest() {
+        Parcel parcel = Parcel.obtain();
+        parcel.writeInterfaceToken(IMEDIA_PLAYER);
+        return parcel;
+    }
+
+    /**
+     * Invoke a generic method on the native player using opaque
+     * parcels for the request and reply. Both payloads' format is a
+     * convention between the java caller and the native player.
+     * Must be called after setDataSource to make sure a native player
+     * exists.
+     *
+     * @param request Parcel with the data for the extension. The
+     * caller must use {@link #newRequest()} to get one.
+     *
+     * @param[out] reply Parcel with the data returned by the
+     * native player.
+     *
+     * @return The status code see utils/Errors.h
+     */
+    public int invoke(Parcel request, Parcel reply) {
+        int retcode = native_invoke(request, reply);
+        reply.setDataPosition(0);
+        return retcode;
+    }
+
+    /**
      * Sets the SurfaceHolder to use for displaying the video portion of the media.
      * This call is optional. Not calling it when playing back a video will
      * result in only the audio track being played.
@@ -915,8 +957,18 @@
      */
     public native Bitmap getFrameAt(int msec) throws IllegalStateException;
 
+    /**
+     * @param request Parcel destinated to the media player. The
+     *                Interface token must be set to the IMediaPlayer
+     *                one to be routed correctly through the system.
+     * @param reply Parcel that will contain the reply.
+     * @return The status code.
+     */
+    private native final int native_invoke(Parcel request, Parcel reply);
+
     private native final void native_setup(Object mediaplayer_this);
     private native final void native_finalize();
+
     @Override
     protected void finalize() { native_finalize(); }
 
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 3dd8563..c46f64e 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -307,10 +307,14 @@
     private boolean mDefaultRingtoneSet;
     /** Whether the scanner has set a default sound for the notification ringtone. */
     private boolean mDefaultNotificationSet;
+    /** Whether the scanner has set a default sound for the alarm ringtone. */
+    private boolean mDefaultAlarmSet;
     /** The filename for the default sound for the ringer ringtone. */
     private String mDefaultRingtoneFilename;
     /** The filename for the default sound for the notification ringtone. */
     private String mDefaultNotificationFilename;
+    /** The filename for the default sound for the alarm ringtone. */
+    private String mDefaultAlarmAlertFilename;
     /**
      * The prefix for system properties that define the default sound for
      * ringtones. Concatenate the name of the setting from Settings
@@ -369,6 +373,8 @@
                 + Settings.System.RINGTONE);
         mDefaultNotificationFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
                 + Settings.System.NOTIFICATION_SOUND);
+        mDefaultAlarmAlertFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
+                + Settings.System.ALARM_ALERT);
     }
     
     private MyMediaScannerClient mClient = new MyMediaScannerClient();
@@ -748,6 +754,12 @@
                     setSettingIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
                     mDefaultRingtoneSet = true;
                 }
+            } else if (alarms && !mDefaultAlarmSet) {
+                if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
+                        doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
+                    setSettingIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
+                    mDefaultAlarmSet = true;
+                }
             }
             
             return result;
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 8f05cec..70d29d2 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -122,8 +122,9 @@
      * current ringtone, which will be used to show a checkmark next to the item
      * for this {@link Uri}. If showing an item for "Default" (@see
      * {@link #EXTRA_RINGTONE_SHOW_DEFAULT}), this can also be one of
-     * {@link System#DEFAULT_RINGTONE_URI} or
-     * {@link System#DEFAULT_NOTIFICATION_URI} to have the "Default" item
+     * {@link System#DEFAULT_RINGTONE_URI},
+     * {@link System#DEFAULT_NOTIFICATION_URI}, or
+     * {@link System#DEFAULT_ALARM_ALERT_URI} to have the "Default" item
      * checked.
      * 
      * @see #ACTION_RINGTONE_PICKER
@@ -134,8 +135,9 @@
     /**
      * Given to the ringtone picker as a {@link Uri}. The {@link Uri} of the
      * ringtone to play when the user attempts to preview the "Default"
-     * ringtone. This can be one of {@link System#DEFAULT_RINGTONE_URI} or
-     * {@link System#DEFAULT_NOTIFICATION_URI} to have the "Default" point to
+     * ringtone. This can be one of {@link System#DEFAULT_RINGTONE_URI},
+     * {@link System#DEFAULT_NOTIFICATION_URI}, or
+     * {@link System#DEFAULT_ALARM_ALERT_URI} to have the "Default" point to
      * the current sound for the given default sound type. If you are showing a
      * ringtone picker for some other type of sound, you are free to provide any
      * {@link Uri} here.
@@ -163,8 +165,9 @@
      * <p>
      * It will be one of:
      * <li> the picked ringtone,
-     * <li> a {@link Uri} that equals {@link System#DEFAULT_RINGTONE_URI} or
-     * {@link System#DEFAULT_NOTIFICATION_URI} if the default was chosen,
+     * <li> a {@link Uri} that equals {@link System#DEFAULT_RINGTONE_URI},
+     * {@link System#DEFAULT_NOTIFICATION_URI}, or
+     * {@link System#DEFAULT_ALARM_ALERT_URI} if the default was chosen,
      * <li> null if the "Silent" item was picked.
      * 
      * @see #ACTION_RINGTONE_PICKER
@@ -624,7 +627,8 @@
      * 
      * @param context A context used for querying.
      * @param type The type whose default sound should be returned. One of
-     *            {@link #TYPE_RINGTONE} or {@link #TYPE_NOTIFICATION}.
+     *            {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or
+     *            {@link #TYPE_ALARM}.
      * @return A {@link Uri} pointing to the default sound for the sound type.
      * @see #setActualDefaultRingtoneUri(Context, int, Uri)
      */
@@ -640,7 +644,8 @@
      * 
      * @param context A context used for querying.
      * @param type The type whose default sound should be set. One of
-     *            {@link #TYPE_RINGTONE} or {@link #TYPE_NOTIFICATION}.
+     *            {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or
+     *            {@link #TYPE_ALARM}.
      * @param ringtoneUri A {@link Uri} pointing to the default sound to set.
      * @see #getActualDefaultRingtoneUri(Context, int)
      */
@@ -655,6 +660,8 @@
             return Settings.System.RINGTONE;
         } else if ((type & TYPE_NOTIFICATION) != 0) {
             return Settings.System.NOTIFICATION_SOUND;
+        } else if ((type & TYPE_ALARM) != 0) {
+            return Settings.System.ALARM_ALERT;
         } else {
             return null;
         }
@@ -674,8 +681,9 @@
      * Returns the type of a default {@link Uri}.
      * 
      * @param defaultRingtoneUri The default {@link Uri}. For example,
-     *            {@link System#DEFAULT_RINGTONE_URI} or
-     *            {@link System#DEFAULT_NOTIFICATION_URI}.
+     *            {@link System#DEFAULT_RINGTONE_URI},
+     *            {@link System#DEFAULT_NOTIFICATION_URI}, or
+     *            {@link System#DEFAULT_ALARM_ALERT_URI}.
      * @return The type of the defaultRingtoneUri, or -1.
      */
     public static int getDefaultType(Uri defaultRingtoneUri) {
@@ -685,6 +693,8 @@
             return TYPE_RINGTONE;
         } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_NOTIFICATION_URI)) {
             return TYPE_NOTIFICATION;
+        } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_ALARM_ALERT_URI)) {
+            return TYPE_ALARM;
         } else {
             return -1;
         }
@@ -704,6 +714,8 @@
             return Settings.System.DEFAULT_RINGTONE_URI;
         } else if ((type & TYPE_NOTIFICATION) != 0) {
             return Settings.System.DEFAULT_NOTIFICATION_URI;
+        } else if ((type & TYPE_ALARM) != 0) {
+            return Settings.System.DEFAULT_ALARM_ALERT_URI;
         } else {
             return null;
         }
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 8ee0cbd..f19649b 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -18,6 +18,7 @@
     libnativehelper \
     libcutils \
     libutils \
+    libbinder \
     libmedia \
     libsgl \
     libui
@@ -26,6 +27,7 @@
 
 LOCAL_C_INCLUDES += \
     external/tremor/Tremor \
+    frameworks/base/core/jni \
     $(PV_INCLUDES) \
     $(JNI_H_INCLUDE) \
     $(call include-path-for, corecg graphics)
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 6317fe2..2c08c16 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -20,6 +20,7 @@
 #include "utils/Log.h"
 
 #include <media/mediaplayer.h>
+#include <media/MediaPlayerInterface.h>
 #include <stdio.h>
 #include <assert.h>
 #include <limits.h>
@@ -30,6 +31,8 @@
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "utils/Errors.h"  // for status_t
+#include "android_util_Binder.h"
+#include <binder/Parcel.h>
 
 
 // ----------------------------------------------------------------------------
@@ -442,6 +445,28 @@
     return NULL;
 }
 
+
+// Sends the request and reply parcels to the media player via the
+// binder interface.
+static jint
+android_media_MediaPlayer_invoke(JNIEnv *env, jobject thiz,
+                                 jobject java_request, jobject java_reply)
+{
+    sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
+    if (media_player == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+    }
+
+
+    Parcel *request = parcelForJavaObject(env, java_request);
+    Parcel *reply = parcelForJavaObject(env, java_reply);
+
+    const status_t status = media_player->invoke(*request, reply);
+    // Don't use process_media_player_call which use the async loop to
+    // report errors, instead returns the status.
+    return status;
+}
+
 static void
 android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
 {
@@ -503,6 +528,7 @@
     {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer_isLooping},
     {"setVolume",           "(FF)V",                            (void *)android_media_MediaPlayer_setVolume},
     {"getFrameAt",          "(I)Landroid/graphics/Bitmap;",     (void *)android_media_MediaPlayer_getFrameAt},
+    {"native_invoke",       "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
     {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},
     {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
 };
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 374ddeb..9ff2e24 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -9,6 +9,7 @@
 LOCAL_SHARED_LIBRARIES := \
 	libcutils \
 	libutils \
+	libbinder \
 	libandroid_runtime \
 	libnativehelper \
 	libmedia
diff --git a/media/libdrm/mobile2/include/rights/RoManager.h b/media/libdrm/mobile2/include/rights/RoManager.h
index cf398b3..71e9eef 100644
--- a/media/libdrm/mobile2/include/rights/RoManager.h
+++ b/media/libdrm/mobile2/include/rights/RoManager.h
@@ -64,12 +64,6 @@
     vector<Ro*> getAllRo();
 
     /**
-     * Get the private key of the device.
-     * @return the private key.
-     */
-    const string& getDevicePrivateKey() const;
-
-    /**
      * Get ro which contained rights of specific content.
      * @param contentID the specific id of content.
      * @return NULL if not fount otherwise the related ro.
diff --git a/media/libdrm/mobile2/src/rights/RoManager.cpp b/media/libdrm/mobile2/src/rights/RoManager.cpp
index 848c2ba..a115d21 100644
--- a/media/libdrm/mobile2/src/rights/RoManager.cpp
+++ b/media/libdrm/mobile2/src/rights/RoManager.cpp
@@ -121,9 +121,3 @@
     return true;
 }
 
-/** see RoManager.h */
-const string& RoManager::getDevicePrivateKey() const
-{
-    string pk;
-    return pk;
-}
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 8020da2..806ef52 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -21,7 +21,7 @@
 	JetPlayer.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libui libcutils libutils libsonivox
+	libui libcutils libutils libbinder libsonivox
 
 LOCAL_MODULE:= libmedia
 
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 4c8b02a..cf0965e 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -29,11 +29,11 @@
 #include <media/AudioSystem.h>
 #include <media/AudioRecord.h>
 
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
 #include <utils/Log.h>
-#include <utils/MemoryDealer.h>
-#include <utils/Parcel.h>
-#include <utils/IPCThreadState.h>
+#include <binder/MemoryDealer.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
 #include <utils/Timers.h>
 #include <cutils/atomic.h>
 
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index a21a7a4..86d0542 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -18,7 +18,7 @@
 //#define LOG_NDEBUG 0
 
 #include <utils/Log.h>
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
 #include <media/AudioSystem.h>
 #include <math.h>
 
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 289bd75..4a1b69e 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -32,9 +32,9 @@
 #include <media/AudioTrack.h>
 
 #include <utils/Log.h>
-#include <utils/MemoryDealer.h>
-#include <utils/Parcel.h>
-#include <utils/IPCThreadState.h>
+#include <binder/MemoryDealer.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
 #include <utils/Timers.h>
 #include <cutils/atomic.h>
 
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index eeaa54f..6fc0cb7 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -21,7 +21,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 
 #include <media/IAudioFlinger.h>
 
@@ -353,12 +353,6 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnAudioFlinger::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp
index 9d00aefb..75699b4 100644
--- a/media/libmedia/IAudioFlingerClient.cpp
+++ b/media/libmedia/IAudioFlingerClient.cpp
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 
 #include <media/IAudioFlingerClient.h>
 
@@ -51,12 +51,6 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnAudioFlingerClient::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp
index 6e42dac..8fb5d3d 100644
--- a/media/libmedia/IAudioRecord.cpp
+++ b/media/libmedia/IAudioRecord.cpp
@@ -18,7 +18,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 
 #include <media/IAudioRecord.h>
 
@@ -66,12 +66,6 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnAudioRecord::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp
index abc202d..75b861b 100644
--- a/media/libmedia/IAudioTrack.cpp
+++ b/media/libmedia/IAudioTrack.cpp
@@ -18,7 +18,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 
 #include <media/IAudioTrack.h>
 
@@ -91,12 +91,6 @@
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnAudioTrack::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index 85b5944..397a55b 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -17,7 +17,7 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 #include <SkBitmap.h>
 #include <media/IMediaMetadataRetriever.h>
 
@@ -126,16 +126,10 @@
     }
 };
 
-IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.hardware.IMediaMetadataRetriever");
+IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnMediaMetadataRetriever::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
@@ -215,4 +209,3 @@
 // ----------------------------------------------------------------------------
 
 }; // namespace android
-
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index f18765a..3f278f4 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -18,7 +18,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 
 #include <media/IMediaPlayer.h>
 #include <ui/ISurface.h>
@@ -39,7 +39,8 @@
     RESET,
     SET_AUDIO_STREAM_TYPE,
     SET_LOOPING,
-    SET_VOLUME
+    SET_VOLUME,
+    INVOKE,
 };
 
 class BpMediaPlayer: public BpInterface<IMediaPlayer>
@@ -170,18 +171,19 @@
         remote()->transact(SET_VOLUME, data, &reply);
         return reply.readInt32();
     }
+
+    status_t invoke(const Parcel& request, Parcel *reply)
+    { // Avoid doing any extra copy. The interface descriptor should
+      // have been set by MediaPlayer.java.
+        status_t retcode = remote()->transact(INVOKE, request, reply);
+        return retcode;
+    }
 };
 
-IMPLEMENT_META_INTERFACE(MediaPlayer, "android.hardware.IMediaPlayer");
+IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer");
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnMediaPlayer::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
@@ -266,6 +268,11 @@
             reply->writeInt32(setVolume(data.readFloat(), data.readFloat()));
             return NO_ERROR;
         } break;
+        case INVOKE: {
+            CHECK_INTERFACE(IMediaPlayer, data, reply);
+            invoke(data, reply);
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
@@ -274,4 +281,3 @@
 // ----------------------------------------------------------------------------
 
 }; // namespace android
-
diff --git a/media/libmedia/IMediaPlayerClient.cpp b/media/libmedia/IMediaPlayerClient.cpp
index 65022cd..bf51829 100644
--- a/media/libmedia/IMediaPlayerClient.cpp
+++ b/media/libmedia/IMediaPlayerClient.cpp
@@ -16,8 +16,8 @@
 */
 
 #include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
 
 #include <media/IMediaPlayerClient.h>
 
@@ -46,16 +46,10 @@
     }
 };
 
-IMPLEMENT_META_INTERFACE(MediaPlayerClient, "android.hardware.IMediaPlayerClient");
+IMPLEMENT_META_INTERFACE(MediaPlayerClient, "android.media.IMediaPlayerClient");
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnMediaPlayerClient::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
@@ -74,4 +68,3 @@
 }
 
 }; // namespace android
-
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index 01cdb6c..0f64259 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -17,9 +17,10 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
+#include <utils/Errors.h>  // for status_t
 #include <media/IMediaPlayerService.h>
 #include <media/IMediaRecorder.h>
 
@@ -111,16 +112,10 @@
     }
 };
 
-IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.hardware.IMediaPlayerService");
+IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.media.IMediaPlayerService");
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-            LOGW("Call incorrectly routed to " #interface); \
-            return PERMISSION_DENIED; \
-        } } while (0)
-
 status_t BnMediaPlayerService::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 84d08c4..df7d301 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -18,7 +18,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "IMediaRecorder"
 #include <utils/Log.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
 #include <ui/ISurface.h>
 #include <ui/ICamera.h>
 #include <media/IMediaPlayerClient.h>
@@ -264,16 +264,10 @@
     }
 };
 
-IMPLEMENT_META_INTERFACE(MediaRecorder, "android.hardware.IMediaRecorder");
+IMPLEMENT_META_INTERFACE(MediaRecorder, "android.media.IMediaRecorder");
 
 // ----------------------------------------------------------------------
 
-#define CHECK_INTERFACE(interface, data, reply) \
-    do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
-        LOGW("Call incorrectly routed to " #interface); \
-        return PERMISSION_DENIED; \
-    } } while (0)
-
 status_t BnMediaRecorder::onTransact(
                                      uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index 09afc6c..d34a8ed 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -18,8 +18,8 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaMetadataRetriever"
 
-#include <utils/IServiceManager.h>
-#include <utils/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
 #include <media/mediametadataretriever.h>
 #include <media/IMediaPlayerService.h>
 #include <utils/Log.h>
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 24e3e6f..4683166 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -24,13 +24,13 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-#include <utils/IServiceManager.h>
-#include <utils/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
 
 #include <media/mediaplayer.h>
 #include <media/AudioTrack.h>
 
-#include <utils/MemoryBase.h>
+#include <binder/MemoryBase.h>
 
 namespace android {
 
@@ -196,6 +196,20 @@
     return err;
 }
 
+status_t MediaPlayer::invoke(const Parcel& request, Parcel *reply)
+{
+    Mutex::Autolock _l(mLock);
+    if ((mPlayer != NULL) && ( mCurrentState & MEDIA_PLAYER_INITIALIZED ))
+    {
+         LOGV("invoke %d", request.dataSize());
+         return  mPlayer->invoke(request, reply);
+    }
+    LOGE("invoke failed: wrong state %X", mCurrentState);
+    return INVALID_OPERATION;
+}
+
+
+
 status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface)
 {
     LOGV("setVideoSurface");
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 5093f0e..6b63931 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -20,7 +20,7 @@
 #include <utils/Log.h>
 #include <ui/Surface.h>
 #include <media/mediarecorder.h>
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
 #include <utils/String8.h>
 #include <media/IMediaPlayerService.h>
 #include <media/IMediaRecorder.h>
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index f7f2490..0877142 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -20,6 +20,7 @@
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
     libutils \
+	libbinder \
     libvorbisidec \
     libsonivox \
     libopencore_player \
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 31eecac..e39495b 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -30,10 +30,13 @@
 #include <cutils/atomic.h>
 
 #include <android_runtime/ActivityManager.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
-#include <utils/MemoryHeapBase.h>
-#include <utils/MemoryBase.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+#include <utils/Errors.h>  // for status_t
+#include <utils/String8.h>
+#include <utils/Vector.h>
 #include <cutils/properties.h>
 
 #include <media/MediaPlayerInterface.h>
@@ -61,7 +64,6 @@
 #undef __KERNEL__
 #endif
 
-
 namespace android {
 
 // TODO: Temp hack until we can register players
@@ -105,7 +107,11 @@
 
 sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(pid_t pid)
 {
+#ifndef NO_OPENCORE
     sp<MediaRecorderClient> recorder = new MediaRecorderClient(pid);
+#else
+    sp<MediaRecorderClient> recorder = NULL;
+#endif
     LOGV("Create new media recorder client from pid %d", pid);
     return recorder;
 }
@@ -532,10 +538,12 @@
 {
     sp<MediaPlayerBase> p;
     switch (playerType) {
+#ifndef NO_OPENCORE
         case PV_PLAYER:
             LOGV(" create PVPlayer");
             p = new PVPlayer();
             break;
+#endif
         case SONIVOX_PLAYER:
             LOGV(" create MidiFile");
             p = new MidiFile();
@@ -665,6 +673,14 @@
     return p->setVideoSurface(surface);
 }
 
+status_t MediaPlayerService::Client::invoke(const Parcel& request,
+                                            Parcel *reply)
+{
+    sp<MediaPlayerBase> p = getPlayer();
+    if (p == NULL) return UNKNOWN_ERROR;
+    return p->invoke(request, reply);
+}
+
 status_t MediaPlayerService::Client::prepareAsync()
 {
     LOGV("[%d] prepareAsync", mConnId);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index f138886..12f2231 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -18,7 +18,10 @@
 #ifndef ANDROID_MEDIAPLAYERSERVICE_H
 #define ANDROID_MEDIAPLAYERSERVICE_H
 
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <ui/SurfaceComposerClient.h>
 
@@ -184,6 +187,7 @@
         virtual status_t        setAudioStreamType(int type);
         virtual status_t        setLooping(int loop);
         virtual status_t        setVolume(float leftVolume, float rightVolume);
+        virtual status_t        invoke(const Parcel& request, Parcel *reply);
 
         sp<MediaPlayerBase>     createPlayer(player_type playerType);
                 status_t        setDataSource(const char *url);
@@ -235,4 +239,3 @@
 }; // namespace android
 
 #endif // ANDROID_MEDIAPLAYERSERVICE_H
-
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 8bc410c..e54f20d 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -25,10 +25,10 @@
 #include <string.h>
 #include <cutils/atomic.h>
 #include <android_runtime/ActivityManager.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
-#include <utils/MemoryHeapBase.h>
-#include <utils/MemoryBase.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
 #include <media/PVMediaRecorder.h>
 #include <utils/String16.h>
 
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index a320bd5..ba8d9a8 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -26,10 +26,10 @@
 
 #include <string.h>
 #include <cutils/atomic.h>
-#include <utils/MemoryDealer.h>
+#include <binder/MemoryDealer.h>
 #include <android_runtime/ActivityManager.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
 #include <media/MediaMetadataRetrieverInterface.h>
 #include <media/MediaPlayerInterface.h>
 #include <media/PVMetadataRetriever.h>
@@ -49,7 +49,11 @@
     mThumbnail = NULL;
     mAlbumArt = NULL;
 
+#ifndef NO_OPENCORE
     mRetriever = new PVMetadataRetriever();
+#else
+    mRetriever = NULL;
+#endif
     if (mRetriever == NULL) {
         LOGE("failed to initialize the retriever");
     }
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index ce29c98..88d50bf 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -18,9 +18,12 @@
 #ifndef ANDROID_MEDIAMETADATARETRIEVERSERVICE_H
 #define ANDROID_MEDIAMETADATARETRIEVERSERVICE_H
 
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
 #include <utils/KeyedVector.h>
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 
 #include <media/MediaMetadataRetrieverInterface.h>
 
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
index 302f1cf..83d97fe 100644
--- a/media/libmediaplayerservice/MidiFile.h
+++ b/media/libmediaplayerservice/MidiFile.h
@@ -46,6 +46,7 @@
     virtual status_t    reset();
     virtual status_t    setLooping(int loop);
     virtual player_type playerType() { return SONIVOX_PLAYER; }
+    virtual status_t    invoke(const Parcel& request, Parcel *reply) {return INVALID_OPERATION;}
 
 private:
             status_t    createOutputTrack();
@@ -74,4 +75,3 @@
 }; // namespace android
 
 #endif // ANDROID_MIDIFILE_H
-
diff --git a/media/libmediaplayerservice/VorbisPlayer.h b/media/libmediaplayerservice/VorbisPlayer.h
index c30dc1b..4024654 100644
--- a/media/libmediaplayerservice/VorbisPlayer.h
+++ b/media/libmediaplayerservice/VorbisPlayer.h
@@ -53,6 +53,7 @@
     virtual status_t    reset();
     virtual status_t    setLooping(int loop);
     virtual player_type playerType() { return VORBIS_PLAYER; }
+    virtual status_t    invoke(const Parcel& request, Parcel *reply) {return INVALID_OPERATION;}
 
 private:
             status_t    setdatasource(const char *path, int fd, int64_t offset, int64_t length);
@@ -88,4 +89,3 @@
 }; // namespace android
 
 #endif // ANDROID_VORBISPLAYER_H
-
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index c681698..a92cea8 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -8,7 +8,8 @@
 	libaudioflinger \
 	libcameraservice \
 	libmediaplayerservice \
-	libutils
+	libutils \
+	libbinder
 
 base := $(LOCAL_PATH)/../..
 
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index 6954b63..fbea0d4 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -20,9 +20,9 @@
 #include <unistd.h>
 #include <grp.h>
 
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
 #include <utils/Log.h>
 
 #include <AudioFlinger.h>
diff --git a/media/sdutils/sdutil.cpp b/media/sdutils/sdutil.cpp
index a9aabf0..6f0cdfb2c 100644
--- a/media/sdutils/sdutil.cpp
+++ b/media/sdutils/sdutil.cpp
@@ -15,8 +15,8 @@
  */
 
 #include <hardware_legacy/IMountService.h>
-#include <utils/BpBinder.h>
-#include <utils/IServiceManager.h>
+#include <binder/BpBinder.h>
+#include <binder/IServiceManager.h>
 
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index 84058f5..4adb04e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -47,7 +47,7 @@
  */
 public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<MediaFrameworkTest> {
 
-    private String TAG = "MediaFrameworkPerformance";
+    private String TAG = "MediaPlayerPerformance";
 
     private SQLiteDatabase mDB;
     private SurfaceHolder mSurfaceHolder = null;
@@ -76,9 +76,11 @@
 
     public void createDB() {
         mDB = SQLiteDatabase.openOrCreateDatabase("/sdcard/perf.db", null);
-        mDB.execSQL("CREATE TABLE perfdata (_id INTEGER PRIMARY KEY," + 
+        mDB.execSQL("CREATE TABLE IF NOT EXISTS perfdata (_id INTEGER PRIMARY KEY," + 
                 "file TEXT," + "setdatatime LONG," + "preparetime LONG," +
                 "playtime LONG" + ");");
+        //clean the table before adding new data
+        mDB.execSQL("DELETE FROM perfdata");
     }
 
     public void audioPlaybackStartupTime(String[] testFile) {
@@ -137,6 +139,10 @@
         audioPlaybackStartupTime(MediaNames.MP3FILES);
         audioPlaybackStartupTime(MediaNames.AACFILES);
 
+        //close the database after all transactions
+        if (mDB.isOpen()) {
+            mDB.close();
+        }
     }
 
     public void wmametadatautility(String[] testFile) {
diff --git a/obex/Android.mk b/obex/Android.mk
new file mode 100644
index 0000000..fbfe9be
--- /dev/null
+++ b/obex/Android.mk
@@ -0,0 +1,9 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_MODULE:= javax.obex
+
+include $(BUILD_JAVA_LIBRARY)
diff --git a/obex/javax/obex/ApplicationParameter.java b/obex/javax/obex/ApplicationParameter.java
new file mode 100644
index 0000000..e808360
--- /dev/null
+++ b/obex/javax/obex/ApplicationParameter.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * @hide
+ */
+public final class ApplicationParameter {
+
+    private byte[] b_array;
+    private int length;
+    private int max_length = 1000;
+
+    public static class TRIPLET_TAGID {
+        public static final byte ORDER_TAGID = 0x01;
+
+        public static final byte SEARCH_VALUE_TAGID = 0x02;
+
+        public static final byte SEARCH_ATTRIBUTE_TAGID = 0x03;
+
+        // if equals to "0", PSE only reply number of contacts
+        public static final byte MAXLISTCOUNT_TAGID = 0x04;
+
+        public static final byte LISTSTARTOFFSET_TAGID = 0x05;
+
+        public static final byte FILTER_TAGID = 0x06;
+
+        public static final byte FORMAT_TAGID = 0x07;
+
+        // only used if max list count = 0
+        public static final byte PHONEBOOKSIZE_TAGID = 0x08;
+
+        // only used in "mch" in response
+        public static final byte NEWMISSEDCALLS_TAGID = 0x09;
+    }
+
+    public static class TRIPLET_VALUE {
+        public static class ORDER {
+            public static final byte ORDER_BY_INDEX = 0x00;
+
+            public static final byte ORDER_BY_ALPHANUMERIC = 0x01;
+
+            public static final byte ORDER_BY_PHONETIC = 0x02;
+        }
+
+        public static class SEARCHATTRIBUTE {
+            public static final byte SEARCH_BY_NAME = 0x00;
+
+            public static final byte SEARCH_BY_NUMBER = 0x01;
+
+            public static final byte SEARCH_BY_SOUND = 0x02;
+        }
+
+        public static class FORMAT {
+            public static final byte VCARD_VERSION_21 = 0x00;
+
+            public static final byte VCARD_VERSION_30 = 0x01;
+        }
+    }
+
+    public static class TRIPLET_LENGTH {
+        public static final byte ORDER_LENGTH = 1;
+
+        //public final byte SEARCH_VALUE_LENGTH = 0x02;
+        public static final byte SEARCH_ATTRIBUTE_LENGTH = 1;
+
+        public static final byte MAXLISTCOUNT_LENGTH = 2;
+
+        public static final byte LISTSTARTOFFSET_LENGTH = 2;
+
+        public static final byte FILTER_LENGTH = 8;
+
+        public static final byte FORMAT_LENGTH = 1;
+
+        public static final byte PHONEBOOKSIZE_LENGTH = 2;
+
+        public static final byte NEWMISSEDCALLS_LENGTH = 1;
+    }
+
+    /*
+    public class TRIPLET_STRUCTURE{
+        TRIPLET_TAGID id;
+        TRIPLET_LENGTH length;
+        byte[] value;
+    }
+    */
+    public ApplicationParameter() {
+        b_array = new byte[max_length];
+        length = 0;
+    }
+
+    public void addAPPHeader(byte tag, byte len, byte[] value) {
+        if ((length + len + 2) > max_length) {
+            byte[] array_tmp = new byte[length + 4 * len];
+            System.arraycopy(b_array, 0, array_tmp, 0, length);
+            b_array = array_tmp;
+            max_length = length + 4 * len;
+        }
+        b_array[length++] = tag;
+        b_array[length++] = len;
+        System.arraycopy(value, 0, b_array, length, len);
+        length += len;
+    }
+
+    public byte[] getAPPparam() {
+        byte[] para = new byte[length];
+        System.arraycopy(b_array, 0, para, 0, length);
+        return para;
+    }
+}
diff --git a/obex/javax/obex/Authenticator.java b/obex/javax/obex/Authenticator.java
new file mode 100644
index 0000000..90da7ba
--- /dev/null
+++ b/obex/javax/obex/Authenticator.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * This interface provides a way to respond to authentication challenge and
+ * authentication response headers.  When a client or server receives an
+ * authentication challenge or authentication response header, the
+ * <code>onAuthenticationChallenge()</code> or
+ * <code>onAuthenticationResponse()</code> will be called, respectively, by
+ * the implementation.
+ * <P>
+ * For more information on how the authentication procedure works in OBEX,
+ * please review the IrOBEX specification at
+ * <A HREF="http://www.irda.org">http://www.irda.org</A>.
+ * <P>
+ * <STRONG>Authentication Challenges</STRONG>
+ * <P>
+ * When a client or server receives an authentication challenge header, the
+ * <code>onAuthenticationChallenge()</code> method will be invoked by the
+ * OBEX API implementation.  The application will then return the user name
+ * (if needed) and password via a <code>PasswordAuthentication</code> object.
+ * The password in this object is not sent in the authentication response.
+ * Instead, the 16-byte challenge received in the authentication challenge is
+ * combined with the password returned from the
+ * <code>onAuthenticationChallenge()</code> method and passed through the MD5
+ * hash algorithm.  The resulting value is sent in the authentication response
+ * along with the user name if it was provided.
+ * <P>
+ * <STRONG>Authentication Responses</STRONG>
+ * <P>
+ * When a client or server receives an authentication response header, the
+ * <code>onAuthenticationResponse()</code> method is invoked by the API
+ * implementation with the user name received in the authentication response
+ * header.  (The user name will be <code>null</code> if no user name was
+ * provided in the authentication response header.)  The application must
+ * determine the correct password.  This value should be returned from the
+ * <code>onAuthenticationResponse()</code> method.  If the authentication
+ * request should fail without the implementation checking the password,
+ * <code>null</code> should
+ * be returned by the application.  (This is needed for reasons like not
+ * recognizing the user name, etc.)  If the returned value is not
+ * <code>null</code>, the OBEX API implementation will combine the password
+ * returned from the <code>onAuthenticationResponse()</code> method and
+ * challenge sent via the authentication challenge, apply the MD5 hash
+ * algorithm, and compare the result to the response hash received in the
+ * authentication response header.  If the values are not equal, an
+ * <code>IOException</code> will be thrown if the client requested authentication.
+ * If the server requested authentication, the
+ * <code>onAuthenticationFailure()</code> method will be called on the
+ * <code>ServerRequestHandler</code> that failed authentication.  The
+ * connection is <B>not</B> closed if authentication failed.
+ *
+ * @hide
+ */
+public interface Authenticator {
+
+    /**
+     * Called when a client or a server receives an authentication challenge
+     * header. It should respond to the challenge with a
+     * <code>PasswordAuthentication</code> that contains the correct user name
+     * and password for the challenge.
+     *
+     * @param description the description of which user name and password
+     * should be used; if no description is provided in the authentication
+     * challenge or the description is encoded in an encoding scheme that is
+     * not supported, an empty string will be provided
+     *
+     * @param isUserIdRequired <code>true</code> if the user ID is required;
+     * <code>false</code> if the user ID is not required
+     *
+     * @param isFullAccess <code>true</code> if full access to the server
+     * will be granted; <code>false</code> if read only access will be
+     * granted
+     *
+     * @return a <code>PasswordAuthentication</code> object containing the
+     * user name and password used for authentication
+     */
+    public PasswordAuthentication onAuthenticationChallenge(String description,
+            boolean isUserIdRequired, boolean isFullAccess);
+
+    /**
+     * Called when a client or server receives an authentication response
+     * header.  This method will provide the user name and expect the correct
+     * password to be returned.
+     *
+     * @param userName the user name provided in the authentication response;
+     * may be <code>null</code>
+     *
+     * @return the correct password for the user name provided; if
+     * <code>null</code> is returned then the authentication request failed
+     */
+    public byte[] onAuthenticationResponse(byte[] userName);
+}
diff --git a/obex/javax/obex/BaseStream.java b/obex/javax/obex/BaseStream.java
new file mode 100644
index 0000000..67581bf
--- /dev/null
+++ b/obex/javax/obex/BaseStream.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+
+/**
+ * This interface defines the methods needed by a parent that uses the
+ * PrivateInputStream and PrivateOutputStream objects defined in this package.
+ *
+ * @hide
+ */
+public interface BaseStream {
+
+    /**
+     * Verifies that this object is still open.
+     *
+     * @throws IOException if the object is closed
+     */
+    public void ensureOpen() throws IOException;
+
+    /**
+     * Verifies that additional information may be sent.  In other words, the
+     * operation is not done.
+     *
+     * @throws IOException if the operation is completed
+     */
+    public void ensureNotDone() throws IOException;
+
+    /**
+     * Continues the operation since there is no data to read.
+     *
+     * @param sendEmpty <code>true</code> if the operation should send an
+     * empty packet or not send anything if there is no data to send
+     * @param inStream  <code>true</code> if the stream is input stream or
+     * is output stream
+     * @return <code>true</code> if the operation was completed;
+     * <code>false</code> if no operation took place
+     *
+     * @throws IOException if an IO error occurs
+     */
+    public boolean continueOperation(boolean sendEmpty, boolean inStream) throws IOException;
+
+    /**
+     * Called when the output or input stream is closed.
+     *
+     * @param inStream <code>true</code> if the input stream is closed;
+     * <code>false</code> if the output stream is closed
+     *
+     * @throws IOException if an IO error occurs
+     */
+    public void streamClosed(boolean inStream) throws IOException;
+}
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java
new file mode 100644
index 0000000..5bc302a
--- /dev/null
+++ b/obex/javax/obex/ClientOperation.java
@@ -0,0 +1,840 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * This class implements the <code>Operation</code> interface.  It will read
+ * and write data via puts and gets.
+ *
+ * @hide
+ */
+public final class ClientOperation implements Operation, BaseStream {
+
+    /**
+     * Defines the basic packet length used by OBEX.  Event OBEX packet has the
+     * same basic format:<BR>
+     * Byte 0: Request or Response Code
+     * Byte 1&2: Length of the packet.
+     */
+    private static final int BASE_PACKET_LENGTH = 3;
+
+    private ClientSession parent;
+
+    private InputStream socketInput;
+
+    private PrivateInputStream privateInput;
+
+    private PrivateOutputStream privateOutput;
+
+    private boolean isClosed;
+
+    private String exceptionMessage;
+
+    private int maxPacketSize;
+
+    private boolean isDone;
+
+    private boolean isGet;
+
+    private HeaderSet requestHeaders;
+
+    private HeaderSet replyHeaders;
+
+    private boolean isEndOfBodySent;
+
+    private boolean inputStreamOpened;
+
+    private boolean outputStreamOpened;
+
+    private boolean isValidateConnected;
+
+    /** 
+     * Creates new OperationImpl to read and write data to a server
+     *
+     * @param in the input stream to read from
+     *
+     * @param maxSize the maximum packet size
+     *
+     * @param p the parent to this object
+     *
+     * @param headers the headers to set in the initial request
+     *
+     * @param type <code>true</code> if this is a get request;
+     * <code>false</code. if this is a put request
+     *
+     * @throws IOExcpetion if the an IO error occured
+     */
+    public ClientOperation(InputStream in, int maxSize, ClientSession p, HeaderSet header,
+            boolean type) throws IOException {
+
+        parent = p;
+        isEndOfBodySent = false;
+        socketInput = in;
+        isClosed = false;
+        isDone = false;
+        maxPacketSize = maxSize;
+        isGet = type;
+
+        inputStreamOpened = false;
+        outputStreamOpened = false;
+        isValidateConnected = false;
+
+        privateInput = null;
+        privateOutput = null;
+
+        replyHeaders = new HeaderSet();
+
+        requestHeaders = new HeaderSet();
+
+        int[] headerList = header.getHeaderList();
+
+        if (headerList != null) {
+
+            for (int i = 0; i < headerList.length; i++) {
+                requestHeaders.setHeader(headerList[i], header.getHeader(headerList[i]));
+            }
+        }
+
+        if ((header).authChall != null) {
+            requestHeaders.authChall = new byte[(header).authChall.length];
+            System.arraycopy((header).authChall, 0, requestHeaders.authChall, 0,
+                    (header).authChall.length);
+        }
+
+        if ((header).authResp != null) {
+            requestHeaders.authResp = new byte[(header).authResp.length];
+            System.arraycopy((header).authResp, 0, requestHeaders.authResp, 0,
+                    (header).authResp.length);
+
+        }
+        //        requestHeaders = (HeaderSet)header;
+    }
+
+    /**
+     * Sends an ABORT message to the server.  By calling this method, the
+     * corresponding input and output streams will be closed along with this
+     * object.
+     *
+     * @throws IOException if the transaction has already ended or if an
+     * OBEX server called this method
+     */
+    public synchronized void abort() throws IOException {
+        ensureOpen();
+        // need check again .
+        //	if(isDone) {
+        //	     throw new IOException("Operation has already ended");
+        //	}
+
+        //no compatible with sun-ri
+        if ((isDone) && (replyHeaders.responseCode != ObexHelper.OBEX_HTTP_CONTINUE)) {
+            throw new IOException("Operation has already ended");
+        }
+
+        exceptionMessage = "Operation aborted";
+        if ((!isDone) && (replyHeaders.responseCode == ObexHelper.OBEX_HTTP_CONTINUE)) {
+            isDone = true;
+            /*
+             * Since we are not sending any headers or returning any headers then
+             * we just need to write and read the same bytes
+             */
+            parent.sendRequest(0xFF, null, replyHeaders, null);
+
+            if (replyHeaders.responseCode != ResponseCodes.OBEX_HTTP_OK) {
+                throw new IOException("Invalid response code from server");
+            }
+
+            exceptionMessage = null;
+        }
+
+        close();
+    }
+
+    /**
+     * Retrieves the response code retrieved from the server.  Response codes
+     * are defined in the <code>ResponseCodes</code> interface.
+     *
+     * @return the response code retrieved from the server
+     *
+     * @throws IOException if an error occurred in the transport layer during
+     * the transaction; if this method is called on a <code>HeaderSet</code>
+     * object created by calling <code>createHeaderSet</code> in a
+     * <code>ClientSession</code> object
+     */
+    public synchronized int getResponseCode() throws IOException {
+        //avoid dup validateConnection
+        if ((replyHeaders.responseCode == -1)
+                || (replyHeaders.responseCode == ObexHelper.OBEX_HTTP_CONTINUE)) {
+            validateConnection();
+        }
+
+        return replyHeaders.responseCode;
+    }
+
+    /**
+     * This method will always return <code>null</code>
+     *
+     * @return <code>null</code>
+     */
+    public String getEncoding() {
+        return null;
+    }
+
+    /**
+     * Returns the type of content that the resource connected to is providing.
+     * E.g. if the connection is via HTTP, then the value of the content-type
+     * header field is returned.
+     *
+     * @return the content type of the resource that the URL references, or
+     * <code>null</code> if not known
+     */
+    public String getType() {
+        try {
+            return (String)replyHeaders.getHeader(HeaderSet.TYPE);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the length of the content which is being provided. E.g. if the
+     * connection is via HTTP, then the value of the content-length header
+     * field is returned.
+     *
+     * @return the content length of the resource that this connection's URL
+     * references, or -1 if the content length is not known
+     */
+    public long getLength() {
+        try {
+            Long temp = (Long)replyHeaders.getHeader(HeaderSet.LENGTH);
+
+            if (temp == null) {
+                return -1;
+            } else {
+                return temp.longValue();
+            }
+        } catch (IOException e) {
+            return -1;
+        }
+    }
+
+    /**
+     * Open and return an input stream for a connection.
+     *
+     * @return an input stream
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public InputStream openInputStream() throws IOException {
+        // TODO: this mode is not set yet.
+        // if ((parent.mode & Connector.READ) == 0)
+        // throw new IOException("write-only connection");
+
+        ensureOpen();
+
+        if (inputStreamOpened)
+            throw new IOException("no more input streams available");
+        if (isGet) {
+            // send the GET request here
+            validateConnection();
+            isValidateConnected = true;
+        } else {
+            if (privateInput == null) {
+                privateInput = new PrivateInputStream(this);
+            }
+        }
+
+        inputStreamOpened = true;
+
+        return privateInput;
+    }
+
+    /**8
+     * Open and return a data input stream for a connection.
+     *
+     * @return an input stream
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public DataInputStream openDataInputStream() throws IOException {
+        return new DataInputStream(openInputStream());
+    }
+
+    /**
+     * Open and return an output stream for a connection.
+     *
+     * @return an output stream
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public OutputStream openOutputStream() throws IOException {
+        // TODO: this mode is not set yet.
+        //    	if ((parent.mode & Connector.WRITE) == 0)
+        //    		throw new IOException("read-only connection");
+        ensureOpen();
+        ensureNotDone();
+
+        if (outputStreamOpened)
+            throw new IOException("no more output streams available");
+
+        if (privateOutput == null) {
+            // there are 3 bytes operation headers and 3 bytes body headers //
+            privateOutput = new PrivateOutputStream(this, maxPacketSize - 6);
+        }
+
+        outputStreamOpened = true;
+
+        return privateOutput;
+    }
+
+    public int getMaxPacketSize() {
+        return maxPacketSize - 6;
+    }
+
+    /**
+     * Open and return a data output stream for a connection.
+     *
+     * @return an output stream
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public DataOutputStream openDataOutputStream() throws IOException {
+        return new DataOutputStream(openOutputStream());
+    }
+
+    /**
+     * Closes the connection and ends the transaction
+     *
+     * @throws IOException if the operation has already ended or is closed
+     */
+    public void close() throws IOException {
+        isClosed = true;
+        inputStreamOpened = false;
+        outputStreamOpened = false;
+        parent.setRequestInactive();
+    }
+
+    /**
+     * Returns the headers that have been received during the operation.
+     * Modifying the object returned has no effect on the headers that are
+     * sent or retrieved.
+     *
+     * @return the headers received during this <code>Operation</code>
+     *
+     * @throws IOException if this <code>Operation</code> has been closed
+     */
+    public HeaderSet getReceivedHeaders() throws IOException {
+        ensureOpen();
+
+        return replyHeaders;
+    }
+
+    /**
+     * Specifies the headers that should be sent in the next OBEX message that
+     * is sent.
+     *
+     * @param headers the headers to send in the next message
+     *
+     * @throws IOException  if this <code>Operation</code> has been closed
+     * or the transaction has ended and no further messages will be exchanged
+     *
+     * @throws IllegalArgumentException if <code>headers</code> was not created
+     * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
+     *
+     * @throws NullPointerException if <code>headers</code> is <code>null</code>
+     */
+    public void sendHeaders(HeaderSet headers) throws IOException {
+        ensureOpen();
+        if (isDone) {
+            throw new IOException("Operation has already exchanged all data");
+        }
+
+        if (headers == null) {
+            throw new NullPointerException("Headers may not be null");
+        }
+
+        int[] headerList = headers.getHeaderList();
+        if (headerList != null) {
+            for (int i = 0; i < headerList.length; i++) {
+                requestHeaders.setHeader(headerList[i], headers.getHeader(headerList[i]));
+            }
+        }
+    }
+
+    /**
+     * Reads a response from the server.  It will populate the appropriate body
+     * and headers.
+     *
+     * @return <code>true</code> if the transaction should end;
+     * <code>false</code> if the transaction should not end
+     *
+     * @throws IOException if an IO error occurred
+     */
+    private boolean readResponse() throws IOException {
+        replyHeaders.responseCode = socketInput.read();
+        int packetLength = socketInput.read();
+        packetLength = (packetLength << 8) + socketInput.read();
+
+        if (packetLength > ObexHelper.MAX_PACKET_SIZE_INT) {
+            if (exceptionMessage != null) {
+                abort();
+            }
+            throw new IOException("Received a packet that was too big");
+        }
+
+        if (packetLength > BASE_PACKET_LENGTH) {
+            int dataLength = packetLength - BASE_PACKET_LENGTH;
+            byte[] data = new byte[dataLength];
+            int readLength = socketInput.read(data);
+            if (readLength != dataLength) {
+                throw new IOException("Received a packet without data as decalred length");
+            }
+            byte[] body = ObexHelper.updateHeaderSet(replyHeaders, data);
+
+            if (body != null) {
+                privateInput.writeBytes(body, 1);
+
+                /*
+                 * Determine if a body (0x48) header or an end of body (0x49)
+                 * was received.  If we received an end of body and
+                 * a response code of OBEX_HTTP_OK, then the operation should
+                 * end.
+                 */
+                if ((body[0] == 0x49) && (replyHeaders.responseCode == ResponseCodes.OBEX_HTTP_OK)) {
+                    return false;
+                }
+            }
+        }
+
+        if (replyHeaders.responseCode == ObexHelper.OBEX_HTTP_CONTINUE) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Verifies that additional information may be sent.  In other words, the
+     * operation is not done.
+     *
+     * @throws IOException if the operation is completed
+     */
+    public void ensureNotDone() throws IOException {
+        if (isDone) {
+            throw new IOException("Operation has completed");
+        }
+    }
+
+    /**
+     * Verifies that the connection is open and no exceptions should be thrown.
+     *
+     * @throws IOException if an exception needs to be thrown
+     */
+    public void ensureOpen() throws IOException {
+        parent.ensureOpen();
+
+        if (exceptionMessage != null) {
+            throw new IOException(exceptionMessage);
+        }
+        if (isClosed) {
+            throw new IOException("Operation has already ended");
+        }
+    }
+
+    /**
+     * Verifies that the connection is open and the proper data has been read.
+     *
+     * @throws IOException if an IO error occurs
+     */
+    private void validateConnection() throws IOException {
+        ensureOpen();
+
+        // to sure only one privateInput object exist.
+        if (privateInput == null) {
+            startProcessing();
+        }
+    }
+
+    /**
+     * Sends a request to the client of the specified type
+     *
+     * @param response the response code to send back to the client
+     *
+     * @return <code>true</code> if there is more data to send;
+     * <code>false</code> if there is no more data to send
+     *
+     * @throws IOException if an IO error occurs
+     */
+    protected boolean sendRequest(int type) throws IOException {
+        boolean returnValue = false;
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int bodyLength = -1;
+        byte[] headerArray = ObexHelper.createHeader(requestHeaders, true);
+        if (privateOutput != null) {
+            bodyLength = privateOutput.size();
+        }
+
+        /*
+         * Determine if there is space to add a body request.  At present
+         * this method checks to see if there is room for at least a 17
+         * byte body header.  This number needs to be at least 6 so that
+         * there is room for the header ID and length and the reply ID and
+         * length, but it is a waste of resources if we can't send much of
+         * the body.
+         */
+        if ((BASE_PACKET_LENGTH + headerArray.length) > maxPacketSize) {
+            int end = 0;
+            int start = 0;
+            // split & send the headerArray in multiple packets.
+
+            while (end != headerArray.length) {
+                //split the headerArray
+                end = ObexHelper.findHeaderEnd(headerArray, start, maxPacketSize
+                        - BASE_PACKET_LENGTH);
+                // can not split 
+                if (end == -1) {
+                    isDone = true;
+                    abort();
+                    // isDone = true;
+                    exceptionMessage = "Header larger then can be sent in a packet";
+                    isClosed = true;
+
+                    if (privateInput != null) {
+                        privateInput.close();
+                    }
+
+                    if (privateOutput != null) {
+                        privateOutput.close();
+                    }
+                    throw new IOException("OBEX Packet exceeds max packet size");
+                }
+
+                byte[] sendHeader = new byte[end - start];
+                System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
+                if (!parent.sendRequest(type, sendHeader, replyHeaders, privateInput)) {
+                    return false;
+                }
+
+                if (replyHeaders.responseCode != ObexHelper.OBEX_HTTP_CONTINUE) {
+                    return false;
+                }
+
+                start = end;
+            }
+
+            if (bodyLength > 0) {
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            out.write(headerArray);
+        }
+
+        if (bodyLength > 0) {
+            /*
+             * Determine if I can send the whole body or just part of
+             * the body.  Remember that there is the 3 bytes for the
+             * response message and 3 bytes for the header ID and length
+             */
+            if (bodyLength > (maxPacketSize - headerArray.length - 6)) {
+                returnValue = true;
+
+                bodyLength = maxPacketSize - headerArray.length - 6;
+            }
+
+            byte[] body = privateOutput.readBytes(bodyLength);
+
+            /*
+             * Since this is a put request if the final bit is set or
+             * the output stream is closed we need to send the 0x49
+             * (End of Body) otherwise, we need to send 0x48 (Body)
+             */
+            if ((privateOutput.isClosed()) && (!returnValue) && (!isEndOfBodySent)
+                    && ((type & 0x80) != 0)) {
+                out.write(0x49);
+                isEndOfBodySent = true;
+            } else {
+                out.write(0x48);
+            }
+
+            bodyLength += 3;
+            out.write((byte)(bodyLength >> 8));
+            out.write((byte)bodyLength);
+
+            if (body != null) {
+                out.write(body);
+            }
+        }
+
+        if (outputStreamOpened && bodyLength <= 0 && !isEndOfBodySent) {
+            // only 0x82 or 0x83 can send 0x49
+            if ((type & 0x80) == 0) {
+                out.write(0x48);
+            } else {
+                out.write(0x49);
+                isEndOfBodySent = true;
+
+            }
+
+            bodyLength = 3;
+            out.write((byte)(bodyLength >> 8));
+            out.write((byte)bodyLength);
+        }
+
+        if (out.size() == 0) {
+            if (!parent.sendRequest(type, null, replyHeaders, privateInput)) {
+                return false;
+            }
+            return returnValue;
+        }
+        if ((out.size() > 0)
+                && (!parent.sendRequest(type, out.toByteArray(), replyHeaders, privateInput))) {
+            return false;
+        }
+
+        // send all of the output data in 0x48, 
+        // send 0x49 with empty body
+        if ((privateOutput != null) && (privateOutput.size() > 0))
+            returnValue = true;
+
+        return returnValue;
+    }
+
+    /**
+     * This method starts the processing thread results.  It will send the
+     * initial request.  If the response takes more then one packet, a thread
+     * will be started to handle additional requests
+     *
+     * @throws IOException if an IO error occurs
+     */
+    private synchronized void startProcessing() throws IOException {
+
+        if (privateInput == null) {
+            privateInput = new PrivateInputStream(this);
+        }
+        boolean more = true;
+
+        if (isGet) {
+            if (!isDone) {
+                replyHeaders.responseCode = ObexHelper.OBEX_HTTP_CONTINUE;
+                while ((more) && (replyHeaders.responseCode == ObexHelper.OBEX_HTTP_CONTINUE)) {
+                    more = sendRequest(0x03);
+                }
+
+                if (replyHeaders.responseCode == ObexHelper.OBEX_HTTP_CONTINUE) {
+                    parent.sendRequest(0x83, null, replyHeaders, privateInput);
+                }
+                if (replyHeaders.responseCode != ObexHelper.OBEX_HTTP_CONTINUE) {
+                    isDone = true;
+                }
+            }
+        } else {
+
+            if (!isDone) {
+                replyHeaders.responseCode = ObexHelper.OBEX_HTTP_CONTINUE;
+                while ((more) && (replyHeaders.responseCode == ObexHelper.OBEX_HTTP_CONTINUE)) {
+                    more = sendRequest(0x02);
+
+                }
+            }
+
+            if (replyHeaders.responseCode == ObexHelper.OBEX_HTTP_CONTINUE) {
+                parent.sendRequest(0x82, null, replyHeaders, privateInput);
+            }
+
+            if (replyHeaders.responseCode != ObexHelper.OBEX_HTTP_CONTINUE) {
+                isDone = true;
+            }
+        }
+    }
+
+    /**
+     * Continues the operation since there is no data to read.
+     *
+     * @param sendEmpty <code>true</code> if the operation should send an
+     * empty packet or not send anything if there is no data to send
+     * @param inStream  <code>true</code> if the stream is input stream or
+     * is output stream
+     * @throws IOException if an IO error occurs
+     */
+    public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
+            throws IOException {
+
+        if (isGet) {
+            if ((inStream) && (!isDone)) {
+                // to deal with inputstream in get operation
+                parent.sendRequest(0x83, null, replyHeaders, privateInput);
+                /*
+                  * Determine if that was not the last packet in the operation
+                  */
+                if (replyHeaders.responseCode != ObexHelper.OBEX_HTTP_CONTINUE) {
+                    isDone = true;
+                }
+
+                return true;
+
+            } else if ((!inStream) && (!isDone)) {
+                // to deal with outputstream in get operation
+
+                if (privateInput == null) {
+                    privateInput = new PrivateInputStream(this);
+                }
+                sendRequest(0x03);
+                return true;
+
+            } else if (isDone) {
+                return false;
+            }
+
+        } else {
+            if ((!inStream) && (!isDone)) {
+                // to deal with outputstream in put operation
+                if (replyHeaders.responseCode == -1) {
+                    replyHeaders.responseCode = ObexHelper.OBEX_HTTP_CONTINUE;
+                }
+                sendRequest(0x02);
+                return true;
+            } else if ((inStream) && (!isDone)) {
+                // How to deal with inputstream  in put operation ?
+                return false;
+
+            } else if (isDone) {
+                return false;
+            }
+
+        }
+        return false;
+    }
+
+    /**
+     * Called when the output or input stream is closed.
+     *
+     * @param inStream <code>true</code> if the input stream is closed;
+     * <code>false</code> if the output stream is closed
+     *
+     * @throws IOException if an IO error occurs
+     */
+    public void streamClosed(boolean inStream) throws IOException {
+        if (!isGet) {
+            if ((!inStream) && (!isDone)) {
+                // to deal with outputstream in put operation
+
+                boolean more = true;
+
+                if ((privateOutput != null) && (privateOutput.size() <= 0)) {
+                    byte[] headerArray = ObexHelper.createHeader(requestHeaders, false);
+                    if (headerArray.length <= 0)
+                        more = false;
+                }
+                // If have not sent any data so send  all now
+                if (replyHeaders.responseCode == -1) {
+                    replyHeaders.responseCode = ObexHelper.OBEX_HTTP_CONTINUE;
+                }
+
+                while ((more) && (replyHeaders.responseCode == ObexHelper.OBEX_HTTP_CONTINUE)) {
+                    more = sendRequest(0x02);
+                }
+
+                /*
+                 * According to the IrOBEX specification, after the final put, you
+                 * only have a single reply to send.  so we don't need the while
+                 * loop.
+                 */
+                while (replyHeaders.responseCode == ObexHelper.OBEX_HTTP_CONTINUE) {
+
+                    sendRequest(0x82);
+                }
+                isDone = true;
+            } else if ((inStream) && (isDone)) {
+                // how to deal with input stream in put stream ?
+                isDone = true;
+            }
+        } else {
+            isValidateConnected = false;
+            if ((inStream) && (!isDone)) {
+
+                // to deal with inputstream in get operation
+                // Have not sent any data so send it all now
+
+                if (replyHeaders.responseCode == -1) {
+                    replyHeaders.responseCode = ObexHelper.OBEX_HTTP_CONTINUE;
+                }
+
+                while (replyHeaders.responseCode == ObexHelper.OBEX_HTTP_CONTINUE) {
+                    if (!sendRequest(0x83)) {
+                        break;
+                    }
+                }
+                while (replyHeaders.responseCode == ObexHelper.OBEX_HTTP_CONTINUE) {
+                    parent.sendRequest(0x83, null, replyHeaders, privateInput);
+                }
+                isDone = true;
+            } else if ((!inStream) && (!isDone)) {
+                // to deal with outputstream in get operation
+                // part of the data may have been sent in continueOperation.
+
+                boolean more = true;
+
+                if ((privateOutput != null) && (privateOutput.size() <= 0)) {
+                    byte[] headerArray = ObexHelper.createHeader(requestHeaders, false);
+                    if (headerArray.length <= 0)
+                        more = false;
+                }
+
+                if (privateInput == null) {
+                    privateInput = new PrivateInputStream(this);
+                }
+                if ((privateOutput != null) && (privateOutput.size() <= 0))
+                    more = false;
+
+                replyHeaders.responseCode = ObexHelper.OBEX_HTTP_CONTINUE;
+                while ((more) && (replyHeaders.responseCode == ObexHelper.OBEX_HTTP_CONTINUE)) {
+                    more = sendRequest(0x03);
+                }
+                sendRequest(0x83);
+                //                parent.sendRequest(0x83, null, replyHeaders, privateInput);
+                if (replyHeaders.responseCode != ObexHelper.OBEX_HTTP_CONTINUE) {
+                    isDone = true;
+                }
+
+            }
+        }
+    }
+}
diff --git a/obex/javax/obex/ClientSession.java b/obex/javax/obex/ClientSession.java
new file mode 100644
index 0000000..f65ded9
--- /dev/null
+++ b/obex/javax/obex/ClientSession.java
@@ -0,0 +1,723 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This class implements the <code>Operation</code> interface.  It will read
+ * and write data via puts and gets.
+ *
+ * @hide
+ */
+public final class ClientSession implements ObexSession {
+    private Authenticator mAuthenticator;
+
+    private boolean mOpen;
+
+    // Determines if an OBEX layer connection has been established
+    private boolean mObexConnected;
+
+    private byte[] mConnectionId = null;
+
+    private byte[] mChallengeDigest = null;
+
+    /*
+     * The max Packet size must be at least 256 according to the OBEX
+     * specification.
+     */
+    private int maxPacketSize = 256;
+
+    private boolean mRequestActive;
+
+    private final InputStream mInput;
+
+    private final OutputStream mOutput;
+
+    public ClientSession(ObexTransport trans) throws IOException {
+        mInput = trans.openInputStream();
+        mOutput = trans.openOutputStream();
+        mOpen = true;
+        mRequestActive = false;
+    }
+
+    public HeaderSet connect(HeaderSet header) throws IOException {
+        ensureOpen();
+        if (mObexConnected) {
+            throw new IOException("Already connected to server");
+        }
+        setRequestActive();
+
+        int totalLength = 4;
+        byte[] head = null;
+
+        // Determine the header byte array
+        if (header != null) {
+            if (header.nonce != null) {
+                mChallengeDigest = new byte[16];
+                System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
+            }
+            head = ObexHelper.createHeader(header, false);
+            totalLength += head.length;
+        }
+        /*
+        * Write the OBEX CONNECT packet to the server.
+        * Byte 0: 0x80
+        * Byte 1&2: Connect Packet Length
+        * Byte 3: OBEX Version Number (Presently, 0x10)
+        * Byte 4: Flags (For TCP 0x00)
+        * Byte 5&6: Max OBEX Packet Length (Defined in MAX_PACKET_SIZE)
+        * Byte 7 to n: headers
+        */
+        byte[] requestPacket = new byte[totalLength];
+        // We just need to start at  byte 3 since the sendRequest() method will
+        // handle the length and 0x80.
+        requestPacket[0] = (byte)0x10;
+        requestPacket[1] = (byte)0x00;
+        requestPacket[2] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8);
+        requestPacket[3] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF);
+        if (head != null) {
+            System.arraycopy(head, 0, requestPacket, 4, head.length);
+        }
+
+        // check with local max packet size
+        if ((requestPacket.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
+            throw new IOException("Packet size exceeds max packet size");
+        }
+
+        HeaderSet returnHeaderSet = new HeaderSet();
+        sendRequest(0x80, requestPacket, returnHeaderSet, null);
+
+        /*
+        * Read the response from the OBEX server.
+        * Byte 0: Response Code (If successful then OBEX_HTTP_OK)
+        * Byte 1&2: Packet Length
+        * Byte 3: OBEX Version Number
+        * Byte 4: Flags3
+        * Byte 5&6: Max OBEX packet Length
+        * Byte 7 to n: Optional HeaderSet
+        */
+        if (returnHeaderSet.responseCode == ResponseCodes.OBEX_HTTP_OK) {
+            mObexConnected = true;
+        }
+        setRequestInactive();
+
+        return returnHeaderSet;
+    }
+
+    public Operation get(HeaderSet header) throws IOException {
+
+        if (!mObexConnected) {
+            throw new IOException("Not connected to the server");
+        }
+        setRequestActive();
+
+        ensureOpen();
+
+        if (header == null) {
+            header = new HeaderSet();
+        } else {
+            if (header.nonce != null) {
+                mChallengeDigest = new byte[16];
+                System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
+            }
+        }
+        // Add the connection ID if one exists
+        if (mConnectionId != null) {
+            header.connectionID = new byte[4];
+            System.arraycopy(mConnectionId, 0, header.connectionID, 0, 4);
+        }
+
+        return new ClientOperation(mInput, maxPacketSize, this, header, true);
+    }
+
+    /**
+     *  0xCB Connection Id an identifier used for OBEX connection multiplexing
+     */
+    public void setConnectionID(long id) {
+        if ((id < 0) || (id > 0xFFFFFFFFL)) {
+            throw new IllegalArgumentException("Connection ID is not in a valid range");
+        }
+        mConnectionId = ObexHelper.convertToByteArray(id);
+    }
+
+    public HeaderSet delete(HeaderSet header) throws IOException {
+
+        Operation op = put(header);
+        op.getResponseCode();
+        HeaderSet returnValue = op.getReceivedHeaders();
+        op.close();
+
+        return returnValue;
+    }
+
+    public HeaderSet disconnect(HeaderSet header) throws IOException {
+        if (!mObexConnected) {
+            throw new IOException("Not connected to the server");
+        }
+        setRequestActive();
+
+        ensureOpen();
+        // Determine the header byte array
+        byte[] head = null;
+        if (header != null) {
+            if (header.nonce != null) {
+                mChallengeDigest = new byte[16];
+                System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
+            }
+            // Add the connection ID if one exists
+            if (mConnectionId != null) {
+                header.connectionID = new byte[4];
+                System.arraycopy(mConnectionId, 0, header.connectionID, 0, 4);
+            }
+            head = ObexHelper.createHeader(header, false);
+
+            if ((head.length + 3) > maxPacketSize) {
+                throw new IOException("Packet size exceeds max packet size");
+            }
+        } else {
+            // Add the connection ID if one exists
+            if (mConnectionId != null) {
+                head = new byte[5];
+                head[0] = (byte)0xCB;
+                System.arraycopy(mConnectionId, 0, head, 1, 4);
+            }
+        }
+
+        HeaderSet returnHeaderSet = new HeaderSet();
+        sendRequest(0x81, head, returnHeaderSet, null);
+
+        /*
+         * An OBEX DISCONNECT reply from the server:
+         * Byte 1: Response code
+         * Bytes 2 & 3: packet size
+         * Bytes 4 & up: headers
+         */
+
+        /* response code , and header are ignored
+         * */
+
+        synchronized (this) {
+            mObexConnected = false;
+            setRequestInactive();
+        }
+
+        return returnHeaderSet;
+    }
+
+    public long getConnectionID() {
+
+        if (mConnectionId == null) {
+            return -1;
+        }
+        return ObexHelper.convertToLong(mConnectionId);
+    }
+
+    public Operation put(HeaderSet header) throws IOException {
+        if (!mObexConnected) {
+            throw new IOException("Not connected to the server");
+        }
+        setRequestActive();
+
+        ensureOpen();
+
+        if (header == null) {
+            header = new HeaderSet();
+        } else {
+            // when auth is initated by client ,save the digest 
+            if (header.nonce != null) {
+                mChallengeDigest = new byte[16];
+                System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
+            }
+        }
+
+        // Add the connection ID if one exists
+        if (mConnectionId != null) {
+
+            header.connectionID = new byte[4];
+            System.arraycopy(mConnectionId, 0, header.connectionID, 0, 4);
+        }
+
+        return new ClientOperation(mInput, maxPacketSize, this, header, false);
+    }
+
+    public void setAuthenticator(Authenticator auth) {
+        if (auth == null) {
+            throw new NullPointerException("Authenticator may not be null");
+        }
+        mAuthenticator = auth;
+    }
+
+    public HeaderSet setPath(HeaderSet header, boolean backup, boolean create)
+            throws IOException {
+        if (!mObexConnected) {
+            throw new IOException("Not connected to the server");
+        }
+        setRequestActive();
+        ensureOpen();
+
+        int totalLength = 2;
+        byte[] head = null;
+
+        if (header == null) {
+            header = new HeaderSet();
+        } else {
+            if (header.nonce != null) {
+                mChallengeDigest = new byte[16];
+                System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
+            }
+        }
+
+        // when auth is initiated by client ,save the digest
+        if (header.nonce != null) {
+            mChallengeDigest = new byte[16];
+            System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
+        }
+
+        // Add the connection ID if one exists
+        if (mConnectionId != null) {
+            header.connectionID = new byte[4];
+            System.arraycopy(mConnectionId, 0, header.connectionID, 0, 4);
+        }
+
+        head = ObexHelper.createHeader(header, false);
+        totalLength += head.length;
+
+        if (totalLength > maxPacketSize) {
+            throw new IOException("Packet size exceeds max packet size");
+        }
+
+        int flags = 0;
+        /*
+         * The backup flag bit is bit 0 so if we add 1, this will set that bit
+         */
+        if (backup) {
+            flags++;
+        }
+        /*
+         * The create bit is bit 1 so if we or with 2 the bit will be set.
+         */
+        if (!create) {
+            flags |= 2;
+        }
+
+        /*
+         * An OBEX SETPATH packet to the server:
+         * Byte 1: 0x85
+         * Byte 2 & 3: packet size
+         * Byte 4: flags
+         * Byte 5: constants
+         * Byte 6 & up: headers
+         */
+        byte[] packet = new byte[totalLength];
+        packet[0] = (byte)flags;
+        packet[1] = (byte)0x00;
+        if (header != null) {
+            System.arraycopy(head, 0, packet, 2, head.length);
+        }
+
+        HeaderSet returnHeaderSet = new HeaderSet();
+        sendRequest(0x85, packet, returnHeaderSet, null);
+
+        /*
+         * An OBEX SETPATH reply from the server:
+         * Byte 1: Response code
+         * Bytes 2 & 3: packet size
+         * Bytes 4 & up: headers
+         */
+
+        setRequestInactive();
+
+        return returnHeaderSet;
+    }
+
+    /**
+     * Verifies that the connection is open.
+     *
+     * @throws IOException if the connection is closed
+     */
+    public synchronized void ensureOpen() throws IOException {
+        if (!mOpen) {
+            throw new IOException("Connection closed");
+        }
+    }
+
+    /**
+     * Set request inactive.
+     * Allows Put and get operation objects to tell this object when they are
+     * done.
+     */
+    /*package*/ synchronized void setRequestInactive() {
+        mRequestActive = false;
+    }
+
+    /**
+     * Set request to active.
+     * @throws IOException if already active
+     */
+    private synchronized void setRequestActive() throws IOException {
+        if (mRequestActive) {
+            throw new IOException("OBEX request is already being performed");
+        }
+        mRequestActive = true;
+    }
+
+    /**
+     * Sends a standard request to the client.  It will then wait for the reply
+     * and update the header set object provided.  If any authentication
+     * headers (i.e. authentication challenge or authentication response) are
+     * received, they will be processed.
+     *
+     * @param code the type of request to send to the client
+     *
+     * @param head the headers to send to the server
+     *
+     * @param challenge the nonce that was sent in the authentication
+     * challenge header located in <code>head</code>; <code>null</code>
+     * if no authentication header is included in <code>head</code>
+     *
+     * @param header the header object to update with the response
+     *
+     * @param input the input stream used by the Operation object; null if this
+     * is called on a CONNECT, SETPATH or DISCONNECT
+     *
+     * return <code>true</code> if the operation completed successfully;
+     * <code>false</code> if an authentication response failed to pass
+     *
+     * @throws IOException if an IO error occurs
+     */
+    public boolean sendRequest(int code, byte[] head, HeaderSet header,
+            PrivateInputStream privateInput) throws IOException {
+        //check header length with local max size
+        if (head != null) {
+            if ((head.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
+                throw new IOException("header too large ");
+            }
+        }
+        //byte[] nonce;
+        int bytesReceived;
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write((byte)code);
+
+        // Determine if there are any headers to send
+        if (head == null) {
+            out.write(0x00);
+            out.write(0x03);
+        } else {
+            out.write((byte)((head.length + 3) >> 8));
+            out.write((byte)(head.length + 3));
+            out.write(head);
+        }
+
+        // Write the request to the output stream and flush the stream
+        mOutput.write(out.toByteArray());
+        mOutput.flush();
+
+        header.responseCode = mInput.read();
+
+        int length = ((mInput.read() << 8) | (mInput.read()));
+
+        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+            throw new IOException("Packet received exceeds packet size limit");
+        }
+        if (length > 3) {
+            byte[] data = null;
+            if (code == 0x80) {
+                int version = mInput.read();
+                int flags = mInput.read();
+                maxPacketSize = (mInput.read() << 8) + mInput.read();
+
+                //check with local max size
+                if (maxPacketSize > ObexHelper.MAX_PACKET_SIZE_INT) {
+                    maxPacketSize = ObexHelper.MAX_PACKET_SIZE_INT;
+                }
+
+                if (length > 7) {
+                    data = new byte[length - 7];
+
+                    bytesReceived = mInput.read(data);
+                    while (bytesReceived != (length - 7)) {
+                        bytesReceived += mInput.read(data, bytesReceived, data.length
+                                - bytesReceived);
+                    }
+                } else {
+                    return true;
+                }
+            } else {
+                data = new byte[length - 3];
+                bytesReceived = mInput.read(data);
+
+                while (bytesReceived != (length - 3)) {
+                    bytesReceived += mInput.read(data, bytesReceived, data.length - bytesReceived);
+                }
+                if (code == 0xFF) {
+                    return true;
+                }
+            }
+
+            byte[] body = ObexHelper.updateHeaderSet(header, data);
+            if ((privateInput != null) && (body != null)) {
+                privateInput.writeBytes(body, 1);
+            }
+
+            if (header.connectionID != null) {
+                mConnectionId = new byte[4];
+                System.arraycopy(header.connectionID, 0, mConnectionId, 0, 4);
+            }
+
+            if (header.authResp != null) {
+                if (!handleAuthResp(header.authResp)) {
+                    setRequestInactive();
+                    throw new IOException("Authentication Failed");
+                }
+            }
+
+            if ((header.responseCode == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
+                    && (header.authChall != null)) {
+
+                if (handleAuthChall(header)) {
+                    out.write((byte)0x4E);
+                    out.write((byte)((header.authResp.length + 3) >> 8));
+                    out.write((byte)(header.authResp.length + 3));
+                    out.write(header.authResp);
+                    header.authChall = null;
+                    header.authResp = null;
+
+                    byte[] sendHeaders = new byte[out.size() - 3];
+                    System.arraycopy(out.toByteArray(), 3, sendHeaders, 0, sendHeaders.length);
+
+                    return sendRequest(code, sendHeaders, header, privateInput);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Called when the client received an authentication challenge header.  This
+     * will cause the authenticator to handle the authentication challenge.
+     *
+     * @param header the header with the authentication challenge
+     *
+     * @return <code>true</code> if the last request should be resent;
+     * <code>false</code> if the last request should not be resent
+     */
+    protected boolean handleAuthChall(HeaderSet header) {
+
+        if (mAuthenticator == null) {
+            return false;
+        }
+
+        /*
+         * An authentication challenge is made up of one required and two
+         * optional tag length value triplets.  The tag 0x00 is required to be
+         * in the authentication challenge and it represents the challenge
+         * digest that was received.  The tag 0x01 is the options tag.  This
+         * tag tracks if user ID is required and if full access will be
+         * granted.  The tag 0x02 is the realm, which provides a description of
+         * which user name and password to use.
+         */
+        byte[] challenge = ObexHelper.getTagValue((byte)0x00, header.authChall);
+        byte[] option = ObexHelper.getTagValue((byte)0x01, header.authChall);
+        byte[] description = ObexHelper.getTagValue((byte)0x02, header.authChall);
+
+        String realm = "";
+        if (description != null) {
+            byte[] realmString = new byte[description.length - 1];
+            System.arraycopy(description, 1, realmString, 0, realmString.length);
+
+            switch (description[0] & 0xFF) {
+
+                case 0x00:
+                    // ASCII encoding
+                    // Fall through
+                case 0x01:
+                    // ISO-8859-1 encoding
+                    try {
+                        realm = new String(realmString, "ISO8859_1");
+                    } catch (Exception e) {
+                        throw new RuntimeException("Unsupported Encoding Scheme");
+                    }
+                    break;
+
+                case 0xFF:
+                    // UNICODE Encoding
+                    realm = ObexHelper.convertToUnicode(realmString, false);
+                    break;
+
+                case 0x02:
+                    // ISO-8859-2 encoding
+                    // Fall through
+                case 0x03:
+                    // ISO-8859-3 encoding
+                    // Fall through
+                case 0x04:
+                    // ISO-8859-4 encoding
+                    // Fall through
+                case 0x05:
+                    // ISO-8859-5 encoding
+                    // Fall through
+                case 0x06:
+                    // ISO-8859-6 encoding
+                    // Fall through
+                case 0x07:
+                    // ISO-8859-7 encoding
+                    // Fall through
+                case 0x08:
+                    // ISO-8859-8 encoding
+                    // Fall through
+                case 0x09:
+                    // ISO-8859-9 encoding
+                    // Fall through
+                default:
+                    throw new RuntimeException("Unsupported Encoding Scheme");
+            }
+        }
+
+        boolean isUserIDRequired = false;
+        boolean isFullAccess = true;
+        if (option != null) {
+            if ((option[0] & 0x01) != 0) {
+                isUserIDRequired = true;
+            }
+
+            if ((option[0] & 0x02) != 0) {
+                isFullAccess = false;
+            }
+        }
+
+        PasswordAuthentication result = null;
+        header.authChall = null;
+
+        try {
+            result = mAuthenticator.onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess);
+        } catch (Exception e) {
+            return false;
+        }
+
+        /*
+         * If no password was provided then do not resend the request
+         */
+        if (result == null) {
+            return false;
+        }
+
+        byte[] password = result.getPassword();
+        if (password == null) {
+            return false;
+        }
+
+        byte[] userName = result.getUserName();
+
+        /*
+         * Create the authentication response header.  It includes 1 required
+         * and 2 option tag length value triples.  The required triple has a
+         * tag of 0x00 and is the response digest.  The first optional tag is
+         * 0x01 and represents the user ID.  If no user ID is provided, then
+         * no user ID will be sent.  The second optional tag is 0x02 and is the
+         * challenge that was received.  This will always be sent
+         */
+        if (userName != null) {
+            header.authResp = new byte[38 + userName.length];
+            header.authResp[36] = (byte)0x01;
+            header.authResp[37] = (byte)userName.length;
+            System.arraycopy(userName, 0, header.authResp, 38, userName.length);
+        } else {
+            header.authResp = new byte[36];
+        }
+
+        // Create the secret String
+        byte[] digest = new byte[challenge.length + password.length];
+        System.arraycopy(challenge, 0, digest, 0, challenge.length);
+        System.arraycopy(password, 0, digest, challenge.length, password.length);
+
+        // Add the Response Digest
+        header.authResp[0] = (byte)0x00;
+        header.authResp[1] = (byte)0x10;
+
+        byte[] responseDigest = ObexHelper.computeMd5Hash(digest);
+        System.arraycopy(responseDigest, 0, header.authResp, 2, 16);
+
+        // Add the challenge
+        header.authResp[18] = (byte)0x02;
+        header.authResp[19] = (byte)0x10;
+        System.arraycopy(challenge, 0, header.authResp, 20, 16);
+
+        return true;
+    }
+
+    /**
+     * Called when the client received an authentication response header.  This
+     * will cause the authenticator to handle the authentication response.
+     *
+     * @param authResp the authentication response
+     *
+     * @return <code>true</code> if the response passed; <code>false</code> if
+     * the response failed
+     */
+    protected boolean handleAuthResp(byte[] authResp) {
+        if (mAuthenticator == null) {
+            return false;
+        }
+
+        byte[] correctPassword = mAuthenticator.onAuthenticationResponse(ObexHelper.getTagValue(
+                (byte)0x01, authResp));
+        if (correctPassword == null) {
+            return false;
+        }
+
+        byte[] temp = new byte[correctPassword.length + 16];
+        System.arraycopy(mChallengeDigest, 0, temp, 0, 16);
+        System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length);
+
+        byte[] correctResponse = ObexHelper.computeMd5Hash(temp);
+        byte[] actualResponse = ObexHelper.getTagValue((byte)0x00, authResp);
+        for (int i = 0; i < 16; i++) {
+            if (correctResponse[i] != actualResponse[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public void close() throws IOException {
+        mOpen = false;
+        mInput.close();
+        mOutput.close();
+    }
+}
diff --git a/obex/javax/obex/HeaderSet.java b/obex/javax/obex/HeaderSet.java
new file mode 100644
index 0000000..5dddf8f
--- /dev/null
+++ b/obex/javax/obex/HeaderSet.java
@@ -0,0 +1,611 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Random;
+
+/**
+ * This class implements the javax.obex.HeaderSet interface for OBEX over
+ * RFCOMM.
+ *
+ * @hide
+ */
+public final class HeaderSet {
+
+    /**
+     * Represents the OBEX Count header.  This allows the connection statement
+     * to tell the server how many objects it plans to send or retrieve.
+     * <P>
+     * The value of <code>COUNT</code> is 0xC0 (192).
+     */
+    public static final int COUNT = 0xC0;
+
+    /**
+     * Represents the OBEX Name header.  This specifies the name of the object.
+     * <P>
+     * The value of <code>NAME</code> is 0x01 (1).
+     */
+    public static final int NAME = 0x01;
+
+    /**
+     * Represents the OBEX Type header.  This allows a request to specify the
+     * type of the object (e.g. text, html, binary, etc.).
+     * <P>
+     * The value of <code>TYPE</code> is 0x42 (66).
+     */
+    public static final int TYPE = 0x42;
+
+    /**
+     * Represents the OBEX Length header.  This is the length of the object in
+     * bytes.
+     * <P>
+     * The value of <code>LENGTH</code> is 0xC3 (195).
+     */
+    public static final int LENGTH = 0xC3;
+
+    /**
+     * Represents the OBEX Time header using the ISO 8601 standards.  This is
+     * the preferred time header.
+     * <P>
+     * The value of <code>TIME_ISO_8601</code> is 0x44 (68).
+     */
+    public static final int TIME_ISO_8601 = 0x44;
+
+    /**
+     * Represents the OBEX Time header using the 4 byte representation.  This
+     * is only included for backwards compatibility.  It represents the number
+     * of seconds since January 1, 1970.
+     * <P>
+     * The value of <code>TIME_4_BYTE</code> is 0xC4 (196).
+     */
+    public static final int TIME_4_BYTE = 0xC4;
+
+    /**
+     * Represents the OBEX Description header.  This is a text description of
+     * the object.
+     * <P>
+     * The value of <code>DESCRIPTION</code> is 0x05 (5).
+     */
+    public static final int DESCRIPTION = 0x05;
+
+    /**
+     * Represents the OBEX Target header.  This is the name of the service an
+     * operation is targeted to.
+     * <P>
+     * The value of <code>TARGET</code> is 0x46 (70).
+     */
+    public static final int TARGET = 0x46;
+
+    /**
+     * Represents the OBEX HTTP header.  This allows an HTTP 1.X header to be
+     * included in a request or reply.
+     * <P>
+     * The value of <code>HTTP</code> is 0x47 (71).
+     */
+    public static final int HTTP = 0x47;
+
+    /**
+     * Represents the OBEX Who header.  Identifies the OBEX application to
+     * determine if the two peers are talking to each other.
+     * <P>
+     * The value of <code>WHO</code> is 0x4A (74).
+     */
+    public static final int WHO = 0x4A;
+
+    /**
+     * Represents the OBEX Object Class header.  This header specifies the
+     * OBEX object class of the object.
+     * <P>
+     * The value of <code>OBJECT_CLASS</code> is 0x4F (79).
+     */
+    public static final int OBJECT_CLASS = 0x4F;
+
+    /**
+     * Represents the OBEX Application Parameter header.  This header specifies
+     * additional application request and response information.
+     * <P>
+     * The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76).
+     */
+    public static final int APPLICATION_PARAMETER = 0x4C;
+
+    private Long count; // 4 byte unsigned integer
+
+    private String name; // null terminated Unicode text string
+
+    private String type; // null terminated ASCII text string
+
+    private Long length; // 4 byte unsigend integer
+
+    private Calendar isoTime; // String of the form YYYYMMDDTHHMMSSZ
+
+    private Calendar byteTime; // 4 byte unsigned integer
+
+    private String description; // null terminated Unicode text String
+
+    private byte[] target; // byte sequence
+
+    private byte[] http; // byte sequence
+
+    private byte[] who; // length prefixed byte sequence
+
+    private byte[] appParam; // byte sequence of the form tag length value
+
+    public byte[] authChall; // The authentication challenge header
+
+    public byte[] authResp; // The authentication response header
+
+    public byte[] connectionID; // THe connection ID
+
+    private byte[] objectClass; // byte sequence
+
+    private String[] unicodeUserDefined; //null terminated unicode string
+
+    private byte[][] sequenceUserDefined; // byte sequence user defined
+
+    private Byte[] byteUserDefined; // 1 byte
+
+    private Long[] integerUserDefined; // 4 byte unsigned integer
+
+    /*package*/ int responseCode;
+
+    /*package*/ byte[] nonce;
+
+    private final Random random;
+
+    /**
+     * Creates new <code>HeaderSet</code> object.
+     *
+     * @param size the max packet size for this connection
+     */
+    public HeaderSet() {
+        unicodeUserDefined = new String[16];
+        sequenceUserDefined = new byte[16][];
+        byteUserDefined = new Byte[16];
+        integerUserDefined = new Long[16];
+        responseCode = -1;
+        random = new Random();
+    }
+
+    /**
+     * Sets the value of the header identifier to the value provided.  The type
+     * of object must correspond to the Java type defined in the description of
+     * this interface.  If <code>null</code> is passed as the
+     * <code>headerValue</code> then the header will be removed from the set of
+     * headers to include in the next request.
+     *
+     * @param headerID the identifier to include in the message
+     *
+     * @param headerValue the value of the header identifier
+     *
+     * @throws IllegalArgumentException if the header identifier provided is
+     * not one defined in this interface or a user-defined header; if the type of
+     * <code>headerValue</code> is not the correct Java type as defined in the
+     * description of this interface\
+     */
+    public void setHeader(int headerID, Object headerValue) {
+        long temp = -1;
+
+        switch (headerID) {
+            case COUNT:
+                if (!(headerValue instanceof Long)) {
+                    if (headerValue == null) {
+                        count = null;
+                        break;
+                    }
+                    throw new IllegalArgumentException("Count must be a Long");
+                }
+                temp = ((Long)headerValue).longValue();
+                if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
+                    throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF");
+                }
+                count = (Long)headerValue;
+                break;
+            case NAME:
+                if ((headerValue != null) && (!(headerValue instanceof String))) {
+                    throw new IllegalArgumentException("Name must be a String");
+                }
+                name = (String)headerValue;
+                break;
+            case TYPE:
+                if ((headerValue != null) && (!(headerValue instanceof String))) {
+                    throw new IllegalArgumentException("Type must be a String");
+                }
+                type = (String)headerValue;
+                break;
+            case LENGTH:
+                if (!(headerValue instanceof Long)) {
+                    if (headerValue == null) {
+                        length = null;
+                        break;
+                    }
+                    throw new IllegalArgumentException("Length must be a Long");
+                }
+                temp = ((Long)headerValue).longValue();
+                if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
+                    throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF");
+                }
+                length = (Long)headerValue;
+                break;
+            case TIME_ISO_8601:
+                if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
+                    throw new IllegalArgumentException("Time ISO 8601 must be a Calendar");
+                }
+                isoTime = (Calendar)headerValue;
+                break;
+            case TIME_4_BYTE:
+                if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
+                    throw new IllegalArgumentException("Time 4 Byte must be a Calendar");
+                }
+                byteTime = (Calendar)headerValue;
+                break;
+            case DESCRIPTION:
+                if ((headerValue != null) && (!(headerValue instanceof String))) {
+                    throw new IllegalArgumentException("Description must be a String");
+                }
+                description = (String)headerValue;
+                break;
+            case TARGET:
+                if (headerValue == null) {
+                    target = null;
+                } else {
+                    if (!(headerValue instanceof byte[])) {
+                        throw new IllegalArgumentException("Target must be a byte array");
+                    } else {
+                        target = new byte[((byte[])headerValue).length];
+                        System.arraycopy(headerValue, 0, target, 0, target.length);
+                    }
+                }
+                break;
+            case HTTP:
+                if (headerValue == null) {
+                    http = null;
+                } else {
+                    if (!(headerValue instanceof byte[])) {
+                        throw new IllegalArgumentException("HTTP must be a byte array");
+                    } else {
+                        http = new byte[((byte[])headerValue).length];
+                        System.arraycopy(headerValue, 0, http, 0, http.length);
+                    }
+                }
+                break;
+            case WHO:
+                if (headerValue == null) {
+                    who = null;
+                } else {
+                    if (!(headerValue instanceof byte[])) {
+                        throw new IllegalArgumentException("WHO must be a byte array");
+                    } else {
+                        who = new byte[((byte[])headerValue).length];
+                        System.arraycopy(headerValue, 0, who, 0, who.length);
+                    }
+                }
+                break;
+            case OBJECT_CLASS:
+                if (headerValue == null) {
+                    objectClass = null;
+                } else {
+                    if (!(headerValue instanceof byte[])) {
+                        throw new IllegalArgumentException("Object Class must be a byte array");
+                    } else {
+                        objectClass = new byte[((byte[])headerValue).length];
+                        System.arraycopy(headerValue, 0, objectClass, 0, objectClass.length);
+                    }
+                }
+                break;
+            case APPLICATION_PARAMETER:
+                if (headerValue == null) {
+                    appParam = null;
+                } else {
+                    if (!(headerValue instanceof byte[])) {
+                        throw new IllegalArgumentException(
+                                "Application Parameter must be a byte array");
+                    } else {
+                        appParam = new byte[((byte[])headerValue).length];
+                        System.arraycopy(headerValue, 0, appParam, 0, appParam.length);
+                    }
+                }
+                break;
+            default:
+                // Verify that it was not a Unicode String user Defined
+                if ((headerID >= 0x30) && (headerID <= 0x3F)) {
+                    if ((headerValue != null) && (!(headerValue instanceof String))) {
+                        throw new IllegalArgumentException(
+                                "Unicode String User Defined must be a String");
+                    }
+                    unicodeUserDefined[headerID - 0x30] = (String)headerValue;
+
+                    break;
+                }
+                // Verify that it was not a byte sequence user defined value
+                if ((headerID >= 0x70) && (headerID <= 0x7F)) {
+
+                    if (headerValue == null) {
+                        sequenceUserDefined[headerID - 0x70] = null;
+                    } else {
+                        if (!(headerValue instanceof byte[])) {
+                            throw new IllegalArgumentException(
+                                    "Byte Sequence User Defined must be a byte array");
+                        } else {
+                            sequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length];
+                            System.arraycopy(headerValue, 0, sequenceUserDefined[headerID - 0x70],
+                                    0, sequenceUserDefined[headerID - 0x70].length);
+                        }
+                    }
+                    break;
+                }
+                // Verify that it was not a Byte user Defined
+                if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
+                    if ((headerValue != null) && (!(headerValue instanceof Byte))) {
+                        throw new IllegalArgumentException("ByteUser Defined must be a Byte");
+                    }
+                    byteUserDefined[headerID - 0xB0] = (Byte)headerValue;
+
+                    break;
+                }
+                // Verify that is was not the 4 byte unsigned integer user
+                // defined header
+                if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
+                    if (!(headerValue instanceof Long)) {
+                        if (headerValue == null) {
+                            integerUserDefined[headerID - 0xF0] = null;
+                            break;
+                        }
+                        throw new IllegalArgumentException("Integer User Defined must be a Long");
+                    }
+                    temp = ((Long)headerValue).longValue();
+                    if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
+                        throw new IllegalArgumentException(
+                                "Integer User Defined must be between 0 and 0xFFFFFFFF");
+                    }
+                    integerUserDefined[headerID - 0xF0] = (Long)headerValue;
+                    break;
+                }
+                throw new IllegalArgumentException("Invalid Header Identifier");
+        }
+    }
+
+    /**
+     * Retrieves the value of the header identifier provided.  The type of the
+     * Object returned is defined in the description of this interface.
+     *
+     * @param headerID the header identifier whose value is to be returned
+     *
+     * @return the value of the header provided or <code>null</code> if the
+     * header identifier specified is not part of this <code>HeaderSet</code>
+     * object
+     *
+     * @throws IllegalArgumentException if the <code>headerID</code> is not
+     * one defined in this interface or any of the user-defined headers
+     *
+     * @throws IOException if an error occurred in the transport layer during
+     * the operation or if the connection has been closed
+     */
+    public Object getHeader(int headerID) throws IOException {
+
+        switch (headerID) {
+            case COUNT:
+                return count;
+            case NAME:
+                return name;
+            case TYPE:
+                return type;
+            case LENGTH:
+                return length;
+            case TIME_ISO_8601:
+                return isoTime;
+            case TIME_4_BYTE:
+                return byteTime;
+            case DESCRIPTION:
+                return description;
+            case TARGET:
+                return target;
+            case HTTP:
+                return http;
+            case WHO:
+                return who;
+            case OBJECT_CLASS:
+                return objectClass;
+            case APPLICATION_PARAMETER:
+                return appParam;
+            default:
+                // Verify that it was not a Unicode String user Defined
+                if ((headerID >= 0x30) && (headerID <= 0x3F)) {
+                    return unicodeUserDefined[headerID - 0x30];
+                }
+                // Verify that it was not a byte sequence user defined header
+                if ((headerID >= 0x70) && (headerID <= 0x7F)) {
+                    return sequenceUserDefined[headerID - 0x70];
+                }
+                // Verify that it was not a byte user defined header
+                if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
+                    return byteUserDefined[headerID - 0xB0];
+                }
+                // Verify that it was not a itneger user defined header
+                if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
+                    return integerUserDefined[headerID - 0xF0];
+                }
+                throw new IllegalArgumentException("Invalid Header Identifier");
+        }
+    }
+
+    /**
+     * Retrieves the list of headers that may be retrieved via the
+     * <code>getHeader</code> method that will not return <code>null</code>.
+     * In other words, this method returns all the headers that are available
+     * in this object.
+     *
+     * @see #getHeader
+     *
+     * @return the array of headers that are set in this object or
+     * <code>null</code> if no headers are available
+     *
+     * @throws IOException if an error occurred in the transport layer during
+     * the operation or the connection has been closed
+     */
+    public int[] getHeaderList() throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        if (count != null) {
+            out.write(COUNT);
+        }
+        if (name != null) {
+            out.write(NAME);
+        }
+        if (type != null) {
+            out.write(TYPE);
+        }
+        if (length != null) {
+            out.write(LENGTH);
+        }
+        if (isoTime != null) {
+            out.write(TIME_ISO_8601);
+        }
+        if (byteTime != null) {
+            out.write(TIME_4_BYTE);
+        }
+        if (description != null) {
+            out.write(DESCRIPTION);
+        }
+        if (target != null) {
+            out.write(TARGET);
+        }
+        if (http != null) {
+            out.write(HTTP);
+        }
+        if (who != null) {
+            out.write(WHO);
+        }
+        if (appParam != null) {
+            out.write(APPLICATION_PARAMETER);
+        }
+        if (objectClass != null) {
+            out.write(OBJECT_CLASS);
+        }
+
+        for (int i = 0x30; i < 0x40; i++) {
+            if (unicodeUserDefined[i - 0x30] != null) {
+                out.write(i);
+            }
+        }
+
+        for (int i = 0x70; i < 0x80; i++) {
+            if (sequenceUserDefined[i - 0x70] != null) {
+                out.write(i);
+            }
+        }
+
+        for (int i = 0xB0; i < 0xC0; i++) {
+            if (byteUserDefined[i - 0xB0] != null) {
+                out.write(i);
+            }
+        }
+
+        for (int i = 0xF0; i < 0x100; i++) {
+            if (integerUserDefined[i - 0xF0] != null) {
+                out.write(i);
+            }
+        }
+
+        byte[] headers = out.toByteArray();
+        out.close();
+
+        if ((headers == null) || (headers.length == 0)) {
+            return null;
+        }
+
+        int[] result = new int[headers.length];
+        for (int i = 0; i < headers.length; i++) {
+            // Convert the byte to a positive integer.  That is, an integer
+            // between 0 and 256.
+            result[i] = headers[i] & 0xFF;
+        }
+
+        return result;
+    }
+
+    /**
+     * Sets the authentication challenge header.  The <code>realm</code> will
+     * be encoded based upon the default encoding scheme used by the
+     * implementation to encode strings.  Therefore, the encoding scheme used
+     * to encode the <code>realm</code> is application dependent.
+     *
+     * @param realm a short description that describes what password to use; if
+     * <code>null</code> no realm will be sent in the authentication challenge
+     * header
+     *
+     * @param userID if <code>true</code>, a user ID is required in the reply;
+     * if <code>false</code>, no user ID is required
+     *
+     * @param access if <code>true</code> then full access will be granted if
+     * successful; if <code>false</code> then read-only access will be granted
+     * if successful
+     */
+    public void createAuthenticationChallenge(String realm, boolean userID, boolean access) {
+
+        try {
+            nonce = new byte[16];
+            for (int i = 0; i < 16; i++) {
+                nonce[i] = (byte)random.nextInt();
+            }
+
+            authChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID);
+        } catch (IOException e) {
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    /**
+     * Returns the response code received from the server.  Response codes
+     * are defined in the <code>ResponseCodes</code> class.
+     *
+     * @see ResponseCodes
+     *
+     * @return the response code retrieved from the server
+     *
+     * @throws IOException if an error occurred in the transport layer during
+     * the transaction; if this method is called on a <code>HeaderSet</code>
+     * object created by calling <code>createHeaderSet()</code> in a
+     * <code>ClientSession</code> object; if this object was created by an OBEX
+     * server
+     */
+    public int getResponseCode() throws IOException {
+        if (responseCode == -1) {
+            throw new IOException("May not be called on a server");
+        } else {
+            return responseCode;
+        }
+    }
+}
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
new file mode 100644
index 0000000..6bf5e3e
--- /dev/null
+++ b/obex/javax/obex/ObexHelper.java
@@ -0,0 +1,995 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import android.security.Md5MessageDigest;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * This class defines a set of helper methods for the implementation of Obex.
+ *
+ * @hide
+ */
+public final class ObexHelper {
+
+    /** Prevent object construction of helper class */
+    private ObexHelper() {}
+
+    /**
+     * Defines the OBEX CONTINUE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_CONTINUE</code> is 0x90 (144).
+     */
+    public static final int OBEX_HTTP_CONTINUE = 0x90;
+
+    /**
+     * The maximum packet size for OBEX packets that this client can handle.
+     * At present, this must be changed for each port.
+     *
+     * TODO: The max packet size should be the Max incoming MTU minus
+     * TODO: L2CAP package headers and RFCOMM package headers.
+     *
+     * TODO: Retrieve the max incoming MTU from
+     * TODO: LocalDevice.getProperty().
+     */
+    /** android note
+     *  set as 0xFFFE to match remote MPS
+     */
+    public static final int MAX_PACKET_SIZE_INT = 0xFFFE;
+
+    /**
+     * Updates the HeaderSet with the headers received in the byte array
+     * provided.  Invalid headers are ignored.
+     * <P>
+     * The first two bits of an OBEX Header specifies the type of object that
+     * is being sent.  The table below specifies the meaning of the high
+     * bits.
+     * <TABLE>
+     * <TR><TH>Bits 8 and 7</TH><TH>Value</TH><TH>Description</TH></TR>
+     * <TR><TD>00</TD><TD>0x00</TD><TD>Null Terminated Unicode text, prefixed
+     * with 2 byte unsigned integer</TD></TR>
+     * <TR><TD>01</TD><TD>0x40</TD><TD>Byte Sequence, length prefixed with
+     * 2 byte unsigned integer</TD></TR>
+     * <TR><TD>10</TD><TD>0x80</TD><TD>1 byte quantity</TD></TR>
+     * <TR><TD>11</TD><TD>0xC0</TD><TD>4 byte quantity - transmitted in
+     * network byte order (high byte first</TD></TR>
+     * </TABLE>
+     * This method uses the information in this table to determine the type of
+     * Java object to create and passes that object with the full header
+     * to setHeader() to update the HeaderSet object.  Invalid headers will
+     * cause an exception to be thrown.  When it is thrown, it is ignored.
+     *
+     * @param header the HeaderSet to update
+     *
+     * @param headerArray the byte array containing headers
+     *
+     * @return the result of the last start body or end body header provided;
+     * the first byte in the result will specify if a body or end of body is
+     * received
+     *
+     * @throws IOException if an invalid header was found
+     */
+    public static byte[] updateHeaderSet(HeaderSet header, byte[] headerArray) throws IOException {
+        int index = 0;
+        int length = 0;
+        int headerID;
+        byte[] value = null;
+        byte[] body = null;
+        HeaderSet headerImpl = header;
+        try {
+            while (index < headerArray.length) {
+                headerID = 0xFF & headerArray[index];
+                switch (headerID & (0xC0)) {
+
+                    /*
+                     * 0x00 is a unicode null terminate string with the first
+                     * two bytes after the header identifier being the length
+                     */
+                    case 0x00:
+                        // Fall through
+                        /*
+                         * 0x40 is a byte sequence with the first
+                         * two bytes after the header identifier being the length
+                         */
+                    case 0x40:
+                        boolean trimTail = true;
+                        index++;
+                        length = 0xFF & headerArray[index];
+                        length = length << 8;
+                        index++;
+                        length += 0xFF & headerArray[index];
+                        length -= 3;
+                        index++;
+                        value = new byte[length];
+                        System.arraycopy(headerArray, index, value, 0, length);
+                        if (length == 0 || (length > 0 && (value[length - 1] != 0))) {
+                            trimTail = false;
+                        }
+                        switch (headerID) {
+                            case HeaderSet.TYPE:
+                                try {
+                                    // Remove trailing null
+                                    if (trimTail == false) {
+                                        headerImpl.setHeader(headerID, new String(value, 0,
+                                                value.length, "ISO8859_1"));
+                                    } else {
+                                        headerImpl.setHeader(headerID, new String(value, 0,
+                                                value.length - 1, "ISO8859_1"));
+                                    }
+                                } catch (UnsupportedEncodingException e) {
+                                    throw new RuntimeException("ISO8859_1 is not supported"
+                                            + e.getMessage());
+                                }
+                                break;
+
+                            // This is the constant for the authentication challenge header
+                            // This header does not have a constant defined in the Java
+                            // OBEX API
+                            case 0x4D:
+                                headerImpl.authChall = new byte[length];
+                                System.arraycopy(headerArray, index, headerImpl.authChall, 0,
+                                        length);
+                                break;
+
+                            // This is the constant for the authentication response header
+                            // This header does not have a constant defined in the Java
+                            // OBEX API
+                            case 0x4E:
+                                headerImpl.authResp = new byte[length];
+                                System
+                                        .arraycopy(headerArray, index, headerImpl.authResp, 0,
+                                                length);
+                                break;
+
+                            /*
+                            * These two case statements are for the body (0x48)
+                             * and end of body (0x49) headers.
+                            */
+                            case 0x48:
+                                /* Fall Through */
+                            case 0x49:
+                                body = new byte[length + 1];
+                                body[0] = (byte)headerID;
+                                System.arraycopy(headerArray, index, body, 1, length);
+                                break;
+
+                            case HeaderSet.TIME_ISO_8601:
+                                try {
+                                    String dateString = new String(value, "ISO8859_1");
+                                    Calendar temp = Calendar.getInstance();
+                                    if ((dateString.length() == 16)
+                                            && (dateString.charAt(15) == 'Z')) {
+                                        temp.setTimeZone(TimeZone.getTimeZone("UTC"));
+                                    }
+                                    temp.set(Calendar.YEAR, Integer.parseInt(dateString.substring(
+                                            0, 4)));
+                                    temp.set(Calendar.MONTH, Integer.parseInt(dateString.substring(
+                                            4, 6)));
+                                    temp.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateString
+                                            .substring(6, 8)));
+                                    temp.set(Calendar.HOUR_OF_DAY, Integer.parseInt(dateString
+                                            .substring(9, 11)));
+                                    temp.set(Calendar.MINUTE, Integer.parseInt(dateString
+                                            .substring(11, 13)));
+                                    temp.set(Calendar.SECOND, Integer.parseInt(dateString
+                                            .substring(13, 15)));
+                                    headerImpl.setHeader(HeaderSet.TIME_ISO_8601, temp);
+                                } catch (UnsupportedEncodingException e) {
+                                    throw new RuntimeException("ISO8859_1 is not supported"
+                                            + e.getMessage());
+                                } catch (Exception e) {
+                                    throw new IOException(
+                                            "Time Header does not follow ISO 8601 standard");
+                                }
+                                break;
+
+                            default:
+                                try {
+                                    if ((headerID & 0xC0) == 0x00) {
+                                        headerImpl.setHeader(headerID, ObexHelper.convertToUnicode(
+                                                value, true));
+                                    } else {
+                                        headerImpl.setHeader(headerID, value);
+                                    }
+                                } catch (Exception e) {
+                                    // Not a valid header so ignore
+                                }
+                        }
+
+                        index += length;
+                        break;
+
+                    /*
+                     * 0x80 is a byte header.  The only valid byte headers are
+                     * the 16 user defined byte headers.
+                     */
+                    case 0x80:
+                        index++;
+                        try {
+                            headerImpl.setHeader(headerID, Byte.valueOf(headerArray[index]));
+                        } catch (Exception e) {
+                            // Not a valid header so ignore
+                        }
+                        index++;
+                        break;
+
+                    /*
+                     * 0xC0 is a 4 byte unsigned integer header and with the
+                     * exception of TIME_4_BYTE will be converted to a Long
+                     * and added.
+                     */
+                    case 0xC0:
+                        index++;
+                        value = new byte[4];
+                        System.arraycopy(headerArray, index, value, 0, 4);
+                        try {
+                            if (headerID != HeaderSet.TIME_4_BYTE) {
+                                // Determine if it is a connection ID.  These
+                                // need to be handled differently
+                                if (headerID == 0xCB) {
+                                    headerImpl.connectionID = new byte[4];
+                                    System.arraycopy(value, 0, headerImpl.connectionID, 0, 4);
+                                } else {
+                                    headerImpl.setHeader(headerID, Long
+                                            .valueOf(convertToLong(value)));
+                                }
+                            } else {
+                                Calendar temp = Calendar.getInstance();
+                                temp.setTime(new Date(convertToLong(value) * 1000L));
+                                headerImpl.setHeader(HeaderSet.TIME_4_BYTE, temp);
+                            }
+                        } catch (Exception e) {
+                            // Not a valid header so ignore
+                            throw new IOException("Header was not formatted properly");
+                        }
+                        index += 4;
+                        break;
+                }
+
+            }
+        } catch (IOException e) {
+            throw new IOException("Header was not formatted properly");
+        }
+
+        return body;
+    }
+
+    /**
+     * Creates the header part of OBEX packet based on the header provided.
+     *
+     * TODO: Could use getHeaderList() to get the array of headers to
+     * TODO: include and then use the high two bits to determine the
+     * TODO: the type of the object and construct the byte array from
+     * TODO: that.  This will make the size smaller.
+     *
+     * @param head the header used to construct the byte array
+     *
+     * @param nullOut <code>true</code> if the header should be set to
+     * <code>null</code> once it is added to the array or <code>false</code>
+     * if it should not be nulled out
+     *
+     * @return the header of an OBEX packet
+     */
+    public static byte[] createHeader(HeaderSet head, boolean nullOut) {
+        Long intHeader = null;
+        String stringHeader = null;
+        Calendar dateHeader = null;
+        Byte byteHeader = null;
+        StringBuffer buffer = null;
+        byte[] value = null;
+        byte[] result = null;
+        byte[] lengthArray = new byte[2];
+        int length;
+        HeaderSet headImpl = null;
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        if (!(head instanceof HeaderSet)) {
+            throw new IllegalArgumentException("Header not created by createHeaderSet");
+        }
+        headImpl = head;
+
+        try {
+            /*
+             * Determine if there is a connection ID to send.  If there is,
+             * then it should be the first header in the packet.
+             */
+            if ((headImpl.connectionID != null) && (headImpl.getHeader(HeaderSet.TARGET) == null)) {
+
+                out.write((byte)0xCB);
+                out.write(headImpl.connectionID);
+            }
+
+            // Count Header
+            intHeader = (Long)headImpl.getHeader(HeaderSet.COUNT);
+            if (intHeader != null) {
+                out.write((byte)HeaderSet.COUNT);
+                value = ObexHelper.convertToByteArray(intHeader.longValue());
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.COUNT, null);
+                }
+            }
+
+            // Name Header
+            stringHeader = (String)headImpl.getHeader(HeaderSet.NAME);
+            if (stringHeader != null) {
+                out.write((byte)HeaderSet.NAME);
+                value = ObexHelper.convertToUnicodeByteArray(stringHeader);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.NAME, null);
+                }
+            }
+
+            // Type Header
+            stringHeader = (String)headImpl.getHeader(HeaderSet.TYPE);
+            if (stringHeader != null) {
+                out.write((byte)HeaderSet.TYPE);
+                try {
+                    value = stringHeader.getBytes("ISO8859_1");
+                } catch (UnsupportedEncodingException e) {
+                    throw new RuntimeException("Unsupported Encoding Scheme: " + e.getMessage());
+                }
+
+                length = value.length + 4;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                out.write(0x00);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.TYPE, null);
+                }
+            }
+
+            // Length Header
+            intHeader = (Long)headImpl.getHeader(HeaderSet.LENGTH);
+            if (intHeader != null) {
+                out.write((byte)HeaderSet.LENGTH);
+                value = ObexHelper.convertToByteArray(intHeader.longValue());
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.LENGTH, null);
+                }
+            }
+
+            // Time ISO Header
+            dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_ISO_8601);
+            if (dateHeader != null) {
+
+                /*
+                 * The ISO Header should take the form YYYYMMDDTHHMMSSZ.  The
+                 * 'Z' will only be included if it is a UTC time.
+                 */
+                buffer = new StringBuffer();
+                int temp = dateHeader.get(Calendar.YEAR);
+                for (int i = temp; i < 1000; i = i * 10) {
+                    buffer.append("0");
+                }
+                buffer.append(temp);
+                temp = dateHeader.get(Calendar.MONTH);
+                if (temp < 10) {
+                    buffer.append("0");
+                }
+                buffer.append(temp);
+                temp = dateHeader.get(Calendar.DAY_OF_MONTH);
+                if (temp < 10) {
+                    buffer.append("0");
+                }
+                buffer.append(temp);
+                buffer.append("T");
+                temp = dateHeader.get(Calendar.HOUR_OF_DAY);
+                if (temp < 10) {
+                    buffer.append("0");
+                }
+                buffer.append(temp);
+                temp = dateHeader.get(Calendar.MINUTE);
+                if (temp < 10) {
+                    buffer.append("0");
+                }
+                buffer.append(temp);
+                temp = dateHeader.get(Calendar.SECOND);
+                if (temp < 10) {
+                    buffer.append("0");
+                }
+                buffer.append(temp);
+
+                if (dateHeader.getTimeZone().getID().equals("UTC")) {
+                    buffer.append("Z");
+                }
+
+                try {
+                    value = buffer.toString().getBytes("ISO8859_1");
+                } catch (UnsupportedEncodingException e) {
+                    throw new RuntimeException("UnsupportedEncodingException: " + e.getMessage());
+                }
+
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(HeaderSet.TIME_ISO_8601);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.TIME_ISO_8601, null);
+                }
+            }
+
+            // Time 4 Byte Header
+            dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_4_BYTE);
+            if (dateHeader != null) {
+                out.write(HeaderSet.TIME_4_BYTE);
+
+                /*
+                 * Need to call getTime() twice.  The first call will return
+                 * a java.util.Date object.  The second call returns the number
+                 * of milliseconds since January 1, 1970.  We need to convert
+                 * it to seconds since the TIME_4_BYTE expects the number of
+                 * seconds since January 1, 1970.
+                 */
+                value = ObexHelper.convertToByteArray(dateHeader.getTime().getTime() / 1000L);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.TIME_4_BYTE, null);
+                }
+            }
+
+            // Description Header
+            stringHeader = (String)headImpl.getHeader(HeaderSet.DESCRIPTION);
+            if (stringHeader != null) {
+                out.write((byte)HeaderSet.DESCRIPTION);
+                value = ObexHelper.convertToUnicodeByteArray(stringHeader);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.DESCRIPTION, null);
+                }
+            }
+
+            // Target Header
+            value = (byte[])headImpl.getHeader(HeaderSet.TARGET);
+            if (value != null) {
+                out.write((byte)HeaderSet.TARGET);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.TARGET, null);
+                }
+            }
+
+            // HTTP Header
+            value = (byte[])headImpl.getHeader(HeaderSet.HTTP);
+            if (value != null) {
+                out.write((byte)HeaderSet.HTTP);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.HTTP, null);
+                }
+            }
+
+            // Who Header
+            value = (byte[])headImpl.getHeader(HeaderSet.WHO);
+            if (value != null) {
+                out.write((byte)HeaderSet.WHO);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.WHO, null);
+                }
+            }
+
+            // Connection ID Header
+            value = (byte[])headImpl.getHeader(HeaderSet.APPLICATION_PARAMETER);
+            if (value != null) {
+                out.write((byte)HeaderSet.APPLICATION_PARAMETER);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.APPLICATION_PARAMETER, null);
+                }
+            }
+
+            // Object Class Header
+            value = (byte[])headImpl.getHeader(HeaderSet.OBJECT_CLASS);
+            if (value != null) {
+                out.write((byte)HeaderSet.OBJECT_CLASS);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.OBJECT_CLASS, null);
+                }
+            }
+
+            // Check User Defined Headers
+            for (int i = 0; i < 16; i++) {
+
+                //Unicode String Header
+                stringHeader = (String)headImpl.getHeader(i + 0x30);
+                if (stringHeader != null) {
+                    out.write((byte)i + 0x30);
+                    value = ObexHelper.convertToUnicodeByteArray(stringHeader);
+                    length = value.length + 3;
+                    lengthArray[0] = (byte)(255 & (length >> 8));
+                    lengthArray[1] = (byte)(255 & length);
+                    out.write(lengthArray);
+                    out.write(value);
+                    if (nullOut) {
+                        headImpl.setHeader(i + 0x30, null);
+                    }
+                }
+
+                // Byte Sequence Header
+                value = (byte[])headImpl.getHeader(i + 0x70);
+                if (value != null) {
+                    out.write((byte)i + 0x70);
+                    length = value.length + 3;
+                    lengthArray[0] = (byte)(255 & (length >> 8));
+                    lengthArray[1] = (byte)(255 & length);
+                    out.write(lengthArray);
+                    out.write(value);
+                    if (nullOut) {
+                        headImpl.setHeader(i + 0x70, null);
+                    }
+                }
+
+                // Byte Header
+                byteHeader = (Byte)headImpl.getHeader(i + 0xB0);
+                if (byteHeader != null) {
+                    out.write((byte)i + 0xB0);
+                    out.write(byteHeader.byteValue());
+                    if (nullOut) {
+                        headImpl.setHeader(i + 0xB0, null);
+                    }
+                }
+
+                // Integer header
+                intHeader = (Long)headImpl.getHeader(i + 0xF0);
+                if (intHeader != null) {
+                    out.write((byte)i + 0xF0);
+                    out.write(ObexHelper.convertToByteArray(intHeader.longValue()));
+                    if (nullOut) {
+                        headImpl.setHeader(i + 0xF0, null);
+                    }
+                }
+            }
+
+            // Add the authentication challenge header
+            if (headImpl.authChall != null) {
+                out.write((byte)0x4D);
+                length = headImpl.authChall.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(headImpl.authChall);
+                if (nullOut) {
+                    headImpl.authChall = null;
+                }
+            }
+
+            // Add the authentication response header
+            if (headImpl.authResp != null) {
+                out.write((byte)0x4E);
+                length = headImpl.authResp.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(headImpl.authResp);
+                if (nullOut) {
+                    headImpl.authResp = null;
+                }
+            }
+
+        } catch (IOException e) {
+        } finally {
+            result = out.toByteArray();
+            try {
+                out.close();
+            } catch (Exception ex) {
+            }
+        }
+
+        return result;
+
+    }
+
+    /**
+     * Determines where the maximum divide is between headers.  This method is
+     * used by put and get operations to separate headers to a size that meets
+     * the max packet size allowed.
+     *
+     * @param headerArray the headers to separate
+     *
+     * @param start the starting index to search
+     *
+     * @param maxSize the maximum size of a packet
+     *
+     * @return the index of the end of the header block to send or -1 if the
+     * header could not be divided because the header is too large
+     */
+    public static int findHeaderEnd(byte[] headerArray, int start, int maxSize) {
+
+        int fullLength = 0;
+        int lastLength = -1;
+        int index = start;
+        int length = 0;
+
+        while ((fullLength < maxSize) && (index < headerArray.length)) {
+            int headerID = (headerArray[index] < 0 ? headerArray[index] + 256 : headerArray[index]);
+            lastLength = fullLength;
+
+            switch (headerID & (0xC0)) {
+
+                case 0x00:
+                    // Fall through
+                case 0x40:
+
+                    index++;
+                    length = (headerArray[index] < 0 ? headerArray[index] + 256
+                            : headerArray[index]);
+                    length = length << 8;
+                    index++;
+                    length += (headerArray[index] < 0 ? headerArray[index] + 256
+                            : headerArray[index]);
+                    length -= 3;
+                    index++;
+                    index += length;
+                    fullLength += length + 3;
+                    break;
+
+                case 0x80:
+
+                    index++;
+                    index++;
+                    fullLength += 2;
+                    break;
+
+                case 0xC0:
+
+                    index += 5;
+                    fullLength += 5;
+                    break;
+
+            }
+
+        }
+
+        /*
+         * Determine if this is the last header or not
+         */
+        if (lastLength == 0) {
+            /*
+             * Since this is the last header, check to see if the size of this
+             * header is less then maxSize.  If it is, return the length of the
+             * header, otherwise return -1.  The length of the header is
+             * returned since it would be the start of the next header
+             */
+            if (fullLength < maxSize) {
+                return headerArray.length;
+            } else {
+                return -1;
+            }
+        } else {
+            return lastLength + start;
+        }
+    }
+
+    /**
+     * Converts the byte array to a long.
+     *
+     * @param b the byte array to convert to a long
+     *
+     * @return the byte array as a long
+     */
+    public static long convertToLong(byte[] b) {
+        long result = 0;
+        long value = 0;
+        long power = 0;
+
+        for (int i = (b.length - 1); i >= 0; i--) {
+            value = b[i];
+            if (value < 0) {
+                value += 256;
+            }
+
+            result = result | (value << power);
+            power += 8;
+        }
+
+        return result;
+    }
+
+    /**
+     * Converts the long to a 4 byte array.  The long must be non negative.
+     *
+     * @param l the long to convert
+     *
+     * @return a byte array that is the same as the long
+     */
+    public static byte[] convertToByteArray(long l) {
+        byte[] b = new byte[4];
+
+        b[0] = (byte)(255 & (l >> 24));
+        b[1] = (byte)(255 & (l >> 16));
+        b[2] = (byte)(255 & (l >> 8));
+        b[3] = (byte)(255 & l);
+
+        return b;
+    }
+
+    /**
+     * Converts the String to a UNICODE byte array.  It will also add the ending
+     * null characters to the end of the string.
+     *
+     * @param s the string to convert
+     *
+     * @return the unicode byte array of the string
+     */
+    public static byte[] convertToUnicodeByteArray(String s) {
+        if (s == null) {
+            return null;
+        }
+
+        char c[] = s.toCharArray();
+        byte[] result = new byte[(c.length * 2) + 2];
+        for (int i = 0; i < c.length; i++) {
+            result[(i * 2)] = (byte)(c[i] >> 8);
+            result[((i * 2) + 1)] = (byte)c[i];
+        }
+
+        // Add the UNICODE null character
+        result[result.length - 2] = 0;
+        result[result.length - 1] = 0;
+
+        return result;
+    }
+
+    /**
+     * Retrieves the value from the byte array for the tag value specified.  The
+     * array should be of the form Tag - Length - Value triplet.
+     *
+     * @param tag the tag to retrieve from the byte array
+     *
+     * @param triplet the byte sequence containing the tag length value form
+     *
+     * @return the value of the specified tag
+     */
+    public static byte[] getTagValue(byte tag, byte[] triplet) {
+
+        int index = findTag(tag, triplet);
+        if (index == -1) {
+            return null;
+        }
+
+        index++;
+        int length = triplet[index] & 0xFF;
+
+        byte[] result = new byte[length];
+        index++;
+        System.arraycopy(triplet, index, result, 0, length);
+
+        return result;
+    }
+
+    /**
+     * Finds the index that starts the tag value pair in the byte array provide.
+     *
+     * @param tag the tag to look for
+     *
+     * @param value the byte array to search
+     *
+     * @return the starting index of the tag or -1 if the tag could not be found
+     */
+    public static int findTag(byte tag, byte[] value) {
+        int length = 0;
+
+        if (value == null) {
+            return -1;
+        }
+
+        int index = 0;
+
+        while ((index < value.length) && (value[index] != tag)) {
+            length = value[index + 1] & 0xFF;
+            index += length + 2;
+        }
+
+        if (index >= value.length) {
+            return -1;
+        }
+
+        return index;
+    }
+
+    /**
+     * Converts the byte array provided to a unicode string.
+     *
+     * @param b the byte array to convert to a string
+     *
+     * @param includesNull determine if the byte string provided contains the
+     * UNICODE null character at the end or not;  if it does, it will be
+     * removed
+     *
+     * @return a Unicode string
+     *
+     * @param IllegalArgumentException if the byte array has an odd length
+     */
+    public static String convertToUnicode(byte[] b, boolean includesNull) {
+        if (b == null) {
+            return null;
+        }
+        int arrayLength = b.length;
+        if (!((arrayLength % 2) == 0)) {
+            throw new IllegalArgumentException("Byte array not of a valid form");
+        }
+        arrayLength = (arrayLength >> 1);
+        if (includesNull) {
+            arrayLength -= 1;
+        }
+
+        char[] c = new char[arrayLength];
+        for (int i = 0; i < arrayLength; i++) {
+            int upper = b[2 * i];
+            int lower = b[(2 * i) + 1];
+            if (upper < 0) {
+                upper += 256;
+            }
+            if (lower < 0) {
+                lower += 256;
+            }
+
+            c[i] = (char)((upper << 8) | lower);
+        }
+
+        return new String(c);
+    }
+
+    /**
+     * Compute the MD5 hash of the byte array provided.
+     * Does not accumulate input.
+     *
+     * @param in the byte array to hash
+     * @return the MD5 hash of the byte array
+     */
+    public static byte[] computeMd5Hash(byte[] in) {
+        Md5MessageDigest md5 = new Md5MessageDigest();
+        return md5.digest(in);
+    }
+
+    /**
+     * Computes an authentication challenge header.
+     *
+     *
+     * @param nonce the challenge that will be provided to the peer;  the
+     * challenge must be 16 bytes long
+     *
+     * @param realm a short description that describes what password to use
+     *
+     * @param access if <code>true</code> then full access will be granted if
+     * successful; if <code>false</code> then read only access will be granted
+     * if successful
+     *
+     * @param userID if <code>true</code>, a user ID is required in the reply;
+     * if <code>false</code>, no user ID is required
+     *
+     * @throws IllegalArgumentException if the challenge is not 16 bytes
+     * long; if the realm can not be encoded in less then 255 bytes
+     *
+     * @throws IOException if the encoding scheme ISO 8859-1 is not supported
+     */
+    public static byte[] computeAuthenticationChallenge(byte[] nonce, String realm, boolean access,
+            boolean userID) throws IOException {
+        byte[] authChall = null;
+
+        if (nonce.length != 16) {
+            throw new IllegalArgumentException("Nonce must be 16 bytes long");
+        }
+
+        /*
+         * The authentication challenge is a byte sequence of the following form
+         * byte 0: 0x00 - the tag for the challenge
+         * byte 1: 0x10 - the length of the challenge; must be 16
+         * byte 2-17: the authentication challenge
+         * byte 18: 0x01 - the options tag; this is optional in the spec, but
+         *                 we are going to include it in every message
+         * byte 19: 0x01 - length of the options; must be 1
+         * byte 20: the value of the options; bit 0 is set if user ID is
+         *          required; bit 1 is set if access mode is read only
+         * byte 21: 0x02 - the tag for authentication realm; only included if
+         *                 an authentication realm is specified
+         * byte 22: the length of the authentication realm; only included if
+         *          the authentication realm is specified
+         * byte 23: the encoding scheme of the authentication realm; we will use
+         *          the ISO 8859-1 encoding scheme since it is part of the KVM
+         * byte 24 & up: the realm if one is specified.
+         */
+        if (realm == null) {
+            authChall = new byte[21];
+        } else {
+            if (realm.length() >= 255) {
+                throw new IllegalArgumentException("Realm must be less then 255 bytes");
+            }
+            authChall = new byte[24 + realm.length()];
+            authChall[21] = 0x02;
+            authChall[22] = (byte)(realm.length() + 1);
+            authChall[23] = 0x01; // ISO 8859-1 Encoding
+            System.arraycopy(realm.getBytes("ISO8859_1"), 0, authChall, 24, realm.length());
+        }
+
+        // Include the nonce field in the header
+        authChall[0] = 0x00;
+        authChall[1] = 0x10;
+        System.arraycopy(nonce, 0, authChall, 2, 16);
+
+        // Include the options header
+        authChall[18] = 0x01;
+        authChall[19] = 0x01;
+        authChall[20] = 0x00;
+
+        if (!access) {
+            authChall[20] = (byte)(authChall[20] | 0x02);
+        }
+        if (userID) {
+            authChall[20] = (byte)(authChall[20] | 0x01);
+        }
+
+        return authChall;
+    }
+}
diff --git a/obex/javax/obex/ObexSession.java b/obex/javax/obex/ObexSession.java
new file mode 100644
index 0000000..1dc50c6c
--- /dev/null
+++ b/obex/javax/obex/ObexSession.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+
+/**
+ * The <code>ObexSession</code> interface characterizes the term
+ * "OBEX Connection" as defined in the IrDA Object Exchange Protocol v1.2, which
+ * could be the server-side view of an OBEX connection, or the client-side view
+ * of the same connection, which is established by server's accepting of a
+ * client issued "CONNECT".
+ * <P>
+ * 
+ * This interface serves as the common super class for
+ * <CODE>ClientSession</CODE> and <CODE>ServerSession</CODE>.
+ * 
+ * @hide
+ */
+public interface ObexSession {
+
+    public void close() throws IOException;
+
+}
diff --git a/obex/javax/obex/ObexTransport.java b/obex/javax/obex/ObexTransport.java
new file mode 100644
index 0000000..d0ba0c9
--- /dev/null
+++ b/obex/javax/obex/ObexTransport.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * The <code>ObexTransport</code> interface defines the underlying transport
+ * connection which carries the OBEX protocol( such as TCP, RFCOMM device file
+ * exposed by Bluetooth or USB in kernel, RFCOMM socket emulated in Android
+ * platform, Irda). This interface provides an abstract layer to be used by the
+ * <code>ObexConnection</code>. Each kind of medium shall have its own
+ * implementation to wrap and follow the same interface.
+ * <P>
+ * See section 1.2.2 of IrDA Object Exchange Protocol specification.
+ * <P>
+ * Different kind of medium may have different construction - for example, the
+ * RFCOMM device file medium may be constructed from a file descriptor or simply
+ * a string while the TCP medium usually from a socket.
+ * 
+ * @hide
+ */
+public interface ObexTransport {
+
+    void create() throws IOException;
+
+    void listen() throws IOException;
+
+    void close() throws IOException;
+
+    void connect() throws IOException;
+
+    void disconnect() throws IOException;
+
+    InputStream openInputStream() throws IOException;
+
+    OutputStream openOutputStream() throws IOException;
+
+    DataInputStream openDataInputStream() throws IOException;
+
+    DataOutputStream openDataOutputStream() throws IOException;
+
+}
diff --git a/obex/javax/obex/Operation.java b/obex/javax/obex/Operation.java
new file mode 100644
index 0000000..5a8bcec
--- /dev/null
+++ b/obex/javax/obex/Operation.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * The <code>Operation</code> interface provides ways to manipulate a single
+ * OBEX PUT or GET operation.  The implementation of this interface sends
+ * OBEX packets as they are built.  If during the operation the peer in the
+ * operation ends the operation, an <code>IOException</code> is thrown on
+ * the next read from the input stream, write to the output stream, or call to
+ * <code>sendHeaders()</code>.
+ * <P>
+ * <STRONG>Definition of methods inherited from <code>ContentConnection</code></STRONG>
+ * <P>
+ * <code>getEncoding()</code> will always return <code>null</code>.
+ * <BR><code>getLength()</code> will return the length specified by the OBEX Length
+ * header or -1 if the OBEX Length header was not included.
+ * <BR><code>getType()</code> will return the value specified in the OBEX Type
+ * header or <code>null</code> if the OBEX Type header was not included.<BR>
+ * <P>
+ * <STRONG>How Headers are Handled</STRONG>
+ * <P>
+ * As headers are received, they may be retrieved through the
+ * <code>getReceivedHeaders()</code> method.  If new headers are set during the
+ * operation, the new headers will be sent during the next packet exchange.
+ * <P>
+ * <STRONG>PUT example</STRONG>
+ * <P>
+ * <PRE>
+ * void putObjectViaOBEX(ClientSession conn, HeaderSet head, byte[] obj)
+ *     throws IOException {
+ *
+ *     // Include the length header
+ *     head.setHeader(head.LENGTH, new Long(obj.length));
+ *
+ *     // Initiate the PUT request
+ *     Operation op = conn.put(head);
+ *
+ *     // Open the output stream to put the object to it
+ *     DataOutputStream out = op.openDataOutputStream();
+ *
+ *     // Send the object to the server
+ *     out.write(obj);
+ *
+ *     // End the transaction
+ *     out.close();
+ *     op.close();
+ * }
+ * </PRE>
+ * <P>
+ * <STRONG>GET example</STRONG>
+ * <P>
+ * <PRE>
+ * byte[] getObjectViaOBEX(ClientSession conn, HeaderSet head) throws IOException {
+ *
+ *     // Send the initial GET request to the server
+ *     Operation op = conn.get(head);
+ *
+ *     // Retrieve the length of the object being sent back
+ *     int length = op.getLength();
+ *
+ *      // Create space for the object
+ *      byte[] obj = new byte[length];
+ *
+ *     // Get the object from the input stream
+ *     DataInputStream in = trans.openDataInputStream();
+ *     in.read(obj);
+ *
+ *     // End the transaction
+ *     in.close();
+ *     op.close();
+ *
+ *     return obj;
+ * }
+ * </PRE>
+ * <H3>Client PUT Operation Flow</H3>
+ * For PUT operations, a call to <code>close()</code> the <code>OutputStream</code>
+ * returned from <code>openOutputStream()</code> or <code>openDataOutputStream()</code>
+ * will signal that the request is done.  (In OBEX terms, the End-Of-Body header should
+ * be sent and the final bit in the request will be set.)  At this point, the
+ * reply from the server may begin to be processed.  A call to
+ * <code>getResponseCode()</code> will do an implicit close on the
+ * <code>OutputStream</code> and therefore signal that the request is done.
+ * <H3>Client GET Operation Flow</H3>
+ * For GET operation, a call to <code>openInputStream()</code> or
+ * <code>openDataInputStream()</code> will signal that the request is done.  (In OBEX
+ * terms, the final bit in the request will be set.)  A call to
+ * <code>getResponseCode()</code> will cause an implicit close on the
+ * <code>InputStream</code>.  No further data may be read at this point.
+ *
+ * @hide
+ */
+public interface Operation {
+
+    /**
+     * Sends an ABORT message to the server.  By calling this method, the
+     * corresponding input and output streams will be closed along with this
+     * object.  No headers are sent in the abort request.  This will end the
+     * operation since <code>close()</code> will be called by this method.
+     *
+     * @throws IOException if the transaction has already ended or if an
+     * OBEX server calls this method
+     */
+    public void abort() throws IOException;
+
+    /**
+     * Returns the headers that have been received during the operation.
+     * Modifying the object returned has no effect on the headers that are
+     * sent or retrieved.
+     *
+     * @return the headers received during this <code>Operation</code>
+     *
+     * @throws IOException if this <code>Operation</code> has been closed
+     */
+    public HeaderSet getReceivedHeaders() throws IOException;
+
+    /**
+     * Specifies the headers that should be sent in the next OBEX message that
+     * is sent.
+     *
+     * @param headers the headers to send in the next message
+     *
+     * @throws IOException  if this <code>Operation</code> has been closed
+     * or the transaction has ended and no further messages will be exchanged
+     *
+     * @throws IllegalArgumentException if <code>headers</code> was not created
+     * by a call to <code>ServerRequestHandler.createHeaderSet()</code> or
+     * <code>ClientSession.createHeaderSet()</code>
+     *
+     * @throws NullPointerException if <code>headers</code> if <code>null</code>
+     */
+    public void sendHeaders(HeaderSet headers) throws IOException;
+
+    /**
+     * Returns the response code received from the server.  Response codes
+     * are defined in the <code>ResponseCodes</code> class.
+     *
+     * @see ResponseCodes
+     *
+     * @return the response code retrieved from the server
+     *
+     * @throws IOException if an error occurred in the transport layer during
+     * the transaction; if this object was created by an OBEX server
+     */
+    public int getResponseCode() throws IOException;
+
+    public String getEncoding();
+
+    public long getLength();
+
+    public String getType();
+
+    public InputStream openInputStream() throws IOException;
+
+    public DataInputStream openDataInputStream() throws IOException;
+
+    public OutputStream openOutputStream() throws IOException;
+
+    public DataOutputStream openDataOutputStream() throws IOException;
+
+    public void close() throws IOException;
+
+    public int getMaxPacketSize();
+}
diff --git a/obex/javax/obex/PasswordAuthentication.java b/obex/javax/obex/PasswordAuthentication.java
new file mode 100644
index 0000000..49833fe
--- /dev/null
+++ b/obex/javax/obex/PasswordAuthentication.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * This class holds user name and password combinations.
+ *
+ * @hide
+ */
+public class PasswordAuthentication {
+
+    private byte[] userName;
+
+    private byte[] password;
+
+    /**
+     * Creates a new <code>PasswordAuthentication</code> with the user name
+     * and password provided.
+     *
+     * @param userName the user name to include; this may be <code>null</code>
+     *
+     * @param password the password to include in the response
+     *
+     * @exception NullPointerException if <code>password</code> is
+     * <code>null</code>
+     */
+    public PasswordAuthentication(byte[] userName, byte[] password) {
+        if (userName != null) {
+            this.userName = new byte[userName.length];
+            System.arraycopy(userName, 0, this.userName, 0, userName.length);
+        }
+
+        this.password = new byte[password.length];
+        System.arraycopy(password, 0, this.password, 0, password.length);
+    }
+
+    /**
+     * Retrieves the user name that was specified in the constructor.
+     * The user name may be <code>null</code>.
+     *
+     * @return the user name
+     */
+    public byte[] getUserName() {
+        return this.userName;
+    }
+
+    /**
+     * Retrieves the password.
+     *
+     * @return the password
+     */
+    public byte[] getPassword() {
+        return this.password;
+    }
+}
diff --git a/obex/javax/obex/PrivateInputStream.java b/obex/javax/obex/PrivateInputStream.java
new file mode 100644
index 0000000..9602649
--- /dev/null
+++ b/obex/javax/obex/PrivateInputStream.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * This object provides an input stream to the Operation objects used in this
+ * package.
+ *
+ * TODO: Include the other read() methods defined in InputStream.
+ *
+ * @hide
+ */
+public class PrivateInputStream extends InputStream {
+
+    private BaseStream parent;
+
+    private byte[] data;
+
+    private int index;
+
+    private boolean isOpen;
+
+    public PrivateInputStream() {
+
+    }
+
+    /**
+     * Creates an input stream for the <code>Operation</code> to read from
+     *
+     * @param p the connection this input stream is for
+     */
+    public PrivateInputStream(BaseStream p) {
+        parent = p;
+        data = new byte[0];
+        index = 0;
+        isOpen = true;
+    }
+
+    /**
+     * Returns the number of bytes that can be read (or skipped over) from this
+     * input stream without blocking by the next caller of a method for this
+     * input stream. The next caller might be the same thread or or another
+     * thread.
+     *
+     * @return the number of bytes that can be read from this input stream
+     * without blocking
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public synchronized int available() throws IOException {
+        ensureOpen();
+        return data.length - index;
+    }
+
+    /**
+     * Reads the next byte of data from the input stream. The value byte is
+     * returned as an int in the range 0 to 255. If no byte is available
+     * because the end of the stream has been reached, the value -1 is
+     * returned. This method blocks until input data is available, the end of
+     * the stream is detected, or an exception is thrown.
+     *
+     * @return the byte read from the input stream or -1 if it reaches the end
+     * of stream
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public synchronized int read() throws IOException {
+        ensureOpen();
+        while (data.length == index) {
+            if (!parent.continueOperation(true, true)) {
+                return -1;
+            }
+        }
+        return (data[index++] & 0xFF);
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    @Override
+    public synchronized int read(byte[] b, int offset, int length) throws IOException {
+
+        if (b == null) {
+            throw new NullPointerException("buffer is null");
+        }
+        if ((offset | length) < 0 || length > b.length - offset) {
+            throw new ArrayIndexOutOfBoundsException("index outof bound");
+        }
+        ensureOpen();
+
+        int currentDataLength = data.length - index;
+        int remainReadLength = length;
+        int offset1 = offset;
+        int result = 0;
+
+        while (currentDataLength <= remainReadLength) {
+            System.arraycopy(data, index, b, offset1, currentDataLength);
+            index += currentDataLength;
+            offset1 += currentDataLength;
+            result += currentDataLength;
+            remainReadLength -= currentDataLength;
+
+            if (!parent.continueOperation(true, true)) {
+                return result == 0 ? -1 : result;
+            }
+            currentDataLength = data.length - index;
+        }
+        if (remainReadLength > 0) {
+            System.arraycopy(data, index, b, offset1, remainReadLength);
+            index += remainReadLength;
+            result += remainReadLength;
+        }
+        return result;
+    }
+
+    /**
+     * Allows the <code>OperationImpl</code> thread to add body data to the
+     * input stream.
+     *
+     * @param body the data to add to the stream
+     *
+     * @param start the start of the body to array to copy
+     */
+    public synchronized void writeBytes(byte[] body, int start) {
+
+        int length = (body.length - start) + (data.length - index);
+        byte[] temp = new byte[length];
+
+        System.arraycopy(data, index, temp, 0, data.length - index);
+        System.arraycopy(body, start, temp, data.length - index, body.length - start);
+
+        data = temp;
+        index = 0;
+        notifyAll();
+    }
+
+    /**
+     * Verifies that this stream is open
+     *
+     * @throws IOException if the stream is not open
+     */
+    private void ensureOpen() throws IOException {
+        parent.ensureOpen();
+        if (!isOpen) {
+            throw new IOException("Input stream is closed");
+        }
+    }
+
+    /**
+     * Closes the input stream.  If the input stream is already closed, do
+     * nothing.
+     *
+     * @throws IOException this will never happen
+     */
+    @Override
+    public void close() throws IOException {
+        isOpen = false;
+        parent.streamClosed(true);
+    }
+}
diff --git a/obex/javax/obex/PrivateOutputStream.java b/obex/javax/obex/PrivateOutputStream.java
new file mode 100644
index 0000000..75220d6
--- /dev/null
+++ b/obex/javax/obex/PrivateOutputStream.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * This object provides an output stream to the Operation objects used in this
+ * package.
+ *
+ * @hide
+ */
+class PrivateOutputStream extends OutputStream {
+
+    private BaseStream parent;
+
+    private ByteArrayOutputStream output;
+
+    private boolean isClosed;
+
+    private int maxPacketSize;
+
+    /**
+     * Creates an empty <code>PrivateOutputStream</code> to write to.
+     *
+     * @param p the connection that this stream runs over
+     */
+    public PrivateOutputStream(BaseStream p, int maxSize) {
+        parent = p;
+        output = new ByteArrayOutputStream();
+        maxPacketSize = maxSize;
+    }
+
+    /**
+     * Determines how many bytes have been written to the output stream.
+     *
+     * @return the number of bytes written to the output stream
+     */
+    protected int size() {
+        return output.size();
+    }
+
+    /**
+     * Writes the specified byte to this output stream. The general contract
+     * for write is that one byte is written to the output stream. The byte to
+     * be written is the eight low-order bits of the argument b. The 24
+     * high-order bits of b are ignored.
+     *
+     * @param b the byte to write
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public synchronized void write(int b) throws IOException {
+        ensureOpen();
+        parent.ensureNotDone();
+        output.write(b);
+        if (output.size() == maxPacketSize) {
+            parent.continueOperation(true, false);
+        }
+    }
+
+    @Override
+    public void write(byte[] buffer) throws IOException {
+        write(buffer, 0, buffer.length);
+    }
+
+    @Override
+    public synchronized void write(byte[] buffer, int offset, int count) throws IOException {
+        int offset1 = offset;
+        int remainLength = count;
+
+        if (buffer == null) {
+            throw new NullPointerException("buffer is null");
+        }
+        if ((offset | count) < 0 || count > buffer.length - offset) {
+            throw new IndexOutOfBoundsException("index outof bound");
+        }
+
+        ensureOpen();
+        parent.ensureNotDone();
+        if (count < maxPacketSize) {
+            output.write(buffer, offset, count);
+        } else {
+            while (remainLength >= maxPacketSize) {
+                output.write(buffer, offset1, maxPacketSize);
+                offset1 += maxPacketSize;
+                remainLength = count - offset1;
+                parent.continueOperation(true, false);
+            }
+            if (remainLength > 0) {
+                output.write(buffer, offset1, remainLength);
+            }
+        }
+    }
+
+    /**
+     * Reads the bytes that have been written to this stream.
+     *
+     * @return the byte array that is written
+     */
+    protected synchronized byte[] readBytes() {
+        if (output.size() > 0) {
+            byte[] result = output.toByteArray();
+            output.reset();
+            return result;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Reads the bytes that have been written to this stream.
+     *
+     * @param size the size of the array to return
+     *
+     * @return the byte array that is written
+     */
+    protected synchronized byte[] readBytes(int size) {
+        if (output.size() > 0) {
+            byte[] temp = output.toByteArray();
+            output.reset();
+            byte[] result = new byte[size];
+            System.arraycopy(temp, 0, result, 0, size);
+            if (temp.length != size) {
+                output.write(temp, size, temp.length - size);
+            }
+            return result;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Verifies that this stream is open
+     *
+     * @throws IOException if the stream is not open
+     */
+    private void ensureOpen() throws IOException {
+        parent.ensureOpen();
+        if (isClosed) {
+            throw new IOException("Output stream is closed");
+        }
+    }
+
+    /**
+     * Closes the output stream.  If the input stream is already closed, do
+     * nothing.
+     *
+     * @throws IOException this will never happen
+     */
+    @Override
+    public void close() throws IOException {
+        isClosed = true;
+        parent.streamClosed(false);
+    }
+
+    /**
+     * Determines if the connection is closed
+     *
+     * @return <code>true</code> if the connection is closed;
+     * <code>false</code> if the connection is open
+     */
+    protected boolean isClosed() {
+        return isClosed;
+    }
+}
diff --git a/obex/javax/obex/ResponseCodes.java b/obex/javax/obex/ResponseCodes.java
new file mode 100644
index 0000000..2d7627b
--- /dev/null
+++ b/obex/javax/obex/ResponseCodes.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * The <code>ResponseCodes</code> class contains the list of valid
+ * response codes a server may send to a client.
+ * <P>
+ * <STRONG>IMPORTANT NOTE</STRONG>
+ * <P>
+ * It is important to note that these values are different then those defined
+ * in <code>javax.microedition.io.HttpConnection</code>.  The values in this
+ * interface represent the values defined in the IrOBEX specification.  The
+ * values in <code>javax.microedition.io.HttpConnection</code> represent values
+ * defined in the HTTP specification.
+ * <P>
+ * <code>OBEX_DATABASE_FULL</code> and <code>OBEX_DATABASE_LOCKED</code> require
+ * further description since they are not defined in HTTP.  The server will send
+ * an <code>OBEX_DATABASE_FULL</code> message when the client requests that
+ * something be placed into a database but the database is full (cannot take
+ * more data).   <code>OBEX_DATABASE_LOCKED</code> will be returned when the
+ * client wishes to access a database, database table, or database record that
+ * has been locked.
+ *
+ * @hide
+ */
+public class ResponseCodes {
+
+    /**
+     * Defines the OBEX SUCCESS response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_OK</code> is 0xA0 (160).
+     */
+    public static final int OBEX_HTTP_OK = 0xA0;
+
+    /**
+     * Defines the OBEX CREATED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_CREATED</code> is 0xA1 (161).
+     */
+    public static final int OBEX_HTTP_CREATED = 0xA1;
+
+    /**
+     * Defines the OBEX ACCEPTED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_ACCEPTED</code> is 0xA2 (162).
+     */
+    public static final int OBEX_HTTP_ACCEPTED = 0xA2;
+
+    /**
+     * Defines the OBEX NON-AUTHORITATIVE INFORMATION response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_NOT_AUTHORITATIVE</code> is 0xA3 (163).
+     */
+    public static final int OBEX_HTTP_NOT_AUTHORITATIVE = 0xA3;
+
+    /**
+     * Defines the OBEX NO CONTENT response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_NO_CONTENT</code> is 0xA4 (164).
+     */
+    public static final int OBEX_HTTP_NO_CONTENT = 0xA4;
+
+    /**
+     * Defines the OBEX RESET CONTENT response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_RESET</code> is 0xA5 (165).
+     */
+    public static final int OBEX_HTTP_RESET = 0xA5;
+
+    /**
+     * Defines the OBEX PARTIAL CONTENT response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_PARTIAL</code> is 0xA6 (166).
+     */
+    public static final int OBEX_HTTP_PARTIAL = 0xA6;
+
+    /**
+     * Defines the OBEX MULTIPLE_CHOICES response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_MULT_CHOICE</code> is 0xB0 (176).
+     */
+    public static final int OBEX_HTTP_MULT_CHOICE = 0xB0;
+
+    /**
+     * Defines the OBEX MOVED PERMANENTLY response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_MOVED_PERM</code> is 0xB1 (177).
+     */
+    public static final int OBEX_HTTP_MOVED_PERM = 0xB1;
+
+    /**
+     * Defines the OBEX MOVED TEMPORARILY response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_MOVED_TEMP</code> is 0xB2 (178).
+     */
+    public static final int OBEX_HTTP_MOVED_TEMP = 0xB2;
+
+    /**
+     * Defines the OBEX SEE OTHER response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_SEE_OTHER</code> is 0xB3 (179).
+     */
+    public static final int OBEX_HTTP_SEE_OTHER = 0xB3;
+
+    /**
+     * Defines the OBEX NOT MODIFIED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_NOT_MODIFIED</code> is 0xB4 (180).
+     */
+    public static final int OBEX_HTTP_NOT_MODIFIED = 0xB4;
+
+    /**
+     * Defines the OBEX USE PROXY response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_USE_PROXY</code> is 0xB5 (181).
+     */
+    public static final int OBEX_HTTP_USE_PROXY = 0xB5;
+
+    /**
+     * Defines the OBEX BAD REQUEST response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_BAD_REQUEST</code> is 0xC0 (192).
+     */
+    public static final int OBEX_HTTP_BAD_REQUEST = 0xC0;
+
+    /**
+     * Defines the OBEX UNAUTHORIZED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_UNAUTHORIZED</code> is 0xC1 (193).
+     */
+    public static final int OBEX_HTTP_UNAUTHORIZED = 0xC1;
+
+    /**
+     * Defines the OBEX PAYMENT REQUIRED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_PAYMENT_REQUIRED</code> is 0xC2 (194).
+     */
+    public static final int OBEX_HTTP_PAYMENT_REQUIRED = 0xC2;
+
+    /**
+     * Defines the OBEX FORBIDDEN response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_FORBIDDEN</code> is 0xC3 (195).
+     */
+    public static final int OBEX_HTTP_FORBIDDEN = 0xC3;
+
+    /**
+     * Defines the OBEX NOT FOUND response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_NOT_FOUND</code> is 0xC4 (196).
+     */
+    public static final int OBEX_HTTP_NOT_FOUND = 0xC4;
+
+    /**
+     * Defines the OBEX METHOD NOT ALLOWED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_BAD_METHOD</code> is 0xC5 (197).
+     */
+    public static final int OBEX_HTTP_BAD_METHOD = 0xC5;
+
+    /**
+     * Defines the OBEX NOT ACCEPTABLE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_NOT_ACCEPTABLE</code> is 0xC6 (198).
+     */
+    public static final int OBEX_HTTP_NOT_ACCEPTABLE = 0xC6;
+
+    /**
+     * Defines the OBEX PROXY AUTHENTICATION REQUIRED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_PROXY_AUTH</code> is 0xC7 (199).
+     */
+    public static final int OBEX_HTTP_PROXY_AUTH = 0xC7;
+
+    /**
+     * Defines the OBEX REQUEST TIME OUT response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_TIMEOUT</code> is 0xC8 (200).
+     */
+    public static final int OBEX_HTTP_TIMEOUT = 0xC8;
+
+    /**
+     * Defines the OBEX METHOD CONFLICT response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_CONFLICT</code> is 0xC9 (201).
+     */
+    public static final int OBEX_HTTP_CONFLICT = 0xC9;
+
+    /**
+     * Defines the OBEX METHOD GONE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_GONE</code> is 0xCA (202).
+     */
+    public static final int OBEX_HTTP_GONE = 0xCA;
+
+    /**
+     * Defines the OBEX METHOD LENGTH REQUIRED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_LENGTH_REQUIRED</code> is 0xCB (203).
+     */
+    public static final int OBEX_HTTP_LENGTH_REQUIRED = 0xCB;
+
+    /**
+     * Defines the OBEX PRECONDITION FAILED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_PRECON_FAILED</code> is 0xCC (204).
+     */
+    public static final int OBEX_HTTP_PRECON_FAILED = 0xCC;
+
+    /**
+     * Defines the OBEX REQUESTED ENTITY TOO LARGE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_ENTITY_TOO_LARGE</code> is 0xCD (205).
+     */
+    public static final int OBEX_HTTP_ENTITY_TOO_LARGE = 0xCD;
+
+    /**
+     * Defines the OBEX REQUESTED URL TOO LARGE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_REQ_TOO_LARGE</code> is 0xCE (206).
+     */
+    public static final int OBEX_HTTP_REQ_TOO_LARGE = 0xCE;
+
+    /**
+     * Defines the OBEX UNSUPPORTED MEDIA TYPE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_UNSUPPORTED_TYPE</code> is 0xCF (207).
+     */
+    public static final int OBEX_HTTP_UNSUPPORTED_TYPE = 0xCF;
+
+    /**
+     * Defines the OBEX INTERNAL SERVER ERROR response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_INTERNAL_ERROR</code> is 0xD0 (208).
+     */
+    public static final int OBEX_HTTP_INTERNAL_ERROR = 0xD0;
+
+    /**
+     * Defines the OBEX NOT IMPLEMENTED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_NOT_IMPLEMENTED</code> is 0xD1 (209).
+     */
+    public static final int OBEX_HTTP_NOT_IMPLEMENTED = 0xD1;
+
+    /**
+     * Defines the OBEX BAD GATEWAY response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_BAD_GATEWAY</code> is 0xD2 (210).
+     */
+    public static final int OBEX_HTTP_BAD_GATEWAY = 0xD2;
+
+    /**
+     * Defines the OBEX SERVICE UNAVAILABLE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_UNAVAILABLE</code> is 0xD3 (211).
+     */
+    public static final int OBEX_HTTP_UNAVAILABLE = 0xD3;
+
+    /**
+     * Defines the OBEX GATEWAY TIMEOUT response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_GATEWAY_TIMEOUT</code> is 0xD4 (212).
+     */
+    public static final int OBEX_HTTP_GATEWAY_TIMEOUT = 0xD4;
+
+    /**
+     * Defines the OBEX HTTP VERSION NOT SUPPORTED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_VERSION</code> is 0xD5 (213).
+     */
+    public static final int OBEX_HTTP_VERSION = 0xD5;
+
+    /**
+     * Defines the OBEX DATABASE FULL response code.
+     * <P>
+     * The value of <code>OBEX_DATABASE_FULL</code> is 0xE0 (224).
+     */
+    public static final int OBEX_DATABASE_FULL = 0xE0;
+
+    /**
+     * Defines the OBEX DATABASE LOCKED response code.
+     * <P>
+     * The value of <code>OBEX_DATABASE_LOCKED</code> is 0xE1 (225).
+     */
+    public static final int OBEX_DATABASE_LOCKED = 0xE1;
+
+    /**
+     * Constructor does nothing.
+     */
+    private ResponseCodes() {}
+}
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
new file mode 100644
index 0000000..5dcbb93
--- /dev/null
+++ b/obex/javax/obex/ServerOperation.java
@@ -0,0 +1,771 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.DataInputStream;
+import java.io.OutputStream;
+import java.io.DataOutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * This class implements the Operation interface for server side connections.
+ * <P>
+ * <STRONG>Request Codes</STRONG>
+ * There are four different request codes that are in this class.  0x02 is a
+ * PUT request that signals that the request is not complete and requires an
+ * additional OBEX packet.  0x82 is a PUT request that says that request is
+ * complete.  In this case, the server can begin sending the response.  The
+ * 0x03 is a GET request that signals that the request is not finished.  When
+ * the server receives a 0x83, the client is signalling the server that it is
+ * done with its request.
+ *
+ * TODO: Extend the ClientOperation and reuse the methods defined
+ * TODO: in that class.
+ *
+ * @hide
+ */
+public class ServerOperation implements Operation, BaseStream {
+
+    private InputStream socketInput;
+
+    private ServerSession parent;
+
+    private int maxPacketLength;
+
+    private int responseSize;
+
+    private boolean isClosed;
+
+    boolean finalBitSet;
+
+    // This variable defines when the end of body
+    // header has been received.  When this header
+    // is received, no further body data will be
+    // received from the client
+    private boolean endOfBody;
+
+    private boolean isGet;
+
+    boolean isAborted;
+
+    HeaderSet requestHeaders;
+
+    HeaderSet replyHeaders;
+
+    PrivateInputStream privateInput;
+
+    private PrivateOutputStream privateOutput;
+
+    private String exceptionString;
+
+    private ServerRequestHandler listener;
+
+    private boolean outputStreamOpened;
+
+    private boolean requestFinished;
+
+    private static final int BASE_PACKET_LENGTH = 3;
+
+    private boolean isHasBody;
+
+    /**
+     * Creates new PutServerOperation
+     *
+     * @param p the parent that created this object
+     *
+     * @param in the input stream to read from
+     *
+     * @param out the output stream to write to
+     *
+     * @param request the initial request that was received from the client
+     *
+     * @param maxSize the max packet size that the client will accept
+     *
+     * @param listen the listener that is responding to the request
+     *
+     * @throws IOException if an IO error occurs
+     */
+    public ServerOperation(ServerSession p, InputStream in, int request, int maxSize,
+            ServerRequestHandler listen) throws IOException {
+
+        isAborted = false;
+        parent = p;
+        socketInput = in;
+        maxPacketLength = maxSize;
+        isClosed = false;
+        requestHeaders = new HeaderSet();
+        replyHeaders = new HeaderSet();
+        privateInput = new PrivateInputStream(this);
+        endOfBody = false;
+        responseSize = 3;
+        listener = listen;
+        requestFinished = false;
+        outputStreamOpened = false;
+        isHasBody = false;
+        int bytesReceived;
+
+        /*
+         * Determine if this is a PUT request
+         */
+        if ((request == 0x02) || (request == 0x82)) {
+            /*
+             * It is a PUT request.
+             */
+            isGet = false;
+        } else {
+            /*
+             * It is a GET request.
+             */
+            isGet = true;
+        }
+
+        /*
+         * Determine if the final bit is set
+         */
+        if ((request & 0x80) == 0) {
+            finalBitSet = false;
+        } else {
+            finalBitSet = true;
+            requestFinished = true;
+        }
+
+        int length = in.read();
+        length = (length << 8) + in.read();
+
+        /*
+         * Determine if the packet length is larger than this device can receive
+         */
+        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+            parent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
+            throw new IOException("Packet received was too large");
+        }
+
+        /*
+         * Determine if any headers were sent in the initial request
+         */
+        if (length > 3) {
+            byte[] data = new byte[length - 3];
+            bytesReceived = in.read(data);
+
+            while (bytesReceived != data.length) {
+                bytesReceived += in.read(data, bytesReceived, data.length - bytesReceived);
+            }
+
+            byte[] body = ObexHelper.updateHeaderSet(requestHeaders, data);
+
+            if (body != null) {
+                isHasBody = true;
+            }
+
+            if (requestHeaders.connectionID != null) {
+                listener.setConnectionID(ObexHelper.convertToLong(requestHeaders.connectionID));
+            } else {
+                listener.setConnectionID(0);
+            }
+
+            if (requestHeaders.authResp != null) {
+                if (!parent.handleAuthResp(requestHeaders.authResp)) {
+                    exceptionString = "Authentication Failed";
+                    parent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
+                    isClosed = true;
+                    requestHeaders.authResp = null;
+                    return;
+                }
+            }
+
+            if (requestHeaders.authChall != null) {
+                parent.handleAuthChall(requestHeaders);
+                // send the  authResp to the client
+                replyHeaders.authResp = new byte[requestHeaders.authResp.length];
+                System.arraycopy(requestHeaders.authResp, 0, replyHeaders.authResp, 0,
+                        replyHeaders.authResp.length);
+                requestHeaders.authResp = null;
+                requestHeaders.authChall = null;
+
+            }
+
+            if (body != null) {
+                /*
+                 * 0x49 is the end of body header.  This signifies that no more
+                 * body data will be sent from the client
+                 */
+                if (body[0] == 0x49) {
+                    endOfBody = true;
+                }
+                //privateInput.writeBytes(body, body.length);
+                //byte [] body_tmp = new byte[body.length-1];
+                //System.arraycopy(body,1,body_tmp,0,body.length-1);
+                //privateInput.writeBytes(body_tmp, body.length-1);
+                privateInput.writeBytes(body, 1);
+            } else {
+                while ((!isGet) && (!finalBitSet)) {
+                    sendReply(ObexHelper.OBEX_HTTP_CONTINUE);
+                    if (privateInput.available() > 0) {
+                        break;
+                    }
+                }
+            }//  if (body != null) 
+
+        }// if (length > 3)
+
+        while ((!isGet) && (!finalBitSet) && (privateInput.available() == 0)) {
+            sendReply(ObexHelper.OBEX_HTTP_CONTINUE);
+            if (privateInput.available() > 0) {
+                break;
+            }
+        }
+
+        // wait for get request finished !!!!
+        while (isGet && !finalBitSet) {
+            sendReply(ObexHelper.OBEX_HTTP_CONTINUE);
+        }
+        if (finalBitSet && isGet) {
+            requestFinished = true;
+        }
+    }
+
+    public synchronized boolean isValidBody() {
+        return isHasBody;
+    }
+
+    /**
+     * Determines if the operation should continue or should wait.  If it
+     * should continue, this method will continue the operation.
+     *
+     * @param sendEmpty if <code>true</code> then this will continue the
+     * operation even if no headers will be sent; if <code>false</code> then
+     * this method will only continue the operation if there are headers to
+     * send
+     * @param isStream  if<code>true</code> the stream is input stream or
+     * is outputstream
+     * @return <code>true</code> if the operation was completed;
+     * <code>false</code> if no operation took place
+     */
+    public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
+            throws IOException {
+        if (!isGet) {
+            if (!finalBitSet) {
+                if (sendEmpty) {
+                    sendReply(ObexHelper.OBEX_HTTP_CONTINUE);
+                    return true;
+                } else {
+                    if ((responseSize > 3) || (privateOutput.size() > 0)) {
+                        sendReply(ObexHelper.OBEX_HTTP_CONTINUE);
+                        return true;
+                    } else {
+                        return false;
+                    }
+                }
+            } else {
+                return false;
+            }
+        } else {
+            sendReply(ObexHelper.OBEX_HTTP_CONTINUE);
+            return true;
+        }
+    }
+
+    /**
+     * Sends a reply to the client.  If the reply is a OBEX_HTTP_CONTINUE, it
+     * will wait for a response from the client before ending.
+     *
+     * @param type the response code to send back to the client
+     *
+     * @return <code>true</code> if the final bit was not set on the reply;
+     * <code>false</code> if no reply was received because the operation ended,
+     * an abort was received, or the final bit was set in the reply
+     *
+     * @throws IOException if an IO error occurs
+     */
+    protected synchronized boolean sendReply(int type) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int bytesReceived;
+
+        long id = listener.getConnectionID();
+        if (id == -1) {
+            replyHeaders.connectionID = null;
+        } else {
+            replyHeaders.connectionID = ObexHelper.convertToByteArray(id);
+        }
+
+        byte[] headerArray = ObexHelper.createHeader(replyHeaders, true);
+        int bodyLength = -1;
+        int orginalBodyLength = -1;
+
+        if (privateOutput != null) {
+            bodyLength = privateOutput.size();
+            orginalBodyLength = bodyLength;
+        }
+
+        if ((BASE_PACKET_LENGTH + headerArray.length) > maxPacketLength) {
+
+            int end = 0;
+            int start = 0;
+
+            while (end != headerArray.length) {
+                end = ObexHelper.findHeaderEnd(headerArray, start, maxPacketLength
+                        - BASE_PACKET_LENGTH);
+                if (end == -1) {
+
+                    isClosed = true;
+
+                    if (privateInput != null) {
+                        privateInput.close();
+                    }
+
+                    if (privateOutput != null) {
+                        privateOutput.close();
+                    }
+                    parent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+                    throw new IOException("OBEX Packet exceeds max packet size");
+                }
+                byte[] sendHeader = new byte[end - start];
+                System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
+
+                parent.sendResponse(type, sendHeader);
+                start = end;
+            }
+
+            if (bodyLength > 0) {
+                return true;
+            } else {
+                return false;
+            }
+
+        } else {
+            out.write(headerArray);
+        }
+
+        /*
+         * Determine if there is space to add a body reply.  First, we need to
+         * verify that the client is finished sending the request.  Next, there
+         * needs to be enough space to send the headers already defined along
+         * with the reply header (3 bytes) and the body header identifier
+         * (3 bytes).
+         */
+
+        /*        if ((finalBitSet) &&
+                    ((bodyLength + 6 + headerArray.length) > maxPacketLength)) {
+
+                    exceptionString = "Header larger then can be sent in a packet";
+                    isClosed = true;
+                    privateInput.close();
+                    if (privateOutput != null) {
+                        privateOutput.close();
+                    }
+                    parent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR,
+                        null);
+                    throw new IOException("OBEX Packet exceeds max packet size");
+                }
+        */
+         
+        if ((finalBitSet) || (headerArray.length < (maxPacketLength - 20))) {
+            if (bodyLength > 0) {
+                /*
+                 * Determine if I can send the whole body or just part of
+                 * the body.  Remember that there is the 3 bytes for the
+                 * response message and 3 bytes for the header ID and length
+                 */
+                if (bodyLength > (maxPacketLength - headerArray.length - 6)) {
+                    bodyLength = maxPacketLength - headerArray.length - 6;
+                }
+
+                byte[] body = privateOutput.readBytes(bodyLength);
+
+                /*
+                 * Since this is a put request if the final bit is set or
+                 * the output stream is closed we need to send the 0x49
+                 * (End of Body) otherwise, we need to send 0x48 (Body)
+                 */
+                if ((finalBitSet) || (privateOutput.isClosed())) {
+                    out.write(0x49);
+                } else {
+                    out.write(0x48);
+                }
+
+                bodyLength += 3;
+                out.write((byte)(bodyLength >> 8));
+                out.write((byte)bodyLength);
+                out.write(body);
+            }
+        }
+
+        if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) {
+            out.write(0x49);
+            orginalBodyLength = 3;
+            out.write((byte)(orginalBodyLength >> 8));
+            out.write((byte)orginalBodyLength);
+
+        }
+
+        responseSize = 3;
+        parent.sendResponse(type, out.toByteArray());
+
+        if (type == ObexHelper.OBEX_HTTP_CONTINUE) {
+            int headerID = socketInput.read();
+            int length = socketInput.read();
+            length = (length << 8) + socketInput.read();
+            if ((headerID != 0x02) && (headerID != 0x82) && (headerID != 0x03)
+                    && (headerID != 0x83)) {
+
+                if (length > 3) {
+                    byte[] temp = new byte[length];
+                    bytesReceived = socketInput.read(temp);
+
+                    while (bytesReceived != length) {
+                        bytesReceived += socketInput.read(temp, bytesReceived, length
+                                - bytesReceived);
+                    }
+                }
+
+                /*
+                 * Determine if an ABORT was sent as the reply
+                 */
+                if (headerID == 0xFF) {
+                    parent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
+                    isClosed = true;
+                    isAborted = true;
+                    exceptionString = "Abort Received";
+                    throw new IOException("Abort Received");
+                } else {
+                    parent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
+                    isClosed = true;
+                    exceptionString = "Bad Request Received";
+                    throw new IOException("Bad Request Received");
+                }
+            } else {
+
+                if ((headerID == 0x82) || (headerID == 0x83)) {
+                    finalBitSet = true;
+                }
+
+                /*
+                 * Determine if the packet length is larger then this device can receive
+                 */
+                if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+                    parent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
+                    throw new IOException("Packet received was too large");
+                }
+
+                /*
+                 * Determine if any headers were sent in the initial request
+                 */
+                if (length > 3) {
+                    byte[] data = new byte[length - 3];
+                    bytesReceived = socketInput.read(data);
+
+                    while (bytesReceived != data.length) {
+                        bytesReceived += socketInput.read(data, bytesReceived, data.length
+                                - bytesReceived);
+                    }
+                    byte[] body = ObexHelper.updateHeaderSet(requestHeaders, data);
+                    if (body != null) {
+                        isHasBody = true;
+                    }
+                    if (requestHeaders.connectionID != null) {
+                        listener.setConnectionID(ObexHelper
+                                .convertToLong(requestHeaders.connectionID));
+                    } else {
+                        listener.setConnectionID(1);
+                    }
+
+                    if (requestHeaders.authResp != null) {
+                        if (!parent.handleAuthResp(requestHeaders.authResp)) {
+                            exceptionString = "Authentication Failed";
+                            parent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
+                            isClosed = true;
+                            requestHeaders.authResp = null;
+                            return false;
+                        }
+                        requestHeaders.authResp = null;
+                    }
+
+                    if (requestHeaders.authChall != null) {
+                        parent.handleAuthChall(requestHeaders);
+                        // send the auhtResp to the client
+                        replyHeaders.authResp = new byte[requestHeaders.authResp.length];
+                        System.arraycopy(requestHeaders.authResp, 0, replyHeaders.authResp, 0,
+                                replyHeaders.authResp.length);
+                        requestHeaders.authResp = null;
+                        requestHeaders.authChall = null;
+                    }
+
+                    if (body != null) {
+                        if (body[0] == 0x49) {
+                            endOfBody = true;
+                        }
+
+                        /*byte [] body_tmp = new byte[body.length-1];
+                        System.arraycopy(body,1,body_tmp,0,body.length-1);
+                        privateInput.writeBytes(body_tmp, body.length-1);*/
+                        privateInput.writeBytes(body, 1);
+
+                    }
+                }
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Sends an ABORT message to the server.  By calling this method, the
+     * corresponding input and output streams will be closed along with this
+     * object.
+     *
+     * @throws IOException if the transaction has already ended or if an
+     * OBEX server called this method
+     */
+    public void abort() throws IOException {
+        throw new IOException("Called from a server");
+    }
+
+    /**
+     * Returns the headers that have been received during the operation.
+     * Modifying the object returned has no effect on the headers that are
+     * sent or retrieved.
+     *
+     * @return the headers received during this <code>Operation</code>
+     *
+     * @throws IOException if this <code>Operation</code> has been closed
+     */
+    public HeaderSet getReceivedHeaders() throws IOException {
+        ensureOpen();
+        return requestHeaders;
+    }
+
+    /**
+     * Specifies the headers that should be sent in the next OBEX message that
+     * is sent.
+     *
+     * @param headers the headers to send in the next message
+     *
+     * @throws IOException  if this <code>Operation</code> has been closed
+     * or the transaction has ended and no further messages will be exchanged
+     *
+     * @throws IllegalArgumentException if <code>headers</code> was not created
+     * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
+     */
+    public void sendHeaders(HeaderSet headers) throws IOException {
+        ensureOpen();
+
+        if (headers == null) {
+            throw new NullPointerException("Headers may not be null");
+        }
+
+        int[] headerList = headers.getHeaderList();
+        if (headerList != null) {
+            for (int i = 0; i < headerList.length; i++) {
+                replyHeaders.setHeader(headerList[i], headers.getHeader(headerList[i]));
+            }
+
+        }
+    }
+
+    /**
+     * Retrieves the response code retrieved from the server.  Response codes
+     * are defined in the <code>ResponseCodes</code> interface.
+     *
+     * @return the response code retrieved from the server
+     *
+     * @throws IOException if an error occurred in the transport layer during
+     * the transaction; if this method is called on a <code>HeaderSet</code>
+     * object created by calling <code>createHeaderSet</code> in a
+     * <code>ClientSession</code> object; if this is called from a server
+     */
+    public int getResponseCode() throws IOException {
+        throw new IOException("Called from a server");
+    }
+
+    /**
+     * Always returns <code>null</code>
+     *
+     * @return <code>null</code>
+     */
+    public String getEncoding() {
+        return null;
+    }
+
+    /**
+     * Returns the type of content that the resource connected to is providing.
+     * E.g. if the connection is via HTTP, then the value of the content-type
+     * header field is returned.
+     *
+     * @return the content type of the resource that the URL references, or
+     * <code>null</code> if not known
+     */
+    public String getType() {
+        try {
+            return (String)requestHeaders.getHeader(HeaderSet.TYPE);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the length of the content which is being provided. E.g. if the
+     * connection is via HTTP, then the value of the content-length header
+     * field is returned.
+     *
+     * @return the content length of the resource that this connection's URL
+     * references, or -1 if the content length is not known
+     */
+    public long getLength() {
+        try {
+            Long temp = (Long)requestHeaders.getHeader(HeaderSet.LENGTH);
+
+            if (temp == null) {
+                return -1;
+            } else {
+                return temp.longValue();
+            }
+        } catch (IOException e) {
+            return -1;
+        }
+    }
+
+    public int getMaxPacketSize() {
+        return maxPacketLength - 6;
+    }
+
+    /**
+     * Open and return an input stream for a connection.
+     *
+     * @return an input stream
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public InputStream openInputStream() throws IOException {
+        ensureOpen();
+        return privateInput;
+    }
+
+    /**
+     * Open and return a data input stream for a connection.
+     *
+     * @return an input stream
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public DataInputStream openDataInputStream() throws IOException {
+        return new DataInputStream(openInputStream());
+    }
+
+    /**
+     * Open and return an output stream for a connection.
+     *
+     * @return an output stream
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public OutputStream openOutputStream() throws IOException {
+        ensureOpen();
+
+        if (outputStreamOpened)
+            throw new IOException("no more input streams available, stream already opened");
+
+        if (!requestFinished)
+            throw new IOException("no  output streams available ,request not finished");
+
+        if (privateOutput == null) {
+            privateOutput = new PrivateOutputStream(this, maxPacketLength - 6);
+        }
+        outputStreamOpened = true;
+        return privateOutput;
+    }
+
+    /**
+     * Open and return a data output stream for a connection.
+     *
+     * @return an output stream
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public DataOutputStream openDataOutputStream() throws IOException {
+        return new DataOutputStream(openOutputStream());
+    }
+
+    /**
+     * Closes the connection and ends the transaction
+     *
+     * @throws IOException if the operation has already ended or is closed
+     */
+    public void close() throws IOException {
+        ensureOpen();
+        isClosed = true;
+    }
+
+    /**
+     * Verifies that the connection is open and no exceptions should be thrown.
+     *
+     * @throws IOException if an exception needs to be thrown
+     */
+    public void ensureOpen() throws IOException {
+        if (exceptionString != null) {
+            throw new IOException(exceptionString);
+        }
+        if (isClosed) {
+            throw new IOException("Operation has already ended");
+        }
+    }
+
+    /**
+     * Verifies that additional information may be sent.  In other words, the
+     * operation is not done.
+     * <P>
+     * Included to implement the BaseStream interface only.  It does not do
+     * anything on the server side since the operation of the Operation object
+     * is not done until after the handler returns from its method.
+     *
+     * @throws IOException if the operation is completed
+     */
+    public void ensureNotDone() throws IOException {
+    }
+
+    /**
+     * Called when the output or input stream is closed.  It does not do
+     * anything on the server side since the operation of the Operation object
+     * is not done until after the handler returns from its method.
+     *
+     * @param inStream <code>true</code> if the input stream is closed;
+     * <code>false</code> if the output stream is closed
+     *
+     * @throws IOException if an IO error occurs
+     */
+    public void streamClosed(boolean inStream) throws IOException {
+
+    }
+}
diff --git a/obex/javax/obex/ServerRequestHandler.java b/obex/javax/obex/ServerRequestHandler.java
new file mode 100644
index 0000000..955e916
--- /dev/null
+++ b/obex/javax/obex/ServerRequestHandler.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * The <code>ServerRequestHandler</code> class defines an event
+ * listener that will respond to OBEX requests made to the server.
+ * <P>
+ * The <code>onConnect()</code>, <code>onSetPath()</code>, <code>onDelete()</code>,
+ * <code>onGet()</code>,
+ * and <code>onPut()</code> methods may return any response code defined
+ * in the <code>ResponseCodes</code> class except for
+ * <code>OBEX_HTTP_CONTINUE</code>.  If <code>OBEX_HTTP_CONTINUE</code> or
+ * a value not defined in the <code>ResponseCodes</code> class is returned,
+ * the server implementation will send an <code>OBEX_HTTP_INTERNAL_ERROR</code>
+ * response to the client.
+ * <P>
+ * <STRONG>Connection ID and Target Headers</STRONG>
+ * <P>
+ * According to the IrOBEX specification, a packet may not contain a Connection
+ * ID and Target header.  Since the Connection ID header is managed by the
+ * implementation, it will not send a Connection ID header, if a Connection ID
+ * was specified, in a packet that has a Target header.  In other words, if an
+ * application adds a Target header to a <code>HeaderSet</code> object used
+ * in an OBEX operation and a Connection ID was specified, no Connection ID
+ * will be sent in the packet containing the Target header.
+ * <P>
+ * <STRONG>CREATE-EMPTY Requests</STRONG>
+ * <P>
+ * A CREATE-EMPTY request allows clients to create empty objects on the server.
+ * When a CREATE-EMPTY request is received, the <code>onPut()</code> method
+ * will be called by the implementation.  To differentiate between a normal
+ * PUT request and a CREATE-EMPTY request, an application must open the
+ * <code>InputStream</code> from the <code>Operation</code> object passed
+ * to the <code>onPut()</code> method.  For a PUT request, the application
+ * will be able to read Body data from this <code>InputStream</code>.  For
+ * a CREATE-EMPTY request, there will be no Body data to read.  Therefore,
+ * a call to <code>InputStream.read()</code> will return -1.
+ *
+ * @hide
+ */
+public class ServerRequestHandler {
+
+    private long connectionID;
+
+    /**
+      * Creates a <code>ServerRequestHandler</code>.
+      */
+    protected ServerRequestHandler() {
+        /*
+         * A connection ID of -1 implies there is no conenction ID
+         */
+        connectionID = -1;
+    }
+
+    /**
+     * Creates a <code>HeaderSet</code> object that may be used in put and get
+     * operations.
+     *
+     * @return the <code>HeaderSet</code> object to use in put and get operations
+     */
+    public final HeaderSet createHeaderSet() {
+        return new HeaderSet();
+    }
+
+    /**
+     * Sets the connection ID header to include in the reply packets.
+     *
+     * @param id the connection ID to use; -1 if no connection ID should be
+     * sent
+     *
+     * @throws IllegalArgumentException if <code>id</code> is not in the
+     * range -1 to 2<sup>32</sup>-1
+     */
+    public void setConnectionID(long id) {
+        if ((id < -1) || (id > 0xFFFFFFFFL)) {
+            throw new IllegalArgumentException("Illegal Connection ID");
+        }
+        connectionID = id;
+    }
+
+    /**
+     * Retrieves the connection ID that is being used in the present connection.
+     * This method will return -1 if no connection ID is being used.
+     *
+     * @return the connection id being used or -1 if no connection ID is being
+     * used
+     */
+    public long getConnectionID() {
+        return connectionID;
+    }
+
+    /**
+     * Called when a CONNECT request is received.
+     * <P>
+     * If this method is not implemented by the class that extends this
+     * class, <code>onConnect()</code> will always return an
+     * <code>OBEX_HTTP_OK</code> response code.
+     * <P>
+     * The headers received in the request can be retrieved from the
+     * <code>request</code> argument.  The headers that should be sent
+     * in the reply must be specified in the <code>reply</code> argument.
+     *
+     * @param request contains the headers sent by the client;
+     * <code>request</code> will never be <code>null</code>
+     *
+     * @param reply the headers that should be sent in the reply;
+     * <code>reply</code> will never be <code>null</code>
+     *
+     * @return a response code defined in <code>ResponseCodes</code> that will be
+     * returned to the client; if an invalid response code is provided, the
+     * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+     */
+    public int onConnect(HeaderSet request, HeaderSet reply) {
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    /**
+     * Called when a DISCONNECT request is received.
+     * <P>
+     * The headers received in the request can be retrieved from the
+     * <code>request</code> argument.  The headers that should be sent
+     * in the reply must be specified in the <code>reply</code> argument.
+     *
+     * @param request contains the headers sent by the client;
+     * <code>request</code> will never be <code>null</code>
+     *
+     * @param reply the headers that should be sent in the reply;
+     * <code>reply</code> will never be <code>null</code>
+     */
+    public void onDisconnect(HeaderSet request, HeaderSet reply) {
+    }
+
+    /**
+     * Called when a SETPATH request is received.
+     * <P>
+     * If this method is not implemented by the class that extends this
+     * class, <code>onSetPath()</code> will always return an
+     * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
+     * <P>
+     * The headers received in the request can be retrieved from the
+     * <code>request</code> argument.  The headers that should be sent
+     * in the reply must be specified in the <code>reply</code> argument.
+     *
+     * @param request contains the headers sent by the client;
+     * <code>request</code> will never be <code>null</code>
+     *
+     * @param reply the headers that should be sent in the reply;
+     * <code>reply</code> will never be <code>null</code>
+     *
+     * @param backup <code>true</code> if the client requests that the server
+     * back up one directory before changing to the path described by
+     * <code>name</code>; <code>false</code> to apply the request to the present
+     * path
+     *
+     * @param create <code>true</code> if the path should be created if it does
+     * not already exist; <code>false</code> if the path should not be created
+     * if it does not exist and an error code should be returned
+     *
+     * @return a response code defined in <code>ResponseCodes</code> that will be
+     * returned to the client; if an invalid response code is provided, the
+     * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+     */
+    public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) {
+
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    /**
+     * Called when a DELETE request is received.
+     * <P>
+     * If this method is not implemented by the class that extends this
+     * class, <code>onDelete()</code> will always return an
+     * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
+     * <P>
+     * The headers received in the request can be retrieved from the
+     * <code>request</code> argument.  The headers that should be sent
+     * in the reply must be specified in the <code>reply</code> argument.
+     *
+     * @param request contains the headers sent by the client;
+     * <code>request</code> will never be <code>null</code>
+     *
+     * @param reply the headers that should be sent in the reply;
+     * <code>reply</code> will never be <code>null</code>
+     *
+     * @return a response code defined in <code>ResponseCodes</code> that will be
+     * returned to the client; if an invalid response code is provided, the
+     * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+     */
+    public int onDelete(HeaderSet request, HeaderSet reply) {
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    /**
+     * Called when a PUT request is received.
+     * <P>
+     * If this method is not implemented by the class that extends this
+     * class, <code>onPut()</code> will always return an
+     * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
+     * <P>
+     * If an ABORT request is received during the processing of a PUT request,
+     * <code>op</code> will be closed by the implementation.
+     *
+     * @param op contains the headers sent by the client and allows new
+     * headers to be sent in the reply; <code>op</code> will never be
+     * <code>null</code>
+     *
+     * @return a response code defined in <code>ResponseCodes</code> that will be
+     * returned to the client; if an invalid response code is provided, the
+     * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+     */
+    public int onPut(Operation op) {
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    /**
+     * Called when a GET request is received.
+     * <P>
+     * If this method is not implemented by the class that extends this
+     * class, <code>onGet()</code> will always return an
+     * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
+     * <P>
+     * If an ABORT request is received during the processing of a GET request,
+     * <code>op</code> will be closed by the implementation.
+     *
+     * @param op contains the headers sent by the client and allows new
+     * headers to be sent in the reply; <code>op</code> will never be
+     * <code>null</code>
+     *
+     * @return a response code defined in <code>ResponseCodes</code> that will be
+     * returned to the client; if an invalid response code is provided, the
+     * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+     */
+    public int onGet(Operation op) {
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    /**
+     * Called when this object attempts to authenticate a client and the
+     * authentication request fails because the response digest in the
+     * authentication response header was wrong.
+     * <P>
+     * If this method is not implemented by the class that extends this class,
+     * this method will do nothing.
+     *
+     * @param userName the user name returned in the authentication response;
+     * <code>null</code> if no user name was provided in the response
+     */
+    public void onAuthenticationFailure(byte[] userName) {
+    }
+
+    /**Called by ServerSession to update the status of current transaction */
+    public void updateStatus(String message) {
+    }
+
+    public void onClose() {
+    }
+}
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
new file mode 100644
index 0000000..9daa6c0
--- /dev/null
+++ b/obex/javax/obex/ServerSession.java
@@ -0,0 +1,922 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This class in an implementation of the ServerSession interface.
+ * 
+ * @hide
+ */
+public class ServerSession implements Runnable, ObexSession {
+
+    private ObexTransport client;
+
+    private InputStream input;
+
+    private OutputStream output;
+
+    private ServerRequestHandler listener;
+
+    private Thread processThread;
+
+    private int maxPacketLength;
+
+    private Authenticator authenticator;
+
+    byte[] challengeDigest;
+
+    private boolean isClosed;
+
+    /**
+     * Creates new ServerSession.
+     *
+     * @param conn
+     *            the connection to the client
+     *
+     * @param handler
+     *            the event listener that will process requests
+     *
+     * @param auth
+     *            the authenticator to use with this connection
+     *
+     * @throws IOException
+     *                if an error occurred while opening the input and output
+     *                streams
+     */
+    public ServerSession(ObexTransport conn, ServerRequestHandler handler, Authenticator auth)
+            throws IOException {
+        authenticator = auth;
+        client = conn;
+        input = client.openInputStream();
+        output = client.openOutputStream();
+        listener = handler;
+        maxPacketLength = 256;
+
+        isClosed = false;
+        processThread = new Thread(this);
+        processThread.start();
+    }
+
+    /* removed as they're provided to the API user. Not used internally. */
+    /*
+     public boolean isCreatedServer() {
+        if (client instanceof BTConnection)
+            return ((BTConnection)client).isServerCreated();
+        else
+            return false;
+    }
+
+    public boolean isClosed() {
+        if (client instanceof BTConnection)
+            return ((BTConnection)client).isClosed();
+        else
+            return false;
+    }
+
+    public int getConnectionHandle() {
+        if (client instanceof BTConnection)
+            return ((BTConnection)client).getConnectionHandle();
+        else
+            return -1;
+    }
+
+    public RemoteDevice getRemoteDevice() {
+        if (client instanceof BTConnection)
+            return ((BTConnection)client).getRemoteDevice();
+        else
+            return null;
+    }*/
+
+    /**
+     * Processes requests made to the server and forwards them to the
+     * appropriate event listener.
+     */
+    public void run() {
+        try {
+
+            boolean done = false;
+            while (!done && !isClosed) {
+                int requestType = input.read();
+                switch (requestType) {
+                    case 0x80:
+                        handleConnectRequest();
+                        break;
+
+                    case 0x81:
+                        handleDisconnectRequest();
+                        done = true;
+                        break;
+
+                    case 0x03:
+                    case 0x83:
+                        handleGetRequest(requestType);
+                        break;
+
+                    case 0x02:
+                    case 0x82:
+                        handlePutRequest(requestType);
+                        break;
+
+                    case 0x85:
+                        handleSetPathRequest();
+                        break;
+
+                    case -1:
+                        done = true;
+                        break;
+
+                    default:
+
+                        /*
+                         * Received a request type that is not recognized so I am
+                         * just going to read the packet and send a not implemented
+                         * to the client
+                         */
+                        int length = input.read();
+                        length = (length << 8) + input.read();
+                        for (int i = 3; i < length; i++) {
+                            input.read();
+                        }
+                        sendResponse(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null);
+
+                        // done = true;
+                }
+            }
+
+        } catch (NullPointerException e) {
+        } catch (Exception e) {
+        }
+        close();
+    }
+
+    /**
+     * Handles a PUT request from a client. This method will provide a
+     * <code>ServerOperation</code> object to the request handler. The
+     * <code>ServerOperation</code> object will handle the rest of the request.
+     * It will also send replies and receive requests until the final reply
+     * should be sent. When the final reply should be sent, this method will get
+     * the response code to use and send the reply. The
+     * <code>ServerOperation</code> object will always reply with a
+     * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
+     * needed.
+     *
+     * @param type
+     *            the type of request received; either 0x02 or 0x82
+     *
+     * @throws IOException
+     *                if an error occurred at the transport layer
+     */
+    private void handlePutRequest(int type) throws IOException {
+        ServerOperation client = new ServerOperation(this, input, type, maxPacketLength, listener);
+        try {
+            int response = -1;
+
+            if ((client.finalBitSet) && !client.isValidBody()) {
+                response = validateResponseCode(listener.onDelete(client.requestHeaders,
+                        client.replyHeaders));
+            } else {
+                response = validateResponseCode(listener.onPut(client));
+            }
+            if (response != ResponseCodes.OBEX_HTTP_OK) {
+                client.sendReply(response);
+            } else if (!client.isAborted) {
+                // wait for the final bit
+                while (!client.finalBitSet) {
+                    client.sendReply(ObexHelper.OBEX_HTTP_CONTINUE);
+                }
+                client.sendReply(response);
+            }
+        } catch (Exception e) {
+            sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+        }
+    }
+
+    /**
+     * Handles a GET request from a client. This method will provide a
+     * <code>ServerOperation</code> object to the request handler. The
+     * <code>ServerOperation</code> object will handle the rest of the request.
+     * It will also send replies and receive requests until the final reply
+     * should be sent. When the final reply should be sent, this method will get
+     * the response code to use and send the reply. The
+     * <code>ServerOperation</code> object will always reply with a
+     * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
+     * needed.
+     *
+     * @param type
+     *            the type of request received; either 0x03 or 0x83
+     *
+     * @throws IOException
+     *                if an error occurred at the transport layer
+     */
+    private void handleGetRequest(int type) throws IOException {
+        ServerOperation client = new ServerOperation(this, input, type, maxPacketLength, listener);
+        try {
+            int response = validateResponseCode(listener.onGet(client));
+
+            if (!client.isAborted) {
+                client.sendReply(response);
+            }
+        } catch (Exception e) {
+            sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+        }
+    }
+
+    /**
+     * Send standard response.
+     *
+     * @param code
+     *            the response code to send
+     *
+     * @param header
+     *            the headers to include in the response
+     *
+     * @throws IOException
+     *                if an IO error occurs
+     */
+    protected void sendResponse(int code, byte[] header) throws IOException {
+        int totalLength = 3;
+        byte[] data = null;
+
+        if (header != null) {
+            totalLength += header.length;
+            data = new byte[totalLength];
+            data[0] = (byte)code;
+            data[1] = (byte)(totalLength >> 8);
+            data[2] = (byte)totalLength;
+            System.arraycopy(header, 0, data, 3, header.length);
+        } else {
+            data = new byte[totalLength];
+            data[0] = (byte)code;
+            data[1] = (byte)0x00;
+            data[2] = (byte)totalLength;
+        }
+        output.write(data);
+        output.flush();
+    }
+
+    /**
+     * Handles a SETPATH request from a client. This method will read the rest
+     * of the request from the client. Assuming the request is valid, it will
+     * create a <code>HeaderSet</code> object to pass to the
+     * <code>ServerRequestHandler</code> object. After the handler processes the
+     * request, this method will create a reply message to send to the server
+     * with the response code provided.
+     *
+     * @throws IOException
+     *                if an error occurred at the transport layer
+     */
+    private void handleSetPathRequest() throws IOException {
+        int length;
+        int flags;
+        int constants;
+        int totalLength = 3;
+        byte[] head = null;
+        int code = -1;
+        int bytesReceived;
+        HeaderSet request = new HeaderSet();
+        HeaderSet reply = new HeaderSet();
+
+        length = input.read();
+        length = (length << 8) + input.read();
+        flags = input.read();
+        constants = input.read();
+
+        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
+            totalLength = 3;
+        } else {
+            if (length > 5) {
+                byte[] headers = new byte[length - 5];
+                bytesReceived = input.read(headers);
+
+                while (bytesReceived != headers.length) {
+                    bytesReceived += input.read(headers, bytesReceived, headers.length
+                            - bytesReceived);
+                }
+
+                ObexHelper.updateHeaderSet(request, headers);
+
+                if (request.connectionID != null) {
+                    listener.setConnectionID(ObexHelper.convertToLong(request.connectionID));
+                } else {
+                    listener.setConnectionID(-1);
+                }
+                // the Auth chan is initiated by the server.
+                // client sent back the authResp .
+                if (request.authResp != null) {
+                    if (!handleAuthResp(request.authResp)) {
+                        code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
+                        listener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
+                                request.authResp));
+                    }
+                    request.authResp = null;
+                }
+            }
+
+            if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
+                // the Auth chan is initiated by the client
+                // the server will send back the authResp to the client
+                if (request.authChall != null) {
+                    handleAuthChall(request);
+                    reply.authResp = new byte[request.authResp.length];
+                    System.arraycopy(request.authResp, 0, reply.authResp, 0, reply.authResp.length);
+                    request.authChall = null;
+                    request.authResp = null;
+                }
+                boolean backup = false;
+                boolean create = true;
+                if (!((flags & 1) == 0)) {
+                    backup = true;
+                }
+                if ((flags & 2) == 0) {
+                    create = false;
+                }
+
+                try {
+                    code = listener.onSetPath(request, reply, backup, create);
+                } catch (Exception e) {
+                    sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+                    return;
+                }
+
+                code = validateResponseCode(code);
+
+                if (reply.nonce != null) {
+                    challengeDigest = new byte[16];
+                    System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16);
+                } else {
+                    challengeDigest = null;
+                }
+
+                long id = listener.getConnectionID();
+                if (id == -1) {
+                    reply.connectionID = null;
+                } else {
+                    reply.connectionID = ObexHelper.convertToByteArray(id);
+                }
+
+                head = ObexHelper.createHeader(reply, false);
+                totalLength += head.length;
+
+                if (totalLength > maxPacketLength) {
+                    totalLength = 3;
+                    head = null;
+                    code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+                }
+            }
+        }
+
+        // Compute Length of OBEX SETPATH packet
+        byte[] replyData = new byte[totalLength];
+        replyData[0] = (byte)code;
+        replyData[1] = (byte)(totalLength >> 8);
+        replyData[2] = (byte)totalLength;
+        if (head != null) {
+            System.arraycopy(head, 0, replyData, 3, head.length);
+        }
+        /*
+         * Write the OBEX SETPATH packet to the server. Byte 0: response code
+         * Byte 1&2: Connect Packet Length Byte 3 to n: headers
+         */
+        output.write(replyData);
+        output.flush();
+    }
+
+    /**
+     * Handles a disconnect request from a client. This method will read the
+     * rest of the request from the client. Assuming the request is valid, it
+     * will create a <code>HeaderSet</code> object to pass to the
+     * <code>ServerRequestHandler</code> object. After the handler processes the
+     * request, this method will create a reply message to send to the server.
+     *
+     * @throws IOException
+     *                if an error occurred at the transport layer
+     */
+    private void handleDisconnectRequest() throws IOException {
+        int length;
+        int code = ResponseCodes.OBEX_HTTP_OK;
+        int totalLength = 3;
+        byte[] head = null;
+        int bytesReceived;
+        HeaderSet request = new HeaderSet();
+        HeaderSet reply = new HeaderSet();
+
+        length = input.read();
+        length = (length << 8) + input.read();
+
+        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
+            totalLength = 3;
+        } else {
+            if (length > 3) {
+                byte[] headers = new byte[length - 3];
+                bytesReceived = input.read(headers);
+
+                while (bytesReceived != headers.length) {
+                    bytesReceived += input.read(headers, bytesReceived, headers.length
+                            - bytesReceived);
+                }
+
+                ObexHelper.updateHeaderSet(request, headers);
+            }
+
+            if (request.connectionID != null) {
+                listener.setConnectionID(ObexHelper.convertToLong(request.connectionID));
+            } else {
+                listener.setConnectionID(1);
+            }
+
+            if (request.authResp != null) {
+                if (!handleAuthResp(request.authResp)) {
+                    code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
+                    listener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
+                            request.authResp));
+                }
+                request.authResp = null;
+            }
+
+            if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
+
+                if (request.authChall != null) {
+                    handleAuthChall(request);
+                    request.authChall = null;
+                }
+
+                try {
+                    listener.onDisconnect(request, reply);
+                } catch (Exception e) {
+                    sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+                    return;
+                }
+
+                /*
+                 * Since a client will never response to an authentication
+                 * challenge on a DISCONNECT, there is no reason to keep track
+                 * of the challenge.
+                 *
+                 * if (reply.nonce != null) { challengeDigest = new byte[16];
+                 * System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16); }
+                 * else { challengeDigest = null; }
+                 */
+
+                long id = listener.getConnectionID();
+                if (id == -1) {
+                    reply.connectionID = null;
+                } else {
+                    reply.connectionID = ObexHelper.convertToByteArray(id);
+                }
+
+                head = ObexHelper.createHeader(reply, false);
+                totalLength += head.length;
+
+                if (totalLength > maxPacketLength) {
+                    totalLength = 3;
+                    head = null;
+                    code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+                }
+            }
+        }
+
+        // Compute Length of OBEX CONNECT packet
+        byte[] replyData;
+        if (head != null) {
+            replyData = new byte[3 + head.length];
+        } else {
+            replyData = new byte[3];
+        }
+        replyData[0] = (byte)code;
+        replyData[1] = (byte)(totalLength >> 8);
+        replyData[2] = (byte)totalLength;
+        if (head != null) {
+            System.arraycopy(head, 0, replyData, 3, head.length);
+        }
+        /*
+         * Write the OBEX DISCONNECT packet to the server. Byte 0: response code
+         * Byte 1&2: Connect Packet Length Byte 3 to n: headers
+         */
+        output.write(replyData);
+        output.flush();
+    }
+
+    /**
+     * Handles a connect request from a client. This method will read the rest
+     * of the request from the client. Assuming the request is valid, it will
+     * create a <code>HeaderSet</code> object to pass to the
+     * <code>ServerRequestHandler</code> object. After the handler processes the
+     * request, this method will create a reply message to send to the server
+     * with the response code provided.
+     *
+     * @throws IOException
+     *                if an error occurred at the transport layer
+     */
+    private void handleConnectRequest() throws IOException {
+        int packetLength;
+        int version;
+        int flags;
+        int totalLength = 7;
+        byte[] head = null;
+        int code = -1;
+        HeaderSet request = new HeaderSet();
+        HeaderSet reply = new HeaderSet();
+        int bytesReceived;
+
+        /*
+         * Read in the length of the OBEX packet, OBEX version, flags, and max
+         * packet length
+         */
+        packetLength = input.read();
+        packetLength = (packetLength << 8) + input.read();
+        version = input.read();
+        flags = input.read();
+        maxPacketLength = input.read();
+        maxPacketLength = (maxPacketLength << 8) + input.read();
+
+        // should we check it?
+        if (maxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) {
+            maxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT;
+        }
+
+        if (packetLength > ObexHelper.MAX_PACKET_SIZE_INT) {
+            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
+            totalLength = 7;
+        } else {
+            if (packetLength > 7) {
+                byte[] headers = new byte[packetLength - 7];
+                bytesReceived = input.read(headers);
+
+                while (bytesReceived != headers.length) {
+                    bytesReceived += input.read(headers, bytesReceived, headers.length
+                            - bytesReceived);
+                }
+
+                ObexHelper.updateHeaderSet(request, headers);
+            }
+
+            if (request.connectionID != null) {
+                listener.setConnectionID(ObexHelper.convertToLong(request.connectionID));
+            } else {
+                listener.setConnectionID(1);
+            }
+
+            if (request.authResp != null) {
+                if (!handleAuthResp(request.authResp)) {
+                    code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
+                    listener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
+                            request.authResp));
+                }
+                request.authResp = null;
+            }
+
+            if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
+                if (request.authChall != null) {
+                    handleAuthChall(request);
+                    reply.authResp = new byte[request.authResp.length];
+                    System.arraycopy(request.authResp, 0, reply.authResp, 0, reply.authResp.length);
+                    request.authChall = null;
+                    request.authResp = null;
+                }
+
+                try {
+                    code = listener.onConnect(request, reply);
+                    code = validateResponseCode(code);
+
+                    if (reply.nonce != null) {
+                        challengeDigest = new byte[16];
+                        System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16);
+                    } else {
+                        challengeDigest = null;
+                    }
+                    long id = listener.getConnectionID();
+                    if (id == -1) {
+                        reply.connectionID = null;
+                    } else {
+                        reply.connectionID = ObexHelper.convertToByteArray(id);
+                    }
+
+                    head = ObexHelper.createHeader(reply, false);
+                    totalLength += head.length;
+
+                    if (totalLength > maxPacketLength) {
+                        totalLength = 7;
+                        head = null;
+                        code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    totalLength = 7;
+                    head = null;
+                    code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+                }
+
+            }
+        }
+
+        // Compute Length of OBEX CONNECT packet
+        byte[] length = ObexHelper.convertToByteArray(totalLength);
+
+        /*
+         * Write the OBEX CONNECT packet to the server. Byte 0: response code
+         * Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number
+         * (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX
+         * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
+         */
+        byte[] sendData = new byte[totalLength];
+        sendData[0] = (byte)code;
+        sendData[1] = length[2];
+        sendData[2] = length[3];
+        sendData[3] = (byte)0x10;
+        sendData[4] = (byte)0x00;
+        sendData[5] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8);
+        sendData[6] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF);
+
+        if (head != null) {
+            System.arraycopy(head, 0, sendData, 7, head.length);
+        }
+
+        output.write(sendData);
+        output.flush();
+    }
+
+    /**
+     * Closes the server session - in detail close I/O streams and the
+     * underlying transport layer. Internal flag is also set so that later
+     * attempt to read/write will throw an exception.
+     */
+    public synchronized void close() {
+        if (listener != null) {
+            listener.onClose();
+        }
+        try {
+            input.close();
+            output.close();
+            client.close();
+            isClosed = true;
+        } catch (Exception e) {
+        }
+        client = null;
+        input = null;
+        output = null;
+        listener = null;
+    }
+
+    /**
+     * Verifies that the response code is valid. If it is not valid, it will
+     * return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code.
+     *
+     * @param code
+     *            the response code to check
+     *
+     * @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code>
+     *         if <code>code</code> is not valid
+     */
+    private int validateResponseCode(int code) {
+
+        if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) {
+            return code;
+        }
+        if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE)
+                && (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) {
+            return code;
+        }
+        if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST)
+                && (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) {
+            return code;
+        }
+        if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR)
+                && (code <= ResponseCodes.OBEX_HTTP_VERSION)) {
+            return code;
+        }
+        if ((code >= ResponseCodes.OBEX_DATABASE_FULL)
+                && (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) {
+            return code;
+        }
+        return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+    }
+
+    /**
+     * Called when the server received an authentication challenge header. This
+     * will cause the authenticator to handle the authentication challenge.
+     *
+     * @param header
+     *            the header with the authentication challenge
+     *
+     * @return <code>true</code> if the last request should be resent;
+     *         <code>false</code> if the last request should not be resent
+     */
+    protected boolean handleAuthChall(HeaderSet header) {
+        if (authenticator == null) {
+            return false;
+        }
+
+        /*
+         * An authentication challenge is made up of one required and two
+         * optional tag length value triplets. The tag 0x00 is required to be in
+         * the authentication challenge and it represents the challenge digest
+         * that was received. The tag 0x01 is the options tag. This tag tracks
+         * if user ID is required and if full access will be granted. The tag
+         * 0x02 is the realm, which provides a description of which user name
+         * and password to use.
+         */
+        byte[] challenge = ObexHelper.getTagValue((byte)0x00, header.authChall);
+        byte[] option = ObexHelper.getTagValue((byte)0x01, header.authChall);
+        byte[] description = ObexHelper.getTagValue((byte)0x02, header.authChall);
+
+        String realm = "";
+        if (description != null) {
+            byte[] realmString = new byte[description.length - 1];
+            System.arraycopy(description, 1, realmString, 0, realmString.length);
+
+            switch (description[0] & 0xFF) {
+
+                case 0x00:
+                    // ASCII encoding
+                    // Fall through
+                case 0x01:
+                    // ISO-8859-1 encoding
+                    try {
+                        realm = new String(realmString, "ISO8859_1");
+                    } catch (Exception e) {
+                        throw new RuntimeException("Unsupported Encoding Scheme");
+                    }
+                    break;
+
+                case 0xFF:
+                    // UNICODE Encoding
+                    realm = ObexHelper.convertToUnicode(realmString, false);
+                    break;
+
+                case 0x02:
+                    // ISO-8859-2 encoding
+                    // Fall through
+                case 0x03:
+                    // ISO-8859-3 encoding
+                    // Fall through
+                case 0x04:
+                    // ISO-8859-4 encoding
+                    // Fall through
+                case 0x05:
+                    // ISO-8859-5 encoding
+                    // Fall through
+                case 0x06:
+                    // ISO-8859-6 encoding
+                    // Fall through
+                case 0x07:
+                    // ISO-8859-7 encoding
+                    // Fall through
+                case 0x08:
+                    // ISO-8859-8 encoding
+                    // Fall through
+                case 0x09:
+                    // ISO-8859-9 encoding
+                    // Fall through
+                default:
+                    throw new RuntimeException("Unsupported Encoding Scheme");
+            }
+        }
+
+        boolean isUserIDRequired = false;
+        boolean isFullAccess = true;
+        if (option != null) {
+            if ((option[0] & 0x01) != 0) {
+                isUserIDRequired = true;
+            }
+
+            if ((option[0] & 0x02) != 0) {
+                isFullAccess = false;
+            }
+        }
+
+        PasswordAuthentication result = null;
+        header.authChall = null;
+
+        try {
+            result = authenticator.onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess);
+        } catch (Exception e) {
+            return false;
+        }
+
+        /*
+         * If no password is provided then we not resent the request
+         */
+        if (result == null) {
+            return false;
+        }
+
+        byte[] password = result.getPassword();
+        if (password == null) {
+            return false;
+        }
+
+        byte[] userName = result.getUserName();
+
+        /*
+         * Create the authentication response header. It includes 1 required and
+         * 2 option tag length value triples. The required triple has a tag of
+         * 0x00 and is the response digest. The first optional tag is 0x01 and
+         * represents the user ID. If no user ID is provided, then no user ID
+         * will be sent. The second optional tag is 0x02 and is the challenge
+         * that was received. This will always be sent
+         */
+        if (userName != null) {
+            header.authResp = new byte[38 + userName.length];
+            header.authResp[36] = (byte)0x01;
+            header.authResp[37] = (byte)userName.length;
+            System.arraycopy(userName, 0, header.authResp, 38, userName.length);
+        } else {
+            header.authResp = new byte[36];
+        }
+
+        // Create the secret String
+        byte[] digest = new byte[challenge.length + password.length + 1];
+        System.arraycopy(challenge, 0, digest, 0, challenge.length);
+        // Insert colon between challenge and password
+        digest[challenge.length] = (byte)0x3A;
+        System.arraycopy(password, 0, digest, challenge.length + 1, password.length);
+
+        // Add the Response Digest
+        header.authResp[0] = (byte)0x00;
+        header.authResp[1] = (byte)0x10;
+
+        System.arraycopy(ObexHelper.computeMd5Hash(digest), 0, header.authResp, 2, 16);
+
+        // Add the challenge
+        header.authResp[18] = (byte)0x02;
+        header.authResp[19] = (byte)0x10;
+        System.arraycopy(challenge, 0, header.authResp, 20, 16);
+
+        return true;
+    }
+
+    /**
+     * Called when the server received an authentication response header. This
+     * will cause the authenticator to handle the authentication response.
+     *
+     * @param authResp
+     *            the authentication response
+     *
+     * @return <code>true</code> if the response passed; <code>false</code> if
+     *         the response failed
+     */
+    protected boolean handleAuthResp(byte[] authResp) {
+        if (authenticator == null) {
+            return false;
+        }
+        // get the correct password from the application
+        byte[] correctPassword = authenticator.onAuthenticationResponse(ObexHelper.getTagValue(
+                (byte)0x01, authResp));
+        if (correctPassword == null) {
+            return false;
+        }
+
+        byte[] temp = new byte[correctPassword.length + 16];
+
+        System.arraycopy(challengeDigest, 0, temp, 0, 16);
+        System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length);
+
+        byte[] correctResponse = ObexHelper.computeMd5Hash(temp);
+        byte[] actualResponse = ObexHelper.getTagValue((byte)0x00, authResp);
+
+        // compare the MD5 hash array .
+        for (int i = 0; i < 16; i++) {
+            if (correctResponse[i] != actualResponse[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/obex/javax/obex/SessionNotifier.java b/obex/javax/obex/SessionNotifier.java
new file mode 100644
index 0000000..fd574c0
--- /dev/null
+++ b/obex/javax/obex/SessionNotifier.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+
+/**
+ * The <code>SessionNotifier</code> interface defines a connection notifier for
+ * server-side OBEX connections.  When a <code>SessionNotifier</code> is
+ * created and calls  <code>acceptAndOpen()</code>, it will begin listening for
+ * clients to create a connection at the transport layer.  When the transport
+ * layer connection is received, the <code>acceptAndOpen()</code> method will
+ * return a  <code>javax.microedition.io.Connection</code> that is the
+ * connection to the client.  The <code>acceptAndOpen()</code> method also takes a
+ * <code>ServerRequestHandler</code> argument that will process the requests
+ * from the client that connects to the server.
+ *
+ * @hide
+ */
+public interface SessionNotifier {
+
+    /**
+     * Waits for a transport layer connection to be established and specifies
+     * the handler to handle the requests from the client.  No authenticator
+     * is associated with this connection, therefore, it is implementation
+     * dependent as to how an authentication challenge and authentication
+     * response header will be received and processed.
+     * <P>
+     * <H4>Additional Note for OBEX over Bluetooth</H4>
+     * If this method is called on a <code>SessionNotifier</code> object that
+     * does not have a <code>ServiceRecord</code> in the SDDB, the
+     * <code>ServiceRecord</code> for this object will be added to the SDDB.
+     * This method requests the BCC to put the
+     * local device in connectable mode so that it will respond to
+     * connection attempts by clients.
+     * <P>
+     * The following checks are done to verify that the service record
+     * provided is valid. If any of these checks fail, then a
+     * <code>ServiceRegistrationException</code> is thrown.
+     * <UL>
+     * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory
+     * service attributes for a <code>btgoep</code> service record, must be
+     * present in the <code>ServiceRecord</code> associated with this notifier.
+     * <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
+     * <LI>The <code>ServiceRecord</code> associated with this notifier must
+     *  not have changed the RFCOMM server channel number
+     * </UL>
+     * <P>
+     * This method will not ensure that <code>ServiceRecord</code> associated
+     * with this notifier is a completely
+     * valid service record. It is the responsibility of the application to
+     * ensure that the service record follows all of the applicable
+     * syntactic and semantic rules for service record correctness.
+     *
+     * @param handler the request handler that will respond to OBEX requests
+     *
+     * @return the connection to the client
+     *
+     * @throws IOException if an error occurs in the transport layer
+     *
+     * @throws NullPointerException if <code>handler</code> is
+     * <code>null</code>
+     *
+     * @throws ServiceRegistrationException if the structure of the
+     * associated service record is invalid or if the service record
+     * could not be added successfully to the local SDDB.  The
+     * structure of service record is invalid if the service
+     * record is missing any mandatory service attributes, or has
+     * changed any of the values described above which are fixed and
+     * cannot be changed. Failures to add the record to the SDDB could
+     * be due to insufficient disk space, database locks, etc.
+     *
+     * @throws BluetoothStateException if the server device could
+     * not be placed in connectable mode because the device user has
+     * configured the device to be non-connectable
+     */
+    public ObexSession acceptAndOpen(ServerRequestHandler handler) throws IOException;
+
+    /**
+     * Waits for a transport layer connection to be established and specifies
+     * the handler to handle the requests from the client and the
+     * <code>Authenticator</code> to use to respond to authentication challenge
+     * and authentication response headers.
+     * <P>
+     * <H4>Additional Note for OBEX over Bluetooth</H4>
+     * If this method is called on a <code>SessionNotifier</code> object that
+     * does not have a <code>ServiceRecord</code> in the SDDB, the
+     * <code>ServiceRecord</code> for this object will be added to the SDDB.
+     * This method requests the BCC to put the
+     * local device in connectable mode so that it will respond to
+     * connection attempts by clients.
+     * <P>
+     * The following checks are done to verify that the service record
+     * provided is valid. If any of these checks fail, then a
+     * <code>ServiceRegistrationException</code> is thrown.
+     * <UL>
+     * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory
+     * service attributes for a <code>btgoep</code> service record, must be
+     * present in the <code>ServiceRecord</code> associated with this notifier.
+     * <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
+     * <LI>The <code>ServiceRecord</code> associated with this notifier must
+     *  not have changed the RFCOMM server channel number
+     * </UL>
+     * <P>
+     * This method will not ensure that <code>ServiceRecord</code> associated
+     * with this notifier is a completely
+     * valid service record. It is the responsibility of the application to
+     * ensure that the service record follows all of the applicable
+     * syntactic and semantic rules for service record correctness.
+     *
+     * @param handler the request handler that will respond to OBEX requests
+     *
+     * @param auth the <code>Authenticator</code> to use with this connection;
+     * if <code>null</code> then no <code>Authenticator</code> will be used
+     *
+     * @return the connection to the client
+     *
+     * @throws IOException if an error occurs in the transport layer
+     *
+     * @throws NullPointerException if <code>handler</code> is
+     * <code>null</code>
+     *
+     * @throws ServiceRegistrationException if the structure of the
+     * associated service record is invalid or if the service record
+     * could not be added successfully to the local SDDB.  The
+     * structure of service record is invalid if the service
+     * record is missing any mandatory service attributes, or has
+     * changed any of the values described above which are fixed and
+     * cannot be changed. Failures to add the record to the SDDB could
+     * be due to insufficient disk space, database locks, etc.
+     *
+     * @throws BluetoothStateException if the server device could
+     * not be placed in connectable mode because the device user has
+     * configured the device to be non-connectable
+     */
+    public ObexSession acceptAndOpen(ServerRequestHandler handler, Authenticator auth)
+            throws IOException;
+}
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 23304d5..02dadbb 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -11,7 +11,7 @@
 	EGL/gpu.cpp			\
 #
 
-LOCAL_SHARED_LIBRARIES += libcutils libutils libui
+LOCAL_SHARED_LIBRARIES += libcutils libutils libbinder libui
 LOCAL_LDLIBS := -lpthread -ldl
 LOCAL_MODULE:= libEGL
 
diff --git a/opengl/libs/EGL/gpu.cpp b/opengl/libs/EGL/gpu.cpp
index 4c902c8..416bd5d 100644
--- a/opengl/libs/EGL/gpu.cpp
+++ b/opengl/libs/EGL/gpu.cpp
@@ -29,11 +29,11 @@
 #include <cutils/log.h>
 #include <cutils/properties.h>
 
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
 #include <utils/threads.h>
-#include <utils/IServiceManager.h>
-#include <utils/IPCThreadState.h>
-#include <utils/Parcel.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/Parcel.h>
 
 #include <ui/EGLDisplaySurface.h>
 #include <ui/ISurfaceComposer.h>
diff --git a/packages/SettingsProvider/etc/bookmarks.xml b/packages/SettingsProvider/etc/bookmarks.xml
index 5fb6608..33b4c97 100644
--- a/packages/SettingsProvider/etc/bookmarks.xml
+++ b/packages/SettingsProvider/etc/bookmarks.xml
@@ -39,10 +39,12 @@
         package="com.android.calendar"
         class="com.android.calendar.LaunchActivity"
         shortcut="l" />
+<!--
     <bookmark
         package="com.google.android.apps.maps"
         class="com.google.android.maps.MapsActivity"
         shortcut="m" />
+-->
     <bookmark
         package="com.android.music"
         class="com.android.music.MusicBrowserActivity"
@@ -55,4 +57,4 @@
         package="com.google.android.youtube"
         class="com.google.android.youtube.HomePage"
         shortcut="y" />
-</bookmarks>
\ No newline at end of file
+</bookmarks>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index c283418..87f4c40 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -36,6 +36,7 @@
          user opt-in via Setup Wizard or Settings.  
     -->
     <string name="def_location_providers_allowed">gps</string>
+    <bool name="assisted_gps_enabled">true</bool>
     <!--  0 == mobile, 1 == wifi. -->
     <integer name="def_network_preference">1</integer>
     <bool name="def_usb_mass_storage_enabled">true</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 660b469..602f3e1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -56,7 +56,7 @@
  * Database helper class for {@link SettingsProvider}.
  * Mostly just has a bit {@link #onCreate} to initialize the database.
  */
-class DatabaseHelper extends SQLiteOpenHelper {
+public class DatabaseHelper extends SQLiteOpenHelper {
     /**
      * Path to file containing default bookmarks, relative to ANDROID_ROOT.
      */
@@ -64,7 +64,7 @@
 
     private static final String TAG = "SettingsProvider";
     private static final String DATABASE_NAME = "settings.db";
-    private static final int DATABASE_VERSION = 34;
+    private static final int DATABASE_VERSION = 35;
 
     private Context mContext;
 
@@ -386,6 +386,21 @@
             upgradeVersion = 34;
         }
 
+        if (upgradeVersion == 34) {
+            db.beginTransaction();
+            try {
+                String value =
+                        mContext.getResources().getBoolean(R.bool.assisted_gps_enabled) ? "1" : "0";
+                db.execSQL("INSERT OR IGNORE INTO secure(name,value) values('" +
+                        Settings.Secure.ASSISTED_GPS_ENABLED + "','" + value + "');");
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+
+            upgradeVersion = 35;
+        }
+
         if (upgradeVersion != currentVersion) {
             Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion
                     + ", must wipe the settings provider");
@@ -653,6 +668,9 @@
         loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
                 R.string.def_location_providers_allowed);
 
+        loadBooleanSetting(stmt, Settings.Secure.ASSISTED_GPS_ENABLED,
+                R.bool.assisted_gps_enabled);
+
         loadIntegerSetting(stmt, Settings.Secure.NETWORK_PREFERENCE,
                 R.integer.def_network_preference);
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 3db52eb..1a03900 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -43,7 +43,7 @@
     private static final String TABLE_FAVORITES = "favorites";
     private static final String TABLE_OLD_FAVORITES = "old_favorites";
 
-    private DatabaseHelper mOpenHelper;
+    protected DatabaseHelper mOpenHelper;
 
     /**
      * Decode a content URL into the table, projection, and arguments
diff --git a/packages/SubscribedFeedsProvider/AndroidManifest.xml b/packages/SubscribedFeedsProvider/AndroidManifest.xml
index ca00a9b..d839c4e 100644
--- a/packages/SubscribedFeedsProvider/AndroidManifest.xml
+++ b/packages/SubscribedFeedsProvider/AndroidManifest.xml
@@ -19,7 +19,7 @@
                 android:writePermission="android.permission.SUBSCRIBED_FEEDS_WRITE" />
         <receiver android:name="SubscribedFeedsBroadcastReceiver">
             <intent-filter>
-                <action android:name="android.intent.action.GTALK_DATA_MESSAGE_RECEIVED" />
+                <action android:name="android.intent.action.REMOTE_INTENT" />
                 <category android:name="GSYNC_TICKLE"/>
             </intent-filter>
             <intent-filter>
diff --git a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java
index 8b3bedf..3cd2cc4 100644
--- a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java
+++ b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java
@@ -16,13 +16,14 @@
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.text.TextUtils;
-import android.net.Uri;
+import android.accounts.Account;
 
 import java.util.ArrayList;
 import java.util.Calendar;
 
+import com.google.android.collect.Lists;
+
 /**
  * A service to handle various intents asynchronously.
  */
@@ -30,7 +31,8 @@
     private static final String TAG = "Sync";
 
     private static final String[] sAccountProjection =
-            new String[] {SubscribedFeeds.Accounts._SYNC_ACCOUNT};
+            new String[] {SubscribedFeeds.Accounts._SYNC_ACCOUNT,
+                    SubscribedFeeds.Accounts._SYNC_ACCOUNT_TYPE};
 
     /** How often to refresh the subscriptions, in milliseconds */
     private static final long SUBSCRIPTION_REFRESH_INTERVAL = 1000L * 60 * 60 * 24; // one day
@@ -39,8 +41,7 @@
 
     private static final String sSubscribedFeedsPrefs = "subscribedFeeds";
 
-    private static final String GTALK_DATA_MESSAGE_RECEIVED =
-            "android.intent.action.GTALK_DATA_MESSAGE_RECEIVED";
+    private static final String REMOTE_INTENT_ACTION = Intent.ACTION_REMOTE_INTENT;
 
     private static final String SUBSCRIBED_FEEDS_REFRESH_ACTION =
             "com.android.subscribedfeeds.action.REFRESH";
@@ -52,13 +53,14 @@
     }
 
     protected void onHandleIntent(Intent intent) {
-        if (GTALK_DATA_MESSAGE_RECEIVED.equals(intent.getAction())) {
-            boolean fromTrustedServer = intent.getBooleanExtra("from_trusted_server", false);
+        if (REMOTE_INTENT_ACTION.equals(intent.getAction())) {
+            boolean fromTrustedServer = intent.getBooleanExtra(
+                    "android.intent.extra.from_trusted_server", false);
             if (fromTrustedServer) {
-                String account = intent.getStringExtra("account");
-                String token = intent.getStringExtra("message_token");
+                String accountName = intent.getStringExtra("account");
+                String token = intent.getStringExtra(Intent.EXTRA_REMOTE_INTENT_TOKEN);
 
-                if (TextUtils.isEmpty(account) || TextUtils.isEmpty(token)) {
+                if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(token)) {
                     if (Config.LOGD) {
                         Log.d(TAG, "Ignoring malformed tickle -- missing account or token.");
                     }
@@ -67,10 +69,10 @@
 
                 if (Config.LOGD) {
                     Log.d(TAG, "Received network tickle for "
-                            + account + " - " + token);
+                            + accountName + " - " + token);
                 }
 
-                handleTickle(this, account, token);
+                handleTickle(this, accountName, token);
             } else {
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
                     Log.v(TAG, "Ignoring tickle -- not from trusted server.");
@@ -102,16 +104,19 @@
         alarmManager.set(AlarmManager.RTC, when, pendingIntent);
     }
 
-    private void handleTickle(Context context, String account, String feed) {
+    private void handleTickle(Context context, String accountName, String feed) {
         Cursor c = null;
         final String where = SubscribedFeeds.Feeds._SYNC_ACCOUNT + "= ? "
+                + "and " + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "= ? "
                 + "and " + SubscribedFeeds.Feeds.FEED + "= ?";
         try {
+            // TODO(fredq) fix the hardcoded type
+            final Account account = new Account(accountName, "com.google.GAIA");
             c = context.getContentResolver().query(SubscribedFeeds.Feeds.CONTENT_URI,
-                    null, where, new String[]{account, feed}, null);
+                    null, where, new String[]{account.mName, account.mType, feed}, null);
             if (c.getCount() == 0) {
                 Log.w(TAG, "received tickle for non-existent feed: "
-                        + "account " + account + ", feed " + feed);
+                        + "account " + accountName + ", feed " + feed);
                 EventLog.writeEvent(LOG_TICKLE, "unknown");
             }
             while (c.moveToNext()) {
@@ -119,21 +124,14 @@
                 String authority = c.getString(c.getColumnIndexOrThrow(
                         SubscribedFeeds.Feeds.AUTHORITY));
                 EventLog.writeEvent(LOG_TICKLE, authority);
-                try {
-                    if (!ContentResolver.getContentService()
-                            .getSyncProviderAutomatically(authority)) {
-                        Log.d(TAG, "supressing tickle since provider " + authority
-                                + " is configured to not sync automatically");
-                        continue;
-                    }
-                } catch (RemoteException e) {
+                if (!ContentResolver.getSyncAutomatically(account, authority)) {
+                    Log.d(TAG, "supressing tickle since provider " + authority
+                            + " is configured to not sync automatically");
                     continue;
                 }
-                Uri uri = Uri.parse("content://" + authority);
                 Bundle extras = new Bundle();
-                extras.putString(ContentResolver.SYNC_EXTRAS_ACCOUNT, account);
                 extras.putString("feed", feed);
-                context.getContentResolver().startSync(uri, extras);
+                ContentResolver.requestSync(account, authority, extras);
             }
         } finally {
             if (c != null) c.deactivate();
@@ -150,31 +148,35 @@
      */
     private void handleRefreshAlarm(Context context) {
         // retrieve the list of accounts from the subscribed feeds
-        ArrayList<String> accounts = new ArrayList<String>();
+        ArrayList<Account> accounts = Lists.newArrayList();
         ContentResolver contentResolver = context.getContentResolver();
         Cursor c = contentResolver.query(SubscribedFeeds.Accounts.CONTENT_URI,
                 sAccountProjection, null, null, null);
-        while (c.moveToNext()) {
-            String account = c.getString(0);
-            if (TextUtils.isEmpty(account)) {
-                continue;
+        try {
+            while (c.moveToNext()) {
+                String accountName = c.getString(0);
+                String accountType = c.getString(1);
+                accounts.add(new Account(accountName, accountType));
             }
-            accounts.add(account);
+        } finally {
+            c.close();
         }
-        c.deactivate();
 
         // Clear the auth tokens for all these accounts so that we are sure
         // they will still be valid until the next time we refresh them.
-        // TODO: add this when the google login service is done
+        // TODO(fredq): add this when the google login service is done
 
         // mark the feeds dirty, by setting the accounts to the same value,
         //  which will trigger a sync.
         try {
             ContentValues values = new ContentValues();
-            for (String account : accounts) {
-                values.put(SyncConstValue._SYNC_ACCOUNT, account);
+            for (Account account : accounts) {
+                values.put(SyncConstValue._SYNC_ACCOUNT, account.mName);
+                values.put(SyncConstValue._SYNC_ACCOUNT_TYPE, account.mType);
                 contentResolver.update(SubscribedFeeds.Feeds.CONTENT_URI, values,
-                        SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?", new String[] {account});
+                        SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=? AND "
+                                + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "=?",
+                        new String[] {account.mName, account.mType});
             }
         } catch (SQLiteFullException e) {
             Log.w(TAG, "disk full while trying to mark the feeds as dirty, skipping");
diff --git a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsProvider.java b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsProvider.java
index 9ecc3d6..d87f5e7 100644
--- a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsProvider.java
+++ b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsProvider.java
@@ -39,7 +39,7 @@
 public class SubscribedFeedsProvider extends AbstractSyncableContentProvider {
     private static final String TAG = "SubscribedFeedsProvider";
     private static final String DATABASE_NAME = "subscribedfeeds.db";
-    private static final int DATABASE_VERSION = 10;
+    private static final int DATABASE_VERSION = 11;
 
     private static final int FEEDS = 1;
     private static final int FEED_ID = 2;
@@ -88,6 +88,7 @@
         db.execSQL("CREATE TABLE feeds (" +
                     "_id INTEGER PRIMARY KEY," +
                     "_sync_account TEXT," + // From the sync source
+                    "_sync_account_type TEXT," + // From the sync source
                     "_sync_id TEXT," + // From the sync source
                     "_sync_time TEXT," + // From the sync source
                     "_sync_version TEXT," + // From the sync source
@@ -106,8 +107,8 @@
                     "WHEN old._sync_id is not null " +
                     "BEGIN " +
                         "INSERT INTO _deleted_feeds " +
-                            "(_sync_id, _sync_account, _sync_version) " +
-                            "VALUES (old._sync_id, old._sync_account, " +
+                            "(_sync_id, _sync_account, _sync_account_type, _sync_version) " +
+                            "VALUES (old._sync_id, old._sync_account, old._sync_account_type, " +
                             "old._sync_version);" +
                     "END");
 
@@ -116,6 +117,7 @@
                     "_sync_id TEXT," +
                     (isTemporary() ? "_sync_local_id INTEGER," : "") + // Used while syncing,
                     "_sync_account TEXT," +
+                    "_sync_account_type TEXT," +
                     "_sync_mark INTEGER, " + // Used to filter out new rows
                     "UNIQUE(_sync_id))");
     }
@@ -170,7 +172,8 @@
                 qb.setDistinct(true);
                 qb.setProjectionMap(ACCOUNTS_PROJECTION_MAP);
                 return qb.query(getDatabase(), projection, selection, selectionArgs,
-                        SubscribedFeeds.Feeds._SYNC_ACCOUNT, null, sortOrder);
+                        SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + ","
+                                + SubscribedFeeds.Feeds._SYNC_ACCOUNT, null, sortOrder);
             case FEED_ID:
                 qb.setTables(sFeedsTable);
                 qb.appendWhere(sFeedsTable + "._id=");
@@ -310,6 +313,8 @@
             DatabaseUtils.cursorStringToContentValues(diffsCursor,
                     SubscribedFeeds.Feeds._SYNC_ACCOUNT, mValues);
             DatabaseUtils.cursorStringToContentValues(diffsCursor,
+                    SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE, mValues);
+            DatabaseUtils.cursorStringToContentValues(diffsCursor,
                     SubscribedFeeds.Feeds._SYNC_VERSION, mValues);
             db.replace(mDeletedTable, SubscribedFeeds.Feeds._SYNC_ID, mValues);
         }
@@ -369,5 +374,7 @@
         ACCOUNTS_PROJECTION_MAP = map;
         map.put(SubscribedFeeds.Accounts._COUNT, "COUNT(*) AS _count");
         map.put(SubscribedFeeds.Accounts._SYNC_ACCOUNT, SubscribedFeeds.Accounts._SYNC_ACCOUNT);
+        map.put(SubscribedFeeds.Accounts._SYNC_ACCOUNT_TYPE,
+                SubscribedFeeds.Accounts._SYNC_ACCOUNT_TYPE);
     }
 }
diff --git a/preloaded-classes b/preloaded-classes
index 0520e41..3858883 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -1,7 +1,5 @@
 # Classes which are preloaded by com.android.internal.os.ZygoteInit.
 android.R$styleable
-android.accounts.AccountMonitor
-android.accounts.AccountMonitor$AccountUpdater
 android.app.Activity
 android.app.ActivityGroup
 android.app.ActivityManager$MemoryInfo$1
@@ -213,7 +211,6 @@
 android.media.AudioManager
 android.media.IAudioService$Stub
 android.media.IAudioService$Stub$Proxy
-android.media.MediaPlayer
 android.net.LocalSocket
 android.net.LocalSocketAddress
 android.net.LocalSocketAddress$Namespace
@@ -488,7 +485,6 @@
 android.webkit.JWebCoreJavaBridge
 android.webkit.LoadListener
 android.webkit.MimeTypeMap
-android.webkit.TextDialog
 android.webkit.URLUtil
 android.webkit.WebBackForwardList
 android.webkit.WebHistoryItem
@@ -504,14 +500,14 @@
 android.webkit.WebSettings$TextSize
 android.webkit.WebSyncManager
 android.webkit.WebSyncManager$SyncHandler
+android.webkit.WebTextView
 android.webkit.WebView
 android.webkit.WebView$ExtendedZoomControls
-android.webkit.WebView$FocusNode
 android.webkit.WebView$PrivateHandler
 android.webkit.WebViewCore
+android.webkit.WebViewCore$CursorData
 android.webkit.WebViewCore$EventHub
 android.webkit.WebViewCore$EventHub$1
-android.webkit.WebViewCore$FocusData
 android.webkit.WebViewCore$WebCoreThread
 android.webkit.WebViewCore$WebCoreThread$1
 android.webkit.WebViewDatabase
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 90d8c9d..5cdce5b 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -92,6 +92,9 @@
     // This should probably be exposed in the API, though it's not critical
     private static final int BATTERY_PLUGGED_NONE = 0;
 
+    private static final int BATTERY_LEVEL_CLOSE_WARNING = 20;
+    private static final int BATTERY_LEVEL_WARNING = 15;
+
     private final Context mContext;
     private final IBatteryStats mBatteryStats;
     
@@ -120,6 +123,7 @@
     private long mDischargeStartTime;
     private int mDischargeStartLevel;
     
+    private boolean mSentLowBatteryBroadcast = false;
     
     public BatteryService(Context context) {
         mContext = context;
@@ -260,6 +264,18 @@
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                 mContext.sendBroadcast(intent);
             }
+
+            final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
+            final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
+
+            /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
+             * - is just un-plugged (previously was plugged) and battery level is under WARNING, or
+             * - is not plugged and battery level crosses the WARNING boundary (becomes < 15).
+             */
+            final boolean sendBatteryLow = !plugged
+                && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+                && mBatteryLevel < BATTERY_LEVEL_WARNING
+                && (oldPlugged || mLastBatteryLevel >= BATTERY_LEVEL_WARNING);
             
             mLastBatteryStatus = mBatteryStatus;
             mLastBatteryHealth = mBatteryHealth;
@@ -269,8 +285,15 @@
             mLastBatteryVoltage = mBatteryVoltage;
             mLastBatteryTemperature = mBatteryTemperature;
             mLastBatteryLevelCritical = mBatteryLevelCritical;
-            
+
             sendIntent();
+            if (sendBatteryLow) {
+                mSentLowBatteryBroadcast = true;
+                mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_LOW));
+            } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= BATTERY_LEVEL_CLOSE_WARNING) {
+                mSentLowBatteryBroadcast = false;
+                mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_OKAY));
+            }
             
             // This needs to be done after sendIntent() so that we get the lastest battery stats.
             if (logOutlier && dischargeDuration != 0) {
diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java
index 7b8a2a4..9c1f942 100644
--- a/services/java/com/android/server/InputDevice.java
+++ b/services/java/com/android/server/InputDevice.java
@@ -63,7 +63,7 @@
             yMoveScale = my != 0 ? (1.0f/my) : 1.0f;
         }
         
-        MotionEvent generateMotion(InputDevice device, long curTime,
+        MotionEvent generateMotion(InputDevice device, long curTime, long curTimeNano,
                 boolean isAbs, Display display, int orientation,
                 int metaState) {
             if (!changed) {
@@ -167,7 +167,7 @@
                 if (!isAbs) {
                     x = y = 0;
                 }
-                return MotionEvent.obtain(downTime, curTime, action,
+                return MotionEvent.obtainNano(downTime, curTime, curTimeNano, action,
                         scaledX, scaledY, scaledPressure, scaledSize, metaState,
                         xPrecision, yPrecision, device.id, edgeFlags);
             } else {
@@ -181,7 +181,7 @@
                     }
                     return null;
                 }
-                MotionEvent me = MotionEvent.obtain(downTime, curTime,
+                MotionEvent me = MotionEvent.obtainNano(downTime, curTime, curTimeNano,
                         MotionEvent.ACTION_MOVE, scaledX, scaledY,
                         scaledPressure, scaledSize, metaState,
                         xPrecision, yPrecision, device.id, edgeFlags);
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
index 411cd6b..78cdf8b 100644
--- a/services/java/com/android/server/KeyInputQueue.java
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -18,8 +18,9 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.os.SystemClock;
+import android.os.LatencyTimer;
 import android.os.PowerManager;
+import android.os.SystemClock;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Display;
@@ -73,14 +74,17 @@
     public static final int FILTER_REMOVE = 0;
     public static final int FILTER_KEEP = 1;
     public static final int FILTER_ABORT = -1;
-    
+
+    private static final boolean MEASURE_LATENCY = false;
+    private LatencyTimer lt;
+
     public interface FilterCallback {
         int filterEvent(QueuedEvent ev);
     }
     
     static class QueuedEvent {
         InputDevice inputDevice;
-        long when;
+        long whenNano;
         int flags; // From the raw event
         int classType; // One of the class constants in InputEvent
         Object event;
@@ -88,7 +92,7 @@
 
         void copyFrom(QueuedEvent that) {
             this.inputDevice = that.inputDevice;
-            this.when = that.when;
+            this.whenNano = that.whenNano;
             this.flags = that.flags;
             this.classType = that.classType;
             this.event = that.event;
@@ -107,6 +111,10 @@
     }
 
     KeyInputQueue(Context context) {
+        if (MEASURE_LATENCY) {
+            lt = new LatencyTimer(100, 1000);
+        }
+
         PowerManager pm = (PowerManager)context.getSystemService(
                                                         Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
@@ -241,7 +249,7 @@
                     
                     if (configChanged) {
                         synchronized (mFirst) {
-                            addLocked(di, SystemClock.uptimeMillis(), 0,
+                            addLocked(di, System.nanoTime(), 0,
                                     RawInputEvent.CLASS_CONFIGURATION_CHANGED,
                                     null);
                         }
@@ -256,6 +264,7 @@
                         // timebase as SystemClock.uptimeMillis().
                         //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
                         final long curTime = SystemClock.uptimeMillis();
+                        final long curTimeNano = System.nanoTime();
                         //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
                         
                         final int classes = di.classes;
@@ -276,7 +285,7 @@
                                 down = false;
                             }
                             int keycode = rotateKeyCodeLocked(ev.keycode);
-                            addLocked(di, curTime, ev.flags,
+                            addLocked(di, curTimeNano, ev.flags,
                                     RawInputEvent.CLASS_KEYBOARD,
                                     newKeyEvent(di, di.mDownTime, curTime, down,
                                             keycode, 0, scancode,
@@ -330,7 +339,7 @@
                                 }
                                 
                                 MotionEvent me;
-                                me = di.mAbs.generateMotion(di, curTime, true,
+                                me = di.mAbs.generateMotion(di, curTime, curTimeNano, true,
                                         mDisplay, mOrientation, mGlobalMetaState);
                                 if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x
                                         + " y=" + di.mAbs.y + " ev=" + me);
@@ -338,15 +347,15 @@
                                     if (WindowManagerPolicy.WATCH_POINTER) {
                                         Log.i(TAG, "Enqueueing: " + me);
                                     }
-                                    addLocked(di, curTime, ev.flags,
+                                    addLocked(di, curTimeNano, ev.flags,
                                             RawInputEvent.CLASS_TOUCHSCREEN, me);
                                 }
-                                me = di.mRel.generateMotion(di, curTime, false,
+                                me = di.mRel.generateMotion(di, curTime, curTimeNano, false,
                                         mDisplay, mOrientation, mGlobalMetaState);
                                 if (false) Log.v(TAG, "Relative: x=" + di.mRel.x
                                         + " y=" + di.mRel.y + " ev=" + me);
                                 if (me != null) {
-                                    addLocked(di, curTime, ev.flags,
+                                    addLocked(di, curTimeNano, ev.flags,
                                             RawInputEvent.CLASS_TRACKBALL, me);
                                 }
                             }
@@ -530,7 +539,7 @@
         }
     }
     
-    private QueuedEvent obtainLocked(InputDevice device, long when,
+    private QueuedEvent obtainLocked(InputDevice device, long whenNano,
             int flags, int classType, Object event) {
         QueuedEvent ev;
         if (mCacheCount == 0) {
@@ -542,7 +551,7 @@
             mCacheCount--;
         }
         ev.inputDevice = device;
-        ev.when = when;
+        ev.whenNano = whenNano;
         ev.flags = flags;
         ev.classType = classType;
         ev.event = event;
@@ -561,13 +570,13 @@
         }
     }
 
-    private void addLocked(InputDevice device, long when, int flags,
+    private void addLocked(InputDevice device, long whenNano, int flags,
             int classType, Object event) {
         boolean poke = mFirst.next == mLast;
 
-        QueuedEvent ev = obtainLocked(device, when, flags, classType, event);
+        QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
         QueuedEvent p = mLast.prev;
-        while (p != mFirst && ev.when < p.when) {
+        while (p != mFirst && ev.whenNano < p.whenNano) {
             p = p.prev;
         }
 
@@ -578,8 +587,15 @@
         ev.inQueue = true;
 
         if (poke) {
+            long time;
+            if (MEASURE_LATENCY) {
+                time = System.nanoTime();
+            }
             mFirst.notify();
             mWakeLock.acquire();
+            if (MEASURE_LATENCY) {
+                lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
+            }
         }
     }
 
diff --git a/services/java/com/android/server/MasterClearReceiver.java b/services/java/com/android/server/MasterClearReceiver.java
index 5a42e76..3c366da 100644
--- a/services/java/com/android/server/MasterClearReceiver.java
+++ b/services/java/com/android/server/MasterClearReceiver.java
@@ -30,8 +30,8 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (intent.getAction().equals("android.intent.action.GTALK_DATA_MESSAGE_RECEIVED")) {
-            if (!intent.getBooleanExtra("from_trusted_server", false)) {
+        if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
+            if (!intent.getBooleanExtra("android.intent.extra.from_trusted_server", false)) {
                 Log.w(TAG, "Ignoring master clear request -- not from trusted server.");
                 return;
             }
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 972aa98..857bfaa 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -1675,6 +1675,9 @@
         }
     }
 
+    /**
+     * @deprecated
+     */
     public void querySyncProviders(List outNames, List outInfo) {
         synchronized (mPackages) {
             Iterator<Map.Entry<String, PackageParser.Provider>> i
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3e4d5f9..c26ba5c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -32,12 +32,7 @@
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.media.AudioService;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
+import android.os.*;
 import android.provider.Contacts.People;
 import android.provider.Settings;
 import android.server.BluetoothA2dpService;
@@ -45,6 +40,7 @@
 import android.server.search.SearchManagerService;
 import android.util.EventLog;
 import android.util.Log;
+import android.accounts.AccountManagerService;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -120,6 +116,14 @@
 
             mContentResolver = context.getContentResolver();
 
+            try {
+                Log.i(TAG, "Starting Account Manager.");
+                ServiceManager.addService(Context.ACCOUNT_SERVICE,
+                        new AccountManagerService(context));
+            } catch (Throwable e) {
+                Log.e(TAG, "Failure starting Account Manager", e);
+            }
+
             Log.i(TAG, "Starting Content Manager.");
             ContentService.main(context,
                     factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL);
@@ -172,7 +176,7 @@
                 bluetooth = new BluetoothDeviceService(context);
                 bluetooth.init();
                 ServiceManager.addService(Context.BLUETOOTH_SERVICE, bluetooth);
-                bluetoothA2dp = new BluetoothA2dpService(context);
+                bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);
                 ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE,
                                           bluetoothA2dp);
 
@@ -413,19 +417,19 @@
     public static final int FACTORY_TEST_OFF = 0;
     public static final int FACTORY_TEST_LOW_LEVEL = 1;
     public static final int FACTORY_TEST_HIGH_LEVEL = 2;
-    
-    /** 
-     * This method is called from Zygote to initialize the system. This will cause the native 
+
+    /**
+     * This method is called from Zygote to initialize the system. This will cause the native
      * services (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call back
      * up into init2() to start the Android services.
-     */ 
+     */
     native public static void init1(String[] args);
 
     public static void main(String[] args) {
         // The system server has to run all of the time, so it needs to be
         // as efficient as possible with its memory usage.
         VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
-        
+
         System.loadLibrary("android_servers");
         init1(args);
     }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 5df88b2..e91798b 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1349,39 +1349,42 @@
                         level = 0;
                     }
 
+                    /*
+                     * The formatting of the results returned by
+                     * wpa_supplicant is intended to make the fields
+                     * line up nicely when printed,
+                     * not to make them easy to parse. So we have to
+                     * apply some heuristics to figure out which field
+                     * is the SSID and which field is the flags.
+                     */
+                    String ssid;
+                    String flags;
+                    if (result.length == 4) {
+                        if (result[3].charAt(0) == '[') {
+                            flags = result[3];
+                            ssid = "";
+                        } else {
+                            flags = "";
+                            ssid = result[3];
+                        }
+                    } else if (result.length == 5) {
+                        flags = result[3];
+                        ssid = result[4];
+                    } else {
+                        // Here, we must have 3 fields: no flags and ssid
+                        // set
+                        flags = "";
+                        ssid = "";
+                    }
+
                     // bssid is the hash key
                     scanResult = mScanResultCache.get(bssid);
                     if (scanResult != null) {
                         scanResult.level = level;
+                        scanResult.SSID = ssid;
+                        scanResult.capabilities = flags;
+                        scanResult.frequency = frequency;
                     } else {
-                        /*
-                         * The formatting of the results returned by
-                         * wpa_supplicant is intended to make the fields
-                         * line up nicely when printed,
-                         * not to make them easy to parse. So we have to
-                         * apply some heuristics to figure out which field
-                         * is the SSID and which field is the flags.
-                         */
-                        String ssid;
-                        String flags;
-                        if (result.length == 4) {
-                            if (result[3].charAt(0) == '[') {
-                                flags = result[3];
-                                ssid = "";
-                            } else {
-                                flags = "";
-                                ssid = result[3];
-                            }
-                        } else if (result.length == 5) {
-                            flags = result[3];
-                            ssid = result[4];
-                        } else {
-                            // Here, we must have 3 fields: no flags and ssid
-                            // set
-                            flags = "";
-                            ssid = "";
-                        }
-
                         // Do not add scan results that have no SSID set
                         if (0 < ssid.trim().length()) {
                             scanResult =
@@ -1443,14 +1446,17 @@
      * Set the number of radio frequency channels that are allowed to be used
      * in the current regulatory domain. This method should be used only
      * if the correct number of channels cannot be determined automatically
-     * for some reason. If the operation is successful, the new value is
+     * for some reason. If the operation is successful, the new value may be
      * persisted as a Secure setting.
      * @param numChannels the number of allowed channels. Must be greater than 0
      * and less than or equal to 16.
+     * @param persist {@code true} if the setting should be remembered.
      * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
      * {@code numChannels} is outside the valid range.
      */
-    public boolean setNumAllowedChannels(int numChannels) {
+    public boolean setNumAllowedChannels(int numChannels, boolean persist) {
+        Log.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+
+                " with persist set to "+persist);
         enforceChangePermission();
         /*
          * Validate the argument. We'd like to let the Wi-Fi driver do this,
@@ -1469,9 +1475,11 @@
             return false;
         }
 
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                               Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
-                               numChannels);
+        if (persist) {
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                                   Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
+                                   numChannels);
+        }
         mWifiStateTracker.setNumAllowedChannels(numChannels);
         return true;
     }
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 9bad153..8c3f61e 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -63,6 +63,7 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.LatencyTimer;
 import android.os.LocalPowerManager;
 import android.os.Looper;
 import android.os.Message;
@@ -134,6 +135,8 @@
     static final boolean DEBUG_STARTING_WINDOW = false;
     static final boolean DEBUG_REORDER = false;
     static final boolean SHOW_TRANSACTIONS = false;
+    static final boolean MEASURE_LATENCY = false;
+    static private LatencyTimer lt;
 
     static final boolean PROFILE_ORIENTATION = false;
     static final boolean BLUR = true;
@@ -502,6 +505,10 @@
 
     private WindowManagerService(Context context, PowerManagerService pm,
             boolean haveInputMethods) {
+        if (MEASURE_LATENCY) {
+            lt = new LatencyTimer(100, 1000);
+        }
+        
         mContext = context;
         mHaveInputMethods = haveInputMethods;
         mLimitedAlphaCompositing = context.getResources().getBoolean(
@@ -3773,7 +3780,7 @@
     private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
         long curTime = SystemClock.uptimeMillis();
 
-        if (eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT) {
+        if (eventType == TOUCH_EVENT || eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT) {
             if (mLastTouchEventType == eventType &&
                     (curTime - mLastUserActivityCallTime) < MIN_TIME_BETWEEN_USERACTIVITIES) {
                 return;
@@ -3833,8 +3840,16 @@
         if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Log.v(TAG,
                 "dispatchPointer " + ev);
 
+        if (MEASURE_LATENCY) {
+            lt.sample("3 Wait for last dispatch ", System.nanoTime() - qev.whenNano);
+        }
+
         Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
                 ev, true, false, pid, uid);
+        
+        if (MEASURE_LATENCY) {
+            lt.sample("3 Last dispatch finished ", System.nanoTime() - qev.whenNano);
+        }
 
         int action = ev.getAction();
 
@@ -3872,7 +3887,8 @@
         WindowState target = (WindowState)targetObj;
 
         final long eventTime = ev.getEventTime();
-
+        final long eventTimeNano = ev.getEventTimeNano();
+        
         //Log.i(TAG, "Sending " + ev + " to " + target);
 
         if (uid != 0 && uid != target.mSession.mUid) {
@@ -3889,6 +3905,10 @@
                 return INJECT_NO_PERMISSION;
             }
         }
+        
+        if (MEASURE_LATENCY) {
+            lt.sample("4 in dispatchPointer     ", System.nanoTime() - eventTimeNano);
+        }
 
         if ((target.mAttrs.flags &
                         WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
@@ -3972,6 +3992,10 @@
             }
         }
 
+        if (MEASURE_LATENCY) {
+            lt.sample("5 in dispatchPointer     ", System.nanoTime() - eventTimeNano);
+        }
+
         synchronized(mWindowMap) {
             if (qev != null && action == MotionEvent.ACTION_MOVE) {
                 mKeyWaiter.bindTargetWindowLocked(target,
@@ -4009,7 +4033,16 @@
             if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) {
                 Log.v(TAG, "Delivering pointer " + qev + " to " + target);
             }
+            
+            if (MEASURE_LATENCY) {
+                lt.sample("6 before svr->client ipc ", System.nanoTime() - eventTimeNano);
+            }
+
             target.mClient.dispatchPointer(ev, eventTime);
+
+            if (MEASURE_LATENCY) {
+                lt.sample("7 after  svr->client ipc ", System.nanoTime() - eventTimeNano);
+            }
             return INJECT_SUCCEEDED;
         } catch (android.os.RemoteException e) {
             Log.i(TAG, "WINDOW DIED during motion dispatch: " + target);
@@ -5178,7 +5211,7 @@
                 }
             }
         }
-    };
+    }
 
     public boolean detectSafeMode() {
         mSafeMode = mPolicy.detectSafeMode();
@@ -5243,9 +5276,13 @@
                 if (DEBUG_INPUT && ev != null) Log.v(
                         TAG, "Event: type=" + ev.classType + " data=" + ev.event);
 
+                if (MEASURE_LATENCY) {
+                    lt.sample("2 got event              ", System.nanoTime() - ev.whenNano);
+                }
+
                 try {
                     if (ev != null) {
-                        curTime = ev.when;
+                        curTime = SystemClock.uptimeMillis();
                         int eventType;
                         if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) {
                             eventType = eventType((MotionEvent)ev.event);
@@ -5256,17 +5293,29 @@
                             eventType = LocalPowerManager.OTHER_EVENT;
                         }
                         try {
-                            long now = SystemClock.uptimeMillis();
-
-                            if ((now - mLastBatteryStatsCallTime)
+                            if ((curTime - mLastBatteryStatsCallTime)
                                     >= MIN_TIME_BETWEEN_USERACTIVITIES) {
-                                mLastBatteryStatsCallTime = now;
+                                mLastBatteryStatsCallTime = curTime;
                                 mBatteryStats.noteInputEvent();
                             }
                         } catch (RemoteException e) {
                             // Ignore
                         }
-                        mPowerManager.userActivity(curTime, false, eventType, false);
+
+                        if (eventType != TOUCH_EVENT
+                                && eventType != LONG_TOUCH_EVENT
+                                && eventType != CHEEK_EVENT) {
+                            mPowerManager.userActivity(curTime, false,
+                                    eventType, false);
+                        } else if (mLastTouchEventType != eventType
+                                || (curTime - mLastUserActivityCallTime)
+                                >= MIN_TIME_BETWEEN_USERACTIVITIES) {
+                            mLastUserActivityCallTime = curTime;
+                            mLastTouchEventType = eventType;
+                            mPowerManager.userActivity(curTime, false,
+                                    eventType, false);
+                        }
+
                         switch (ev.classType) {
                             case RawInputEvent.CLASS_KEYBOARD:
                                 KeyEvent ke = (KeyEvent)ev.event;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6d04b6b..b4c7afc 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -316,6 +316,12 @@
     // Memory pages are 4K.
     static final int PAGE_SIZE = 4*1024;
     
+    // System property defining error report receiver for system apps
+    static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps";
+
+    // System property defining default error report receiver
+    static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default";
+
     // Corresponding memory levels for above adjustments.
     final int EMPTY_APP_MEM;
     final int HIDDEN_APP_MEM;
@@ -7960,27 +7966,62 @@
 
     private ComponentName getErrorReportReceiver(ProcessRecord app) {
         IPackageManager pm = ActivityThread.getPackageManager();
+
         try {
-            // was an installer package name specified when this app was
-            // installed?
-            String installerPackageName = pm.getInstallerPackageName(app.info.packageName);
-            if (installerPackageName == null) {
-                return null;
+            // look for receiver in the installer package
+            String candidate = pm.getInstallerPackageName(app.info.packageName);
+            ComponentName result = getErrorReportReceiver(pm, app.info.packageName, candidate);
+            if (result != null) {
+                return result;
             }
 
-            // is there an Activity in this package that handles ACTION_APP_ERROR?
-            Intent intent = new Intent(Intent.ACTION_APP_ERROR);
-            intent.setPackage(installerPackageName);
-            ResolveInfo info = pm.resolveIntent(intent, null, 0);
-            if (info == null || info.activityInfo == null) {
-                return null;
+            // if the error app is on the system image, look for system apps
+            // error receiver
+            if ((app.info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+                candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
+                result = getErrorReportReceiver(pm, app.info.packageName, candidate);
+                if (result != null) {
+                    return result;
+                }
             }
 
-            return new ComponentName(installerPackageName, info.activityInfo.name);
+            // if there is a default receiver, try that
+            candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
+            return getErrorReportReceiver(pm, app.info.packageName, candidate);
         } catch (RemoteException e) {
-            // will return null and no error report will be delivered
+            // should not happen
+            Log.e(TAG, "error talking to PackageManager", e);
+            return null;
         }
-        return null;
+    }
+
+    /**
+     * Return activity in receiverPackage that handles ACTION_APP_ERROR.
+     *
+     * @param pm PackageManager isntance
+     * @param errorPackage package which caused the error
+     * @param receiverPackage candidate package to receive the error
+     * @return activity component within receiverPackage which handles
+     * ACTION_APP_ERROR, or null if not found
+     */
+    private ComponentName getErrorReportReceiver(IPackageManager pm, String errorPackage,
+            String receiverPackage) throws RemoteException {
+        if (receiverPackage == null || receiverPackage.length() == 0) {
+            return null;
+        }
+
+        // break the loop if it's the error report receiver package that crashed
+        if (receiverPackage.equals(errorPackage)) {
+            return null;
+        }
+
+        Intent intent = new Intent(Intent.ACTION_APP_ERROR);
+        intent.setPackage(receiverPackage);
+        ResolveInfo info = pm.resolveIntent(intent, null, 0);
+        if (info == null || info.activityInfo == null) {
+            return null;
+        }
+        return new ComponentName(receiverPackage, info.activityInfo.name);
     }
 
     void makeAppNotRespondingLocked(ProcessRecord app,
@@ -8302,6 +8343,7 @@
                 report.crashInfo.throwFileName = trace.getFileName();
                 report.crashInfo.throwClassName = trace.getClassName();
                 report.crashInfo.throwMethodName = trace.getMethodName();
+                report.crashInfo.throwLineNumber = trace.getLineNumber();
             } else if (r.notResponding) {
                 report.type = ApplicationErrorReport.TYPE_ANR;
                 report.anrInfo = new ApplicationErrorReport.AnrInfo();
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index 7a8d4e5e..c8db95b 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -76,15 +76,8 @@
     private static StatusBarPolicy sInstance;
 
     // message codes for the handler
-    private static final int EVENT_DATA_CONN_STATE_CHANGED = 2;
-    private static final int EVENT_DATA_ACTIVITY = 3;
     private static final int EVENT_BATTERY_CLOSE = 4;
 
-    // indices into mBatteryThresholds
-    private static final int BATTERY_THRESHOLD_CLOSE_WARNING = 0;
-    private static final int BATTERY_THRESHOLD_WARNING = 1;
-    private static final int BATTERY_THRESHOLD_EMPTY = 2;
-
     private final Context mContext;
     private final StatusBarService mService;
     private final Handler mHandler = new StatusBarHandler();
@@ -99,16 +92,13 @@
     private IBinder mBatteryIcon;
     private IconData mBatteryData;
     private boolean mBatteryFirst = true;
-    private int mBatteryPlugged;
+    private boolean mBatteryPlugged;
     private int mBatteryLevel;
-    private int mBatteryThreshold = 0; // index into mBatteryThresholds
-    private int[] mBatteryThresholds = new int[] { 20, 15, -1 };
     private AlertDialog mLowBatteryDialog;
     private TextView mBatteryLevelTextView;
     private View mBatteryView;
     private int mBatteryViewSequence;
     private boolean mBatteryShowLowOnEndCall = false;
-    private boolean mSentLowBatteryBroadcast = false;
     private static final boolean SHOW_LOW_BATTERY_WARNING = true;
 
     // phone
@@ -363,6 +353,9 @@
             else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
                 updateClock();
             }
+            else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+                updateBattery(intent);
+            }
             else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
                 updateClock();
             }
@@ -377,8 +370,11 @@
             else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
                 updateSyncState(intent);
             }
-            else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
-                updateBattery(intent);
+            else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
+                onBatteryLow(intent);
+            }
+            else if (action.equals(Intent.ACTION_BATTERY_OKAY)) {
+                onBatteryOkay(intent);
             }
             else if (action.equals(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION) ||
                     action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION) ||
@@ -521,6 +517,8 @@
         filter.addAction(Intent.ACTION_TIME_CHANGED);
         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        filter.addAction(Intent.ACTION_BATTERY_LOW);
+        filter.addAction(Intent.ACTION_BATTERY_OKAY);
         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
         filter.addAction(Intent.ACTION_ALARM_CHANGED);
         filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
@@ -564,38 +562,22 @@
         //mService.setIconVisibility(mSyncFailingIcon, isFailing && !isActive);
     }
 
-    private void pickNextBatteryLevel(int level) {
-        final int N = mBatteryThresholds.length;
-        for (int i=0; i<N; i++) {
-            if (level >= mBatteryThresholds[i]) {
-                mBatteryThreshold = i;
-                break;
-            }
-        }
-        if (mBatteryThreshold >= N) {
-            mBatteryThreshold = N-1;
-        }
-    }
-
     private final void updateBattery(Intent intent) {
         mBatteryData.iconId = intent.getIntExtra("icon-small", 0);
         mBatteryData.iconLevel = intent.getIntExtra("level", 0);
         mService.updateIcon(mBatteryIcon, mBatteryData, null);
 
-        int plugged = intent.getIntExtra("plugged", 0);
+        boolean plugged = intent.getIntExtra("plugged", 0) != 0;
         int level = intent.getIntExtra("level", -1);
         if (false) {
             Log.d(TAG, "updateBattery level=" + level
                     + " plugged=" + plugged
                     + " mBatteryPlugged=" + mBatteryPlugged
                     + " mBatteryLevel=" + mBatteryLevel
-                    + " mBatteryThreshold=" + mBatteryThreshold
                     + " mBatteryFirst=" + mBatteryFirst);
         }
 
-        int oldPlugged = mBatteryPlugged;
-        int oldThreshold = mBatteryThreshold;
-        pickNextBatteryLevel(level);
+        boolean oldPlugged = mBatteryPlugged;
 
         mBatteryPlugged = plugged;
         mBatteryLevel = level;
@@ -617,41 +599,31 @@
         }
         */
         if (false) {
-            Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level
-                    + " mBatteryThreshold=" + mBatteryThreshold + " oldThreshold=" + oldThreshold);
+            Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
         }
-        if (plugged == 0
-                && ((oldPlugged != 0 && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING])
-                    || (mBatteryThreshold > oldThreshold
-                        && mBatteryThreshold > BATTERY_THRESHOLD_WARNING))) {
-            // Broadcast the low battery warning
-            mSentLowBatteryBroadcast = true;
-            mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_LOW));
+    }
 
-            if (SHOW_LOW_BATTERY_WARNING) {
-                if (false) {
-                    Log.d(TAG, "mPhoneState=" + mPhoneState
-                            + " mLowBatteryDialog=" + mLowBatteryDialog
-                            + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
-                }
+    private void onBatteryLow(Intent intent) {
+        if (SHOW_LOW_BATTERY_WARNING) {
+            if (false) {
+                Log.d(TAG, "mPhoneState=" + mPhoneState
+                      + " mLowBatteryDialog=" + mLowBatteryDialog
+                      + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
+            }
 
-                if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
-                    showLowBatteryWarning();
-                } else {
-                    mBatteryShowLowOnEndCall = true;
-                }
+            if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
+                showLowBatteryWarning();
+            } else {
+                mBatteryShowLowOnEndCall = true;
             }
-        } else if (mBatteryThreshold < BATTERY_THRESHOLD_WARNING) {
-            if (mSentLowBatteryBroadcast == true) {
-                mSentLowBatteryBroadcast = false;
-                mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_OKAY));
-            }
-            if (SHOW_LOW_BATTERY_WARNING) {
-                if (mLowBatteryDialog != null) {
-                    mLowBatteryDialog.dismiss();
-                    mBatteryShowLowOnEndCall = false;
-                }
-            }
+        }
+    }
+
+    private void onBatteryOkay(Intent intent) {
+        if (mLowBatteryDialog != null
+                && SHOW_LOW_BATTERY_WARNING) {
+            mLowBatteryDialog.dismiss();
+            mBatteryShowLowOnEndCall = false;
         }
     }
 
@@ -716,9 +688,11 @@
     private void showLowBatteryWarning() {
         closeLastBatteryView();
 
-        int level = mBatteryThresholds[mBatteryThreshold > 1 ? mBatteryThreshold - 1 : 0];
+        /* Show exact battery level.
+         * Add 1 because the text says "less than X%".
+         */
         CharSequence levelText = mContext.getString(
-                    com.android.internal.R.string.battery_low_percent_format, level);
+                    com.android.internal.R.string.battery_low_percent_format, mBatteryLevel + 1);
 
         if (mBatteryLevelTextView != null) {
             mBatteryLevelTextView.setText(levelText);
@@ -769,7 +743,7 @@
         }
         if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
             if (mBatteryShowLowOnEndCall) {
-                if (mBatteryPlugged == 0) {
+                if (!mBatteryPlugged) {
                     showLowBatteryWarning();
                 }
                 mBatteryShowLowOnEndCall = false;
diff --git a/services/jni/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp
index 1d66fb1..85d63c9 100644
--- a/services/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/jni/com_android_server_AlarmManagerService.cpp
@@ -19,8 +19,8 @@
 
 #include "JNIHelp.h"
 #include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
+#include <utils/Log.h>
+#include <utils/misc.h>
 
 #include <fcntl.h>
 #include <stdio.h>
diff --git a/services/jni/com_android_server_BatteryService.cpp b/services/jni/com_android_server_BatteryService.cpp
index 2524966..e4f001f 100644
--- a/services/jni/com_android_server_BatteryService.cpp
+++ b/services/jni/com_android_server_BatteryService.cpp
@@ -18,8 +18,8 @@
 
 #include "JNIHelp.h"
 #include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
+#include <utils/Log.h>
+#include <utils/misc.h>
 
 #include <fcntl.h>
 #include <stdio.h>
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index dbe8431..4ed0a5c6 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -990,6 +990,7 @@
      * as:
      *
      * <p><code>
+     * xxxxx
      * xxx-xxxx
      * xxx-xxx-xxxx
      * 1-xxx-xxx-xxxx
@@ -1003,7 +1004,11 @@
         if (length > "+1-nnn-nnn-nnnn".length()) {
             // The string is too long to be formatted
             return;
+        } else if (length <= 5) {
+            // The string is either a shortcode or too short to be formatted
+            return;
         }
+
         CharSequence saved = text.subSequence(0, length);
 
         // Strip the dashes first, as we're going to add them back
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index a26e729..f6665aa 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.SharedPreferences;
+import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
@@ -28,6 +29,7 @@
 import android.os.RegistrantList;
 import android.os.SystemProperties;
 import android.preference.PreferenceManager;
+import android.provider.Settings;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.text.TextUtils;
@@ -185,7 +187,7 @@
         this.mContext = context;
         mLooper = Looper.myLooper();
 
-        setLocaleByCarrier();
+        setPropertiesByCarrier();
 
         setUnitTestMode(unitTestMode);
 
@@ -450,10 +452,10 @@
     }
 
     /**
-     * Set the locale by matching the carrier string in
+     * Set the properties by matching the carrier string in
      * a string-array resource
      */
-    private void setLocaleByCarrier() {
+    private void setPropertiesByCarrier() {
         String carrier = SystemProperties.get("ro.carrier");
 
         if (null == carrier || 0 == carrier.length()) {
@@ -461,18 +463,36 @@
         }
 
         CharSequence[] carrierLocales = mContext.
-                getResources().getTextArray(R.array.carrier_locales);
+                getResources().getTextArray(R.array.carrier_properties);
 
-        for (int i = 0; i < carrierLocales.length-1; i+=2) {
+        for (int i = 0; i < carrierLocales.length; i+=3) {
             String c = carrierLocales[i].toString();
-            String l = carrierLocales[i+1].toString();
             if (carrier.equals(c)) {
+                String l = carrierLocales[i+1].toString();
+                int wifiChannels = 0;
+                try {
+                    wifiChannels = Integer.parseInt(
+                            carrierLocales[i+2].toString());
+                } catch (NumberFormatException e) { }
+
                 String language = l.substring(0, 2);
                 String country = "";
                 if (l.length() >=5) {
                     country = l.substring(3, 5);
                 }
                 setSystemLocale(language, country);
+
+                if (wifiChannels != 0) {
+                    try {
+                        Settings.Secure.getInt(mContext.getContentResolver(),
+                                Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS);
+                    } catch (Settings.SettingNotFoundException e) {
+                        // note this is not persisting
+                        WifiManager wM = (WifiManager)
+                                mContext.getSystemService(Context.WIFI_SERVICE);
+                        wM.setNumAllowedChannels(wifiChannels, false);
+                    }
+                }
                 return;
             }
         }
diff --git a/telephony/java/com/android/internal/telephony/gsm/MccTable.java b/telephony/java/com/android/internal/telephony/gsm/MccTable.java
index e18da56..22b1f4f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/MccTable.java
+++ b/telephony/java/com/android/internal/telephony/gsm/MccTable.java
@@ -35,6 +35,7 @@
         int smallestDigitsMnc;
         String timezone;
         String language;
+        int wifiChannelsAllowed;
 
         MccEntry(int mnc, String iso, int smallestDigitsMCC) {
             this(mnc, iso, smallestDigitsMCC, null);
@@ -45,11 +46,16 @@
         }
 
         MccEntry(int mnc, String iso, int smallestDigitsMCC, String timezone, String language) {
+            this(mnc, iso, smallestDigitsMCC, timezone, language, 0);
+        }
+
+        MccEntry(int mnc, String iso, int smallestDigitsMCC, String timezone, String language, int wifiChannels) {
             this.mcc = mnc;
             this.iso = iso;
             this.smallestDigitsMnc = smallestDigitsMCC;
             this.timezone = timezone;
             this.language = language;
+            this.wifiChannelsAllowed = wifiChannels;
         }
 
         public int compareTo(MccEntry o)
@@ -148,6 +154,23 @@
         }
     }
 
+    /**
+     * Given a GSM Mobile Country Code, returns
+     * the number of wifi channels allowed in that country.
+     * Returns 0 if unavailable
+     */
+    public static int wifiChannelsForMcc(int mcc) {
+        MccEntry entry;
+
+        entry = entryForMcc(mcc);
+
+        if (entry == null) {
+            return 0;
+        } else {
+            return entry.wifiChannelsAllowed;
+        }
+    }
+
     static {
         table = new ArrayList<MccEntry>(240);
 
@@ -169,7 +192,7 @@
          */
 
 		table.add(new MccEntry(202,"gr",2));	//Greece
-		table.add(new MccEntry(204,"nl",2,"Europe/Amsterdam","nl"));	//Netherlands (Kingdom of the)
+		table.add(new MccEntry(204,"nl",2,"Europe/Amsterdam","nl",13)); //Netherlands (Kingdom of the)
 		table.add(new MccEntry(206,"be",2));	//Belgium
 		table.add(new MccEntry(208,"fr",2,"Europe/Paris","fr"));	//France
 		table.add(new MccEntry(212,"mc",2));	//Monaco (Principality of)
@@ -183,11 +206,11 @@
 		table.add(new MccEntry(225,"va",2,"Europe/Rome","it"));	//Vatican City State
 		table.add(new MccEntry(226,"ro",2));	//Romania
 		table.add(new MccEntry(228,"ch",2,"Europe/Zurich","de"));	//Switzerland (Confederation of)
-		table.add(new MccEntry(230,"cz",2,"Europe/Prague","cs"));	//Czech Republic
+		table.add(new MccEntry(230,"cz",2,"Europe/Prague","cs", 13));	//Czech Republic
 		table.add(new MccEntry(231,"sk",2));	//Slovak Republic
-		table.add(new MccEntry(232,"at",2,"Europe/Vienna","de"));	//Austria
-		table.add(new MccEntry(234,"gb",2,"Europe/London","en"));	//United Kingdom of Great Britain and Northern Ireland
-		table.add(new MccEntry(235,"gb",2,"Europe/London","en"));	//United Kingdom of Great Britain and Northern Ireland
+		table.add(new MccEntry(232,"at",2,"Europe/Vienna","de", 13));   //Austria
+		table.add(new MccEntry(234,"gb",2,"Europe/London","en", 13));   //United Kingdom of Great Britain and Northern Ireland
+		table.add(new MccEntry(235,"gb",2,"Europe/London","en", 13));   //United Kingdom of Great Britain and Northern Ireland
 		table.add(new MccEntry(238,"dk",2));	//Denmark
 		table.add(new MccEntry(240,"se",2));	//Sweden
 		table.add(new MccEntry(242,"no",2));	//Norway
@@ -200,7 +223,7 @@
 		table.add(new MccEntry(257,"by",2));	//Belarus (Republic of)
 		table.add(new MccEntry(259,"md",2));	//Moldova (Republic of)
 		table.add(new MccEntry(260,"pl",2,"Europe/Warsaw"));	//Poland (Republic of)
-		table.add(new MccEntry(262,"de",2,"Europe/Berlin","de"));	//Germany (Federal Republic of)
+		table.add(new MccEntry(262,"de",2,"Europe/Berlin","de", 13));   //Germany (Federal Republic of)
 		table.add(new MccEntry(266,"gi",2));	//Gibraltar
 		table.add(new MccEntry(268,"pt",2));	//Portugal
 		table.add(new MccEntry(270,"lu",2));	//Luxembourg
@@ -219,15 +242,15 @@
 		table.add(new MccEntry(293,"sl",2));	//Slovenia (Republic of)
                 table.add(new MccEntry(294,"mk",2));   //The Former Yugoslav Republic of Macedonia
 		table.add(new MccEntry(295,"li",2));	//Liechtenstein (Principality of)
-		table.add(new MccEntry(302,"ca",2));	//Canada
+		table.add(new MccEntry(302,"ca",2, "", "", 11));        //Canada
 		table.add(new MccEntry(308,"pm",2));	//Saint Pierre and Miquelon (Collectivit territoriale de la Rpublique franaise)
-		table.add(new MccEntry(310,"us",3,"","en"));	//United States of America
-		table.add(new MccEntry(311,"us",3,"","en"));	//United States of America
-		table.add(new MccEntry(312,"us",3,"","en"));	//United States of America
-		table.add(new MccEntry(313,"us",3,"","en"));	//United States of America
-		table.add(new MccEntry(314,"us",3,"","en"));	//United States of America
-		table.add(new MccEntry(315,"us",3,"","en"));	//United States of America
-		table.add(new MccEntry(316,"us",3,"","en"));	//United States of America
+		table.add(new MccEntry(310,"us",3,"","en", 11));        //United States of America
+		table.add(new MccEntry(311,"us",3,"","en", 11));        //United States of America
+		table.add(new MccEntry(312,"us",3,"","en", 11));        //United States of America
+		table.add(new MccEntry(313,"us",3,"","en", 11));        //United States of America
+		table.add(new MccEntry(314,"us",3,"","en", 11));        //United States of America
+		table.add(new MccEntry(315,"us",3,"","en", 11));        //United States of America
+		table.add(new MccEntry(316,"us",3,"","en", 11));        //United States of America
 		table.add(new MccEntry(330,"pr",2));	//Puerto Rico
 		table.add(new MccEntry(332,"vi",2));	//United States Virgin Islands
 		table.add(new MccEntry(334,"mx",3));	//Mexico
@@ -283,8 +306,8 @@
 		table.add(new MccEntry(436,"tj",2));	//Tajikistan (Republic of)
 		table.add(new MccEntry(437,"kg",2));	//Kyrgyz Republic
 		table.add(new MccEntry(438,"tm",2));	//Turkmenistan
-		table.add(new MccEntry(440,"jp",2,"Asia/Tokyo","ja"));	//Japan
-		table.add(new MccEntry(441,"jp",2,"Asia/Tokyo","ja"));	//Japan
+		table.add(new MccEntry(440,"jp",2,"Asia/Tokyo","ja", 14));              //Japan
+		table.add(new MccEntry(441,"jp",2,"Asia/Tokyo","ja", 14));              //Japan
 		table.add(new MccEntry(450,"kr",2));	//Korea (Republic of)
 		table.add(new MccEntry(452,"vn",2));	//Viet Nam (Socialist Republic of)
 		table.add(new MccEntry(454,"hk",2));	//"Hong Kong, China"
@@ -298,12 +321,12 @@
 		table.add(new MccEntry(470,"bd",2));	//Bangladesh (People's Republic of)
 		table.add(new MccEntry(472,"mv",2));	//Maldives (Republic of)
 		table.add(new MccEntry(502,"my",2));	//Malaysia
-		table.add(new MccEntry(505,"au",2,"Australia/Sydney","en"));	//Australia
+		table.add(new MccEntry(505,"au",2,"Australia/Sydney","en", 11));        //Australia
 		table.add(new MccEntry(510,"id",2));	//Indonesia (Republic of)
 		table.add(new MccEntry(514,"tl",2));	//Democratic Republic of Timor-Leste
 		table.add(new MccEntry(515,"ph",2));	//Philippines (Republic of the)
 		table.add(new MccEntry(520,"th",2));	//Thailand
-		table.add(new MccEntry(525,"sg",2,"Singapore","en"));	//Singapore (Republic of)
+		table.add(new MccEntry(525,"sg",2,"Singapore","en", 11));               //Singapore (Republic of)
 		table.add(new MccEntry(528,"bn",2));	//Brunei Darussalam
 		table.add(new MccEntry(530,"nz",2,"Pacific/Auckland", "en"));	//New Zealand
 		table.add(new MccEntry(534,"mp",2));	//Northern Mariana Islands (Commonwealth of the)
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index b886410..65d3362 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -21,11 +21,13 @@
 import android.app.IActivityManager;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemProperties;
 import android.os.Registrant;
+import android.provider.Settings;
 import android.util.Log;
 import java.util.ArrayList;
 
@@ -513,6 +515,29 @@
         phone.setSystemLocale(language, country);
     }
 
+    /**
+     * If the number of allowed wifi channels has not been set, set it based on
+     * the MCC of the SIM.
+     * @param mcc Mobile Country Code of the SIM
+     */
+    private void setWifiChannelsFromMccIfNeeded(int mcc) {
+        int wifiChannels = MccTable.wifiChannelsForMcc(mcc);
+
+        if (wifiChannels != 0) {
+            Context context = phone.getContext();
+            // only set to this default if the user hasn't manually set it
+            try {
+                Settings.Secure.getInt(context.getContentResolver(),
+                        Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS);
+            } catch (Settings.SettingNotFoundException e) {
+                WifiManager wM = (WifiManager)
+                        context.getSystemService(Context.WIFI_SERVICE);
+                // don't persist
+                wM.setNumAllowedChannels(wifiChannels, false);
+            }
+        }
+    }
+
     //***** Overridden from Handler
     public void handleMessage(Message msg) {
         AsyncResult ar;
@@ -559,6 +584,7 @@
                 int mcc = Integer.parseInt(imsi.substring(0, 3));
                 setTimezoneFromMccIfNeeded(mcc);
                 setLocaleFromMccIfNeeded(mcc);
+                setWifiChannelsFromMccIfNeeded(mcc);
             break;
 
             case EVENT_GET_MBI_DONE:
diff --git a/test-runner/android/test/IsolatedContext.java b/test-runner/android/test/IsolatedContext.java
index 2866666..03d95b7 100644
--- a/test-runner/android/test/IsolatedContext.java
+++ b/test-runner/android/test/IsolatedContext.java
@@ -2,6 +2,8 @@
 
 import com.google.android.collect.Lists;
 
+import android.accounts.AccountManager;
+import android.accounts.OnAccountsUpdatedListener;
 import android.content.ContextWrapper;
 import android.content.ContentResolver;
 import android.content.Intent;
@@ -11,6 +13,8 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
 
 import java.util.List;
 
@@ -21,6 +25,7 @@
 public class IsolatedContext extends ContextWrapper {
 
     private ContentResolver mResolver;
+    private final MockAccountManager mMockAccountManager;
 
     private List<Intent> mBroadcastIntents = Lists.newArrayList();
 
@@ -28,6 +33,7 @@
             ContentResolver resolver, Context targetContext) {
         super(targetContext);
         mResolver = resolver;
+        mMockAccountManager = new MockAccountManager();
     }
 
     /** Returns the list of intents that were broadcast since the last call to this method. */
@@ -78,8 +84,21 @@
 
     @Override
     public Object getSystemService(String name) {
-        // No services exist in this context.
+        if (Context.ACCOUNT_SERVICE.equals(name)) {
+            return mMockAccountManager;
+        }
+        // No other services exist in this context.
         return null;
     }
 
+    private class MockAccountManager extends AccountManager {
+        public MockAccountManager() {
+            super(IsolatedContext.this, null /* IAccountManager */, null /* handler */);
+        }
+
+        public void addOnAccountsUpdatedListener(OnAccountsUpdatedListener listener,
+                Handler handler, boolean updateImmediately) {
+            // do nothing
+        }
+    }
 }
diff --git a/test-runner/android/test/RenamingDelegatingContext.java b/test-runner/android/test/RenamingDelegatingContext.java
index 3f64340..d780502 100644
--- a/test-runner/android/test/RenamingDelegatingContext.java
+++ b/test-runner/android/test/RenamingDelegatingContext.java
@@ -136,6 +136,11 @@
             return false;
         }
     }
+    
+    @Override
+    public File getDatabasePath(String name) {
+        return mFileContext.getDatabasePath(renamedFileName(name));
+    }
 
     @Override
     public String[] databaseList() {
diff --git a/test-runner/android/test/SyncBaseInstrumentation.java b/test-runner/android/test/SyncBaseInstrumentation.java
index 772d75c..a860bb3 100644
--- a/test-runner/android/test/SyncBaseInstrumentation.java
+++ b/test-runner/android/test/SyncBaseInstrumentation.java
@@ -19,9 +19,9 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.net.Uri;
+import android.accounts.Account;
 
 /**
  * If you would like to test sync a single provider with an
@@ -44,12 +44,12 @@
      * Syncs the specified provider.
      * @throws Exception
      */
-    protected void syncProvider(Uri uri, String account, String authority) throws Exception {
+    protected void syncProvider(Uri uri, String accountName, String authority) throws Exception {
         Bundle extras = new Bundle();
-        extras.putBoolean(ContentResolver.SYNC_EXTRAS_FORCE, true);
-        extras.putString(ContentResolver.SYNC_EXTRAS_ACCOUNT, account);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+        Account account = new Account(accountName, "com.google.GAIA");
 
-        mContentResolver.startSync(uri, extras);
+        ContentResolver.requestSync(account, authority, extras);
         long startTimeInMillis = SystemClock.elapsedRealtime();
         long endTimeInMillis = startTimeInMillis + MAX_TIME_FOR_SYNC_IN_MINS * 60000;
 
@@ -64,7 +64,7 @@
                 break;
             }
 
-            if (isSyncActive(account, authority)) {
+            if (ContentResolver.isSyncActive(account, authority)) {
                 counter = 0;
                 continue;
             }
@@ -73,24 +73,7 @@
     }
 
     protected void cancelSyncsandDisableAutoSync() {
-        try {
-            ContentResolver.getContentService().setListenForNetworkTickles(false);
-        } catch (RemoteException e) {
-        }
-        mContentResolver.cancelSync(null);
-    }
-
-    /**
-     * This method tests if any sync is active or not. Sync is considered to be active if the
-     * entry is in either the Pending or Active tables.
-     * @return
-     */
-    private boolean isSyncActive(String account, String authority) {
-        try {
-            return ContentResolver.getContentService().isSyncActive(account,
-                    authority);
-        } catch (RemoteException e) {
-            return false;
-        }
+        ContentResolver.setMasterSyncAutomatically(false);
+        ContentResolver.cancelSync(null /* all accounts */, null /* all authorities */);
     }
 }
diff --git a/test-runner/android/test/mock/MockContentProvider.java b/test-runner/android/test/mock/MockContentProvider.java
index d04fc44..74f86d8 100644
--- a/test-runner/android/test/mock/MockContentProvider.java
+++ b/test-runner/android/test/mock/MockContentProvider.java
@@ -18,7 +18,11 @@
 
 import android.content.ContentValues;
 import android.content.IContentProvider;
-import android.content.ISyncAdapter;
+import android.content.Entity;
+import android.content.EntityIterator;
+import android.content.ContentProviderResult;
+import android.content.ContentProviderOperation;
+import android.content.OperationApplicationException;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.database.CursorWindow;
@@ -30,6 +34,7 @@
 import android.os.ParcelFileDescriptor;
 
 import java.io.FileNotFoundException;
+import java.util.ArrayList;
 
 /**
  * Mock implementation of IContentProvider that does nothing.  All methods are non-functional and 
@@ -48,6 +53,10 @@
         return 0;
     }
 
+    public Uri insertEntity(Uri uri, Entity entities) throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
     @SuppressWarnings("unused")
     public IBulkCursor bulkQuery(Uri url, String[] projection, String selection,
             String[] selectionArgs, String sortOrder, IContentObserver observer, 
@@ -62,11 +71,6 @@
     }
 
     @SuppressWarnings("unused")
-    public ISyncAdapter getSyncAdapter() throws RemoteException {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("unused")
     public String getType(Uri url) throws RemoteException {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
@@ -87,19 +91,33 @@
             throws FileNotFoundException {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
-    
+
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
     @SuppressWarnings("unused")
     public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
             String sortOrder) throws RemoteException {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    public EntityIterator queryEntities(Uri url, String selection, String[] selectionArgs,
+            String sortOrder) throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
     @SuppressWarnings("unused")
     public int update(Uri url, ContentValues values, String selection, String[] selectionArgs)
             throws RemoteException {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    public int updateEntity(Uri uri, Entity entity) throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
     public IBinder asBinder() {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index d5cd6ef..63a177e 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -292,9 +292,6 @@
         throw new UnsupportedOperationException();
     }
     
-    /**
-     * @hide - to match hiding in superclass
-     */
     @Override
     public String getInstallerPackageName(String packageName) {
         throw new UnsupportedOperationException();
diff --git a/tests/AndroidTests/run_test.sh b/tests/AndroidTests/run_test.sh
index 0cdf63f..6c3710a 100755
--- a/tests/AndroidTests/run_test.sh
+++ b/tests/AndroidTests/run_test.sh
@@ -1,4 +1,4 @@
 framework=/system/framework
 bpath=$framework/core.jar:$framework/ext.jar:$framework/framework.jar:$framework/android.test.runner.jar
-adb shell exec dalvikvm  -Xbootclasspath:$bpath -cp system/app/AndroidTests.apk \
+adb shell exec dalvikvm  -Xbootclasspath:$bpath -cp system/app/AndroidTests.apk:data/app/AndroidTests.apk \
       com.android.internal.util.WithFramework junit.textui.TestRunner $*
diff --git a/tests/AndroidTests/src/com/android/unit_tests/accounts/AccountManagerServiceTest.java b/tests/AndroidTests/src/com/android/unit_tests/accounts/AccountManagerServiceTest.java
new file mode 100644
index 0000000..6b8e1f0
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/accounts/AccountManagerServiceTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.unit_tests.accounts;
+
+import android.test.AndroidTestCase;
+import android.test.RenamingDelegatingContext;
+import android.test.IsolatedContext;
+import android.test.mock.MockContext;
+import android.test.mock.MockContentResolver;
+import android.content.*;
+import android.accounts.Account;
+import android.accounts.AccountManagerService;
+import android.os.Bundle;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
+
+public class AccountManagerServiceTest extends AndroidTestCase {
+    @Override
+    protected void setUp() throws Exception {
+        final String filenamePrefix = "test.";
+        MockContentResolver resolver = new MockContentResolver();
+        RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
+                new MockContext(), // The context that most methods are delegated to
+                getContext(), // The context that file methods are delegated to
+                filenamePrefix);
+        Context context = new IsolatedContext(resolver, targetContextWrapper);
+        setContext(context);
+    }
+
+    public class AccountSorter implements Comparator<Account> {
+        public int compare(Account object1, Account object2) {
+            if (object1 == object2) return 0;
+            if (object1 == null) return 1;
+            if (object2 == null) return -1;
+            int result = object1.mType.compareTo(object2.mType);
+            if (result != 0) return result;
+            return object1.mName.compareTo(object2.mName);
+        }
+    }
+
+    public void testCheckAddAccount() throws Exception {
+        AccountManagerService ams = new AccountManagerService(getContext());
+        Account a11 = new Account("account1", "type1");
+        Account a21 = new Account("account2", "type1");
+        Account a31 = new Account("account3", "type1");
+        Account a12 = new Account("account1", "type2");
+        Account a22 = new Account("account2", "type2");
+        Account a32 = new Account("account3", "type2");
+        assertTrue(ams.addAccount(a11, "p11", null));
+        assertTrue(ams.addAccount(a12, "p12", null));
+        assertTrue(ams.addAccount(a21, "p21", null));
+        assertTrue(ams.addAccount(a22, "p22", null));
+        assertTrue(ams.addAccount(a31, "p31", null));
+        assertTrue(ams.addAccount(a32, "p32", null));
+
+        assertFalse("duplicate account insertion should fail", ams.addAccount(a32, "p", null));
+
+        Account[] accounts = ams.getAccounts();
+        Arrays.sort(accounts, new AccountSorter());
+        assertEquals(6, accounts.length);
+        assertEquals(a11, accounts[0]);
+        assertEquals(a21, accounts[1]);
+        assertEquals(a31, accounts[2]);
+        assertEquals(a12, accounts[3]);
+        assertEquals(a22, accounts[4]);
+        assertEquals(a32, accounts[5]);
+
+        accounts = ams.getAccountsByType("type1" );
+        Arrays.sort(accounts, new AccountSorter());
+        assertEquals(3, accounts.length);
+        assertEquals(a11, accounts[0]);
+        assertEquals(a21, accounts[1]);
+        assertEquals(a31, accounts[2]);
+
+        ams.removeAccount(a21);
+
+        accounts = ams.getAccountsByType("type1" );
+        Arrays.sort(accounts, new AccountSorter());
+        assertEquals(2, accounts.length);
+        assertEquals(a11, accounts[0]);
+        assertEquals(a31, accounts[1]);
+    }
+
+    public void testPasswords() throws Exception {
+        AccountManagerService ams = new AccountManagerService(getContext());
+        Account a11 = new Account("account1", "type1");
+        Account a12 = new Account("account1", "type2");
+        assertTrue(ams.addAccount(a11, "p11", null));
+        assertTrue(ams.addAccount(a12, "p12", null));
+
+        assertEquals("p11", ams.getPassword(a11));
+        assertEquals("p12", ams.getPassword(a12));
+
+        ams.setPassword(a11, "p11b");
+
+        assertEquals("p11b", ams.getPassword(a11));
+        assertEquals("p12", ams.getPassword(a12));
+    }
+
+    public void testUserdata() throws Exception {
+        AccountManagerService ams = new AccountManagerService(getContext());
+        Account a11 = new Account("account1", "type1");
+        Bundle u11 = new Bundle();
+        u11.putString("a", "a_a11");
+        u11.putString("b", "b_a11");
+        u11.putString("c", "c_a11");
+        Account a12 = new Account("account1", "type2");
+        Bundle u12 = new Bundle();
+        u12.putString("a", "a_a12");
+        u12.putString("b", "b_a12");
+        u12.putString("c", "c_a12");
+        assertTrue(ams.addAccount(a11, "p11", u11));
+        assertTrue(ams.addAccount(a12, "p12", u12));
+
+        assertEquals("a_a11", ams.getUserData(a11, "a"));
+        assertEquals("b_a11", ams.getUserData(a11, "b"));
+        assertEquals("c_a11", ams.getUserData(a11, "c"));
+        assertEquals("a_a12", ams.getUserData(a12, "a"));
+        assertEquals("b_a12", ams.getUserData(a12, "b"));
+        assertEquals("c_a12", ams.getUserData(a12, "c"));
+
+        ams.setUserData(a11, "b", "b_a11b");
+
+        assertEquals("a_a11", ams.getUserData(a11, "a"));
+        assertEquals("b_a11b", ams.getUserData(a11, "b"));
+        assertEquals("c_a11", ams.getUserData(a11, "c"));
+        assertEquals("a_a12", ams.getUserData(a12, "a"));
+        assertEquals("b_a12", ams.getUserData(a12, "b"));
+        assertEquals("c_a12", ams.getUserData(a12, "c"));
+    }
+
+    public void testAuthtokens() throws Exception {
+        AccountManagerService ams = new AccountManagerService(getContext());
+        Account a11 = new Account("account1", "type1");
+        Account a12 = new Account("account1", "type2");
+        assertTrue(ams.addAccount(a11, "p11", null));
+        assertTrue(ams.addAccount(a12, "p12", null));
+
+        ams.setAuthToken(a11, "att1", "a11_att1");
+        ams.setAuthToken(a11, "att2", "a11_att2");
+        ams.setAuthToken(a11, "att3", "a11_att3");
+        ams.setAuthToken(a12, "att1", "a12_att1");
+        ams.setAuthToken(a12, "att2", "a12_att2");
+        ams.setAuthToken(a12, "att3", "a12_att3");
+
+        assertEquals("a11_att1", ams.peekAuthToken(a11, "att1"));
+        assertEquals("a11_att2", ams.peekAuthToken(a11, "att2"));
+        assertEquals("a11_att3", ams.peekAuthToken(a11, "att3"));
+        assertEquals("a12_att1", ams.peekAuthToken(a12, "att1"));
+        assertEquals("a12_att2", ams.peekAuthToken(a12, "att2"));
+        assertEquals("a12_att3", ams.peekAuthToken(a12, "att3"));
+
+        ams.setAuthToken(a11, "att3", "a11_att3b");
+        ams.invalidateAuthToken(a12.mType, "a12_att2");
+
+        assertEquals("a11_att1", ams.peekAuthToken(a11, "att1"));
+        assertEquals("a11_att2", ams.peekAuthToken(a11, "att2"));
+        assertEquals("a11_att3b", ams.peekAuthToken(a11, "att3"));
+        assertEquals("a12_att1", ams.peekAuthToken(a12, "att1"));
+        assertNull(ams.peekAuthToken(a12, "att2"));
+        assertEquals("a12_att3", ams.peekAuthToken(a12, "att3"));
+
+        assertNull(ams.readAuthTokenFromDatabase(a12, "att2"));
+    }
+}
\ No newline at end of file
diff --git a/tests/CoreTests/android/content/SyncStorageEngineTest.java b/tests/CoreTests/android/content/SyncStorageEngineTest.java
index dee6e38..533338e 100644
--- a/tests/CoreTests/android/content/SyncStorageEngineTest.java
+++ b/tests/CoreTests/android/content/SyncStorageEngineTest.java
@@ -20,6 +20,7 @@
 import android.test.RenamingDelegatingContext;
 import android.test.mock.MockContext;
 import android.test.mock.MockContentResolver;
+import android.accounts.Account;
 
 public class SyncStorageEngineTest extends AndroidTestCase {
 
@@ -28,7 +29,7 @@
      * correcponding sync is finished. This can happen if the clock changes while we are syncing.
      */
     public void testPurgeActiveSync() throws Exception {
-        final String account = "a@example.com";
+        final Account account = new Account("a@example.com", "example.type");
         final String authority = "testprovider";
 
         MockContentResolver mockResolver = new MockContentResolver();
diff --git a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
index 577d384..ff73b32 100644
--- a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -309,6 +309,11 @@
         number.append("800-55512");
         PhoneNumberUtils.formatNanpNumber(number);
         assertEquals("800-555-12", number.toString());
+
+        number.clear();
+        number.append("46645");
+        PhoneNumberUtils.formatNanpNumber(number);
+        assertEquals("46645", number.toString());
     }
 
     @SmallTest
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
index a389461..39ce1f2 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
@@ -18,6 +18,7 @@
 
 import android.os.Handler;
 import android.os.Message;
+import android.webkit.WebStorage;
 
 import java.util.HashMap;
 
@@ -25,7 +26,7 @@
     
     private EventSender mEventSender;
     private LayoutTestController mLayoutTestController;
-    
+
     private static final int EVENT_DOM_LOG = 1;
     private static final int EVENT_FIRE_KBD = 2;
     private static final int EVENT_KEY_DOWN_1 = 3;
@@ -57,6 +58,8 @@
     private static final int LAYOUT_SET_WINDOW_KEY = 38;
     private static final int LAYOUT_TEST_REPAINT = 39;
     private static final int LAYOUT_WAIT_UNTIL_DONE = 40;
+    private static final int LAYOUT_DUMP_DATABASE_CALLBACKS = 41;
+    private static final int LAYOUT_SET_CAN_OPEN_WINDOWS = 42;
     
     CallbackProxy(EventSender eventSender, 
             LayoutTestController layoutTestController) {
@@ -190,6 +193,14 @@
         case LAYOUT_WAIT_UNTIL_DONE:
             mLayoutTestController.waitUntilDone();
             break;
+
+        case LAYOUT_DUMP_DATABASE_CALLBACKS:
+            mLayoutTestController.dumpDatabaseCallbacks();
+            break;
+
+        case LAYOUT_SET_CAN_OPEN_WINDOWS:
+            mLayoutTestController.setCanOpenWindows();
+            break;
         }
     }
 
@@ -325,4 +336,20 @@
         obtainMessage(LAYOUT_WAIT_UNTIL_DONE).sendToTarget();
     }
 
+    public void dumpDatabaseCallbacks() {
+        obtainMessage(LAYOUT_DUMP_DATABASE_CALLBACKS).sendToTarget();
+    }
+
+    public void clearAllDatabases() {
+        WebStorage.getInstance().deleteAllDatabases();
+    }
+
+    public void setDatabaseQuota(long quota) {
+        WebStorage.getInstance().setQuotaForOrigin("file://", quota);
+    }
+
+    public void setCanOpenWindows() {
+        obtainMessage(LAYOUT_SET_CAN_OPEN_WINDOWS).sendToTarget();
+    }
+
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 4f162b3..ede5197 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -83,7 +83,20 @@
     };
         
     static final String [] ignoreTestList = {
-        };
+        // RegExp is exponatal
+        "fast/regex/test1.html",
+        "fast/regex/slow.html",
+        // RegExp is too large, causing OOM
+        "fast/js/regexp-charclass-crash.html",
+        // The Android browser has no notion of private browsing.
+        "storage/private-browsing-readonly.html",
+        "storage/domstorage/localstorage/private-browsing-affects-storage.html",
+        "storage/domstorage/sessionstorage/private-browsing-affects-storage.html",
+        // Android layout tests are stored in "layout_tests". The following two
+        // tests expect "LayoutTests" in their output.
+        "storage/domstorage/localstorage/iframe-events.html",
+        "storage/domstorage/sessionstorage/iframe-events.html"
+    };
     
     static void fillIgnoreResultSet() {
         // need test plugin
@@ -195,11 +208,7 @@
         ignoreResultList.add("fast/loader/local-iFrame-source-from-local.html");
         // extra spacing because iFrames rendered next to each other on Apple
         ignoreResultList.add("fast/loader/opaque-base-url.html");
-        // RegExp is too large, causing OOM
-        ignoreResultList.add("fast/js/regexp-charclass-crash.html");
         ignoreResultList.add("fast/text/plain-text-line-breaks.html");
-        
-        
     }
     
     static void fillBugTable() {
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
index 6166dd0..e1d802a 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
@@ -58,5 +58,8 @@
     public void queueLoad(String Url, String frameTarget);
 
     public void setAcceptsEditing(boolean b);
-	
+
+    // For storage tests
+    public void dumpDatabaseCallbacks();
+    public void setCanOpenWindows();
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 0d22eca..1980ec2 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -34,6 +34,7 @@
 import android.webkit.SslErrorHandler;
 import android.webkit.WebChromeClient;
 import android.webkit.WebSettings;
+import android.webkit.WebStorage;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
 import android.widget.LinearLayout;
@@ -43,6 +44,8 @@
 import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
 import java.util.Vector;
 
 public class TestShellActivity extends Activity implements LayoutTestController {
@@ -96,48 +99,11 @@
         setContentView(contentView);
 
         mWebView = new WebView(this);
-        mWebView.getSettings().setJavaScriptEnabled(true);
-        mWebView.setWebChromeClient(mChromeClient);
-        mWebView.setWebViewClient(new WebViewClient(){
-
-            @Override
-            public void onPageFinished(WebView view, String url) {
-                Log.v(LOGTAG, "onPageFinished, url=" + url);
-                super.onPageFinished(view, url);
-            }
-
-            @Override
-            public void onPageStarted(WebView view, String url, Bitmap favicon) {
-                Log.v(LOGTAG, "onPageStarted, url=" + url);
-                super.onPageStarted(view, url, favicon);
-            }
-
-            @Override
-            public void onReceivedError(WebView view, int errorCode, String description,
-                    String failingUrl) {
-                Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode
-                        + ", desc=" + description + ", url=" + failingUrl);
-                super.onReceivedError(view, errorCode, description, failingUrl);
-            }
-
-            @Override
-            public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
-                    String host, String realm) {
-                handler.cancel();
-            }
-
-            @Override
-            public void onReceivedSslError(WebView view, SslErrorHandler handler,
-                    SslError error) {
-                handler.proceed();
-            }
-
-        });
         mEventSender = new WebViewEventSender(mWebView);
         mCallbackProxy = new CallbackProxy(mEventSender, this);
 
-        mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController");
-        mWebView.addJavascriptInterface(mCallbackProxy, "eventSender");
+        setupWebViewForLayoutTests(mWebView, mCallbackProxy);
+
         contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0f));
 
         mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
@@ -286,6 +252,12 @@
             if (mDialogStrings != null)
                 os.write(mDialogStrings.toString().getBytes());
             mDialogStrings = null;
+            if (mDatabaseCallbackStrings != null)
+                os.write(mDatabaseCallbackStrings.toString().getBytes());
+            mDatabaseCallbackStrings = null;
+            if (mConsoleMessages != null)
+                os.write(mConsoleMessages.toString().getBytes());
+            mConsoleMessages = null;
             if (webkitData != null)
                 os.write(webkitData.getBytes());
             os.flush();
@@ -434,6 +406,51 @@
         mWebView.invalidate();
     }
 
+    public void dumpDatabaseCallbacks() {
+        Log.v(LOGTAG, "dumpDatabaseCallbacks called.");
+        mDumpDatabaseCallbacks = true;
+    }
+
+    public void setCanOpenWindows() {
+        Log.v(LOGTAG, "setCanOpenWindows called.");
+        mCanOpenWindows = true;
+    }
+
+    private final WebViewClient mViewClient = new WebViewClient(){
+        @Override
+        public void onPageFinished(WebView view, String url) {
+            Log.v(LOGTAG, "onPageFinished, url=" + url);
+            super.onPageFinished(view, url);
+        }
+
+        @Override
+        public void onPageStarted(WebView view, String url, Bitmap favicon) {
+            Log.v(LOGTAG, "onPageStarted, url=" + url);
+            super.onPageStarted(view, url, favicon);
+        }
+
+        @Override
+        public void onReceivedError(WebView view, int errorCode, String description,
+                String failingUrl) {
+            Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode
+                    + ", desc=" + description + ", url=" + failingUrl);
+            super.onReceivedError(view, errorCode, description, failingUrl);
+        }
+
+        @Override
+        public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
+                String host, String realm) {
+            handler.cancel();
+        }
+
+        @Override
+        public void onReceivedSslError(WebView view, SslErrorHandler handler,
+                SslError error) {
+            handler.proceed();
+        }
+    };
+
+
     private final WebChromeClient mChromeClient = new WebChromeClient() {
         @Override
         public void onProgressChanged(WebView view, int newProgress) {
@@ -508,6 +525,72 @@
             result.confirm();
             return true;
         }
+
+        @Override
+        public void onExceededDatabaseQuota(String url_str,
+                String databaseIdentifier, long currentQuota,
+                WebStorage.QuotaUpdater callback) {
+            if (mDumpDatabaseCallbacks) {
+                if (mDatabaseCallbackStrings == null) {
+                    mDatabaseCallbackStrings = new StringBuffer();
+                }
+
+                String protocol = "";
+                String host = "";
+                int port = 0;
+
+                try {
+                    URL url = new URL(url_str);
+                    protocol = url.getProtocol();
+                    host = url.getHost();
+                    if (url.getPort() > -1) {
+                        port = url.getPort();
+                    }
+                } catch (MalformedURLException e) {}
+
+                String databaseCallbackString =
+                        "UI DELEGATE DATABASE CALLBACK: " +
+                        "exceededDatabaseQuotaForSecurityOrigin:{" + protocol +
+                        ", " + host + ", " + port + "} database:" +
+                        databaseIdentifier + "\n";
+                Log.v(LOGTAG, "LOG: "+databaseCallbackString);
+                mDatabaseCallbackStrings.append(databaseCallbackString);
+            }
+            // Give 5MB more quota.
+            callback.updateQuota(currentQuota + 1024 * 1024 * 5);
+        }
+
+        @Override
+        public void addMessageToConsole(String message, int lineNumber,
+                String sourceID) {
+            if (mConsoleMessages == null) {
+                mConsoleMessages = new StringBuffer();
+            }
+            String consoleMessage = "CONSOLE MESSAGE: line "
+                    + lineNumber +": "+ message +"\n";
+            mConsoleMessages.append(consoleMessage);
+            Log.v(LOGTAG, "LOG: "+consoleMessage);
+        }
+
+        @Override
+        public boolean onCreateWindow(WebView view, boolean dialog,
+                boolean userGesture, Message resultMsg) {
+            if (!mCanOpenWindows) {
+                return false;
+            }
+
+            // We never display the new window, just create the view and
+            // allow it's content to execute and be recorded by the test
+            // runner.
+
+            WebView newWindowView = new WebView(TestShellActivity.this);
+            setupWebViewForLayoutTests(newWindowView, mCallbackProxy);
+            WebView.WebViewTransport transport =
+                    (WebView.WebViewTransport) resultMsg.obj;
+            transport.setWebView(newWindowView);
+            resultMsg.sendToTarget();
+            return true;
+        }
     };
 
     private void resetTestStatus() {
@@ -516,9 +599,32 @@
         mTimedOut = false;
         mDumpTitleChanges = false;
         mRequestedWebKitData = false;
+        mDumpDatabaseCallbacks = false;
+        mCanOpenWindows = false;
         mEventSender.resetMouse();
     }
 
+    private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) {
+        if (webview == null) {
+            return;
+        }
+
+        WebSettings settings = webview.getSettings();
+        settings.setJavaScriptEnabled(true);
+        settings.setJavaScriptCanOpenWindowsAutomatically(true);
+        settings.setSupportMultipleWindows(true);
+        settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
+        settings.setDatabaseEnabled(true);
+        settings.setDatabasePath(getDir("databases",0).getAbsolutePath());
+        settings.setDomStorageEnabled(true);
+
+        webview.addJavascriptInterface(callbackProxy, "layoutTestController");
+        webview.addJavascriptInterface(callbackProxy, "eventSender");
+
+        webview.setWebChromeClient(mChromeClient);
+        webview.setWebViewClient(mViewClient);
+    }
+
     private WebView mWebView;
     private WebViewEventSender mEventSender;
     private AsyncHandler mHandler;
@@ -546,6 +652,10 @@
     private StringBuffer mDialogStrings;
     private boolean mKeepWebHistory;
     private Vector mWebHistory;
+    private boolean mDumpDatabaseCallbacks;
+    private StringBuffer mDatabaseCallbackStrings;
+    private StringBuffer mConsoleMessages;
+    private boolean mCanOpenWindows;
 
     static final String TIMEOUT_STR = "**Test timeout";
 
diff --git a/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java b/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java
index aa3d186..42c1e78 100644
--- a/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java
+++ b/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java
@@ -8,6 +8,7 @@
 import android.net.Uri;
 import android.test.AndroidTestCase;
 import android.text.TextUtils;
+import android.accounts.Account;
 
 import java.util.ArrayList;
 import java.util.Map;
@@ -26,7 +27,7 @@
     static final Uri TABLE_URI = Uri.withAppendedPath(CONTENT_URI, TABLE_NAME);
     static final Uri DELETED_TABLE_URI = Uri.withAppendedPath(CONTENT_URI, DELETED_TABLE_NAME);
 
-    private final String ACCOUNT = "account@goo.com";
+    private final Account ACCOUNT = new Account("account@goo.com", "example.type");
 
     private final ArrayList<Expectation> mExpectations = Lists.newArrayList();
 
@@ -65,25 +66,31 @@
         mExpectations.clear();
     }
 
-    ContentValues newValues(String data, String syncId, String syncAccount,
+    ContentValues newValues(String data, String syncId, Account syncAccount,
             String syncTime, String syncVersion, Long syncLocalId) {
         ContentValues values = new ContentValues();
         if (data != null) values.put("data", data);
         if (syncTime != null) values.put("_sync_time", syncTime);
         if (syncVersion != null) values.put("_sync_version", syncVersion);
         if (syncId != null) values.put("_sync_id", syncId);
-        if (syncAccount != null) values.put("_sync_account", syncAccount);
+        if (syncAccount != null) {
+            values.put("_sync_account", syncAccount.mName);
+            values.put("_sync_account_type", syncAccount.mType);
+        }
         values.put("_sync_local_id", syncLocalId);
         values.put("_sync_dirty", 0);
         return values;
     }
 
-    ContentValues newDeletedValues(String syncId, String syncAccount, String syncVersion,
+    ContentValues newDeletedValues(String syncId, Account syncAccount, String syncVersion,
             Long syncLocalId) {
         ContentValues values = new ContentValues();
         if (syncVersion != null) values.put("_sync_version", syncVersion);
         if (syncId != null) values.put("_sync_id", syncId);
-        if (syncAccount != null) values.put("_sync_account", syncAccount);
+        if (syncAccount != null) {
+            values.put("_sync_account", syncAccount.mName);
+            values.put("_sync_account_type", syncAccount.mType);
+        }
         if (syncLocalId != null) values.put("_sync_local_id", syncLocalId);
         return values;
     }
@@ -380,6 +387,7 @@
                     + "_sync_local_id INTEGER, "
                     + "_sync_dirty INTEGER NOT NULL DEFAULT 0, "
                     + "_sync_account TEXT, "
+                    + "_sync_account_type TEXT, "
                     + "_sync_mark INTEGER)");
 
             mDb.execSQL("CREATE TABLE deleted_items ("
@@ -388,6 +396,7 @@
                     + "_sync_id TEXT, "
                     + "_sync_local_id INTEGER, "
                     + "_sync_account TEXT, "
+                    + "_sync_account_type TEXT, "
                     + "_sync_mark INTEGER)");
         }
 
@@ -501,7 +510,7 @@
             throw new UnsupportedOperationException();
         }
 
-        public void onSyncStart(SyncContext context, String account) {
+        public void onSyncStart(SyncContext context, Account account) {
             throw new UnsupportedOperationException();
         }
 
@@ -509,7 +518,7 @@
             throw new UnsupportedOperationException();
         }
 
-        public String getSyncingAccount() {
+        public Account getSyncingAccount() {
             throw new UnsupportedOperationException();
         }
 
@@ -544,24 +553,24 @@
             throw new UnsupportedOperationException();
         }
 
-        protected void onAccountsChanged(String[] accountsArray) {
+        protected void onAccountsChanged(Account[] accountsArray) {
             throw new UnsupportedOperationException();
         }
 
-        protected void deleteRowsForRemovedAccounts(Map<String, Boolean> accounts, String table,
-                String accountColumnName) {
+        protected void deleteRowsForRemovedAccounts(Map<Account, Boolean> accounts, String table
+        ) {
             throw new UnsupportedOperationException();
         }
 
-        public void wipeAccount(String account) {
+        public void wipeAccount(Account account) {
             throw new UnsupportedOperationException();
         }
 
-        public byte[] readSyncDataBytes(String account) {
+        public byte[] readSyncDataBytes(Account account) {
             throw new UnsupportedOperationException();
         }
 
-        public void writeSyncDataBytes(String account, byte[] data) {
+        public void writeSyncDataBytes(Account account, byte[] data) {
             throw new UnsupportedOperationException();
         }
     }
diff --git a/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java b/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java
new file mode 100644
index 0000000..e9a43d7
--- /dev/null
+++ b/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+import junit.framework.TestCase;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+@SmallTest
+public class ContentProviderOperationTest extends TestCase {
+    private final static Uri sTestUri1 = Uri.parse("content://authority/blah");
+    private final static ContentValues sTestValues1;
+
+    private final static Class<ContentProviderOperation.Builder> CLASS_BUILDER =
+            ContentProviderOperation.Builder.class;
+    private final static Class<ContentProviderOperation> CLASS_OPERATION =
+            ContentProviderOperation.class;
+
+    static {
+        sTestValues1 = new ContentValues();
+        sTestValues1.put("a", 1);
+        sTestValues1.put("b", "two");
+    }
+
+    public void testInsert() throws OperationApplicationException {
+        ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+                .withValues(sTestValues1)
+                .build();
+        ContentProviderResult result = op1.apply(new TestContentProvider() {
+            public Uri insert(Uri uri, ContentValues values) {
+                assertEquals(sTestUri1.toString(), uri.toString());
+                assertEquals(sTestValues1.toString(), values.toString());
+                return uri.buildUpon().appendPath("19").build();
+            }
+        }, null, 0);
+        assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
+    }
+
+    public void testInsertNoValues() throws OperationApplicationException {
+        ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+                .build();
+        ContentProviderResult result = op1.apply(new TestContentProvider() {
+            public Uri insert(Uri uri, ContentValues values) {
+                assertEquals(sTestUri1.toString(), uri.toString());
+                assertNull(values);
+                return uri.buildUpon().appendPath("19").build();
+            }
+        }, null, 0);
+        assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
+    }
+
+    public void testInsertFailed() {
+        ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+                .withValues(sTestValues1)
+                .build();
+        try {
+            op1.apply(new TestContentProvider() {
+                public Uri insert(Uri uri, ContentValues values) {
+                    assertEquals(sTestUri1.toString(), uri.toString());
+                    assertEquals(sTestValues1.toString(), values.toString());
+                    return null;
+                }
+            }, null, 0);
+            fail("the apply should have thrown an OperationApplicationException");
+        } catch (OperationApplicationException e) {
+            // this is the expected case
+        }
+    }
+
+    public void testInsertWithBackRefs() throws OperationApplicationException {
+        ContentProviderResult[] previousResults = new ContentProviderResult[4];
+        previousResults[0] = new ContentProviderResult(100);
+        previousResults[1] = new ContentProviderResult(101);
+        previousResults[2] = new ContentProviderResult(102);
+        previousResults[3] = new ContentProviderResult(103);
+        ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+                .withValues(sTestValues1)
+                .withValueBackReference("a1", 3)
+                .withValueBackReference("a2", 1)
+                .build();
+        ContentProviderResult result = op1.apply(new TestContentProvider() {
+            public Uri insert(Uri uri, ContentValues values) {
+                assertEquals(sTestUri1.toString(), uri.toString());
+                ContentValues expected = new ContentValues(sTestValues1);
+                expected.put("a1", 103);
+                expected.put("a2", 101);
+                assertEquals(expected.toString(), values.toString());
+                return uri.buildUpon().appendPath("19").build();
+            }
+        }, previousResults, previousResults.length);
+        assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
+    }
+
+    public void testUpdate() throws OperationApplicationException {
+        ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+                .withValues(sTestValues1)
+                .build();
+        ContentProviderResult[] backRefs = new ContentProviderResult[2];
+        ContentProviderResult result = op1.apply(new TestContentProvider() {
+            public Uri insert(Uri uri, ContentValues values) {
+                assertEquals(sTestUri1.toString(), uri.toString());
+                assertEquals(sTestValues1.toString(), values.toString());
+                return uri.buildUpon().appendPath("19").build();
+            }
+        }, backRefs, 1);
+        assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
+    }
+
+    public void testValueBackRefs() {
+        ContentValues values = new ContentValues();
+        values.put("a", "in1");
+        values.put("a2", "in2");
+        values.put("b", "in3");
+        values.put("c", "in4");
+
+        ContentProviderResult[] previousResults = new ContentProviderResult[4];
+        previousResults[0] = new ContentProviderResult(100);
+        previousResults[1] = new ContentProviderResult(101);
+        previousResults[2] = new ContentProviderResult(102);
+        previousResults[3] = new ContentProviderResult(103);
+
+        ContentValues expectedValues = new ContentValues(values);
+        expectedValues.put("a1", "103");
+        expectedValues.put("a2", "101");
+        expectedValues.put("a3", "102");
+
+        ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+                .withValues(values)
+                .withValueBackReference("a1", 3)
+                .withValueBackReference("a2", 1)
+                .withValueBackReference("a3", 2)
+                .build();
+        ContentValues v2 = op1.resolveValueBackReferences(previousResults, previousResults.length);
+        assertEquals(expectedValues, v2);
+    }
+
+    public void testSelectionBackRefs() {
+        ContentProviderResult[] previousResults = new ContentProviderResult[4];
+        previousResults[0] = new ContentProviderResult(100);
+        previousResults[1] = new ContentProviderResult(101);
+        previousResults[2] = new ContentProviderResult(102);
+        previousResults[3] = new ContentProviderResult(103);
+
+        String[] selectionArgs = new String[]{"a", null, null, "b", null};
+
+        ContentProviderOperation op1 = ContentProviderOperation.newUpdate(sTestUri1)
+                .withSelectionBackReference(1, 3)
+                .withSelectionBackReference(2, 1)
+                .withSelectionBackReference(4, 2)
+                .withSelection("unused", selectionArgs)
+                .build();
+        String[] s2 = op1.resolveSelectionArgsBackReferences(
+                previousResults, previousResults.length);
+        assertEquals("a,103,101,b,102", TextUtils.join(",", s2));
+    }
+
+    public void testParcelingOperation() throws NoSuchFieldException, IllegalAccessException,
+            NoSuchMethodException, InvocationTargetException, InstantiationException {
+        Parcel parcel = Parcel.obtain();
+        ContentProviderOperation op1;
+        ContentProviderOperation op2;
+
+        HashMap<Integer, Integer> selArgsBackRef = new HashMap<Integer, Integer>();
+        selArgsBackRef.put(1, 2);
+        selArgsBackRef.put(3, 4);
+
+        ContentValues values = new ContentValues();
+        values.put("v1", "val1");
+        values.put("v2", "43");
+
+        ContentValues valuesBackRef = new ContentValues();
+        values.put("v3", "val3");
+        values.put("v4", "44");
+
+        try {
+            ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(
+                    Uri.parse("content://goo/bar"));
+
+            builderSetExpectedCount(builder, 42);
+            builderSetSelection(builder, "selection");
+            builderSetSelectionArgs(builder, new String[]{"a", "b"});
+            builderSetSelectionArgsBackReferences(builder, selArgsBackRef);
+            builderSetValues(builder, values);
+            builderSetValuesBackReferences(builder, valuesBackRef);
+
+            op1 = newOperationFromBuilder(builder);
+            op1.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel);
+
+            assertEquals(1 /* ContentProviderOperation.TYPE_INSERT */, operationGetType(op2));
+            assertEquals("content://goo/bar", operationGetUri(op2).toString());
+            assertEquals(Integer.valueOf(42), operationGetExpectedCount(op2));
+            assertEquals("selection", operationGetSelection(op2));
+            assertEquals(2, operationGetSelectionArgs(op2).length);
+            assertEquals("a", operationGetSelectionArgs(op2)[0]);
+            assertEquals("b", operationGetSelectionArgs(op2)[1]);
+            assertEquals(values, operationGetValues(op2));
+            assertEquals(valuesBackRef, operationGetValuesBackReferences(op2));
+            assertEquals(2, operationGetSelectionArgsBackReferences(op2).size());
+            assertEquals(Integer.valueOf(2), operationGetSelectionArgsBackReferences(op2).get(1));
+            assertEquals(Integer.valueOf(4), operationGetSelectionArgsBackReferences(op2).get(3));
+        } finally {
+            parcel.recycle();
+        }
+
+        try {
+            ContentProviderOperation.Builder builder = ContentProviderOperation.newUpdate(
+                    Uri.parse("content://goo/bar"));
+
+            builderSetSelectionArgsBackReferences(builder, selArgsBackRef);
+
+            op1 = newOperationFromBuilder(builder);
+            op1.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel);
+            assertEquals(2 /* ContentProviderOperation.TYPE_UPDATE */, operationGetType(op2));
+            assertEquals("content://goo/bar", operationGetUri(op2).toString());
+            assertNull(operationGetEntity(op2));
+            assertNull(operationGetExpectedCount(op2));
+            assertNull(operationGetSelection(op2));
+            assertNull(operationGetSelectionArgs(op2));
+            assertNull(operationGetValues(op2));
+            assertNull(operationGetValuesBackReferences(op2));
+            assertEquals(2, operationGetSelectionArgsBackReferences(op2).size());
+            assertEquals(Integer.valueOf(2), operationGetSelectionArgsBackReferences(op2).get(1));
+            assertEquals(Integer.valueOf(4), operationGetSelectionArgsBackReferences(op2).get(3));
+        } finally {
+            parcel.recycle();
+        }
+
+        try {
+            ContentProviderOperation.Builder builder = ContentProviderOperation.newDelete(
+                    Uri.parse("content://goo/bar"));
+
+            op1 = newOperationFromBuilder(builder);
+            op1.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel);
+            assertEquals(3 /* ContentProviderOperation.TYPE_DELETE */, operationGetType(op2));
+            assertEquals("content://goo/bar", operationGetUri(op2).toString());
+            assertNull(operationGetEntity(op2));
+            assertNull(operationGetExpectedCount(op2));
+            assertNull(operationGetSelection(op2));
+            assertNull(operationGetSelectionArgs(op2));
+            assertNull(operationGetValues(op2));
+            assertNull(operationGetValuesBackReferences(op2));
+            assertNull(operationGetSelectionArgsBackReferences(op2));
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    private static ContentProviderOperation newOperationFromBuilder(
+            ContentProviderOperation.Builder builder)
+            throws NoSuchMethodException, InstantiationException, IllegalAccessException,
+            InvocationTargetException {
+        final Constructor constructor = CLASS_OPERATION.getDeclaredConstructor(CLASS_BUILDER);
+        constructor.setAccessible(true);
+        return (ContentProviderOperation) constructor.newInstance(builder);
+    }
+
+    private void builderSetSelectionArgsBackReferences(
+            ContentProviderOperation.Builder builder, HashMap<Integer, Integer> selArgsBackRef)
+            throws NoSuchFieldException, IllegalAccessException {
+        Field field;
+        field = CLASS_BUILDER.getDeclaredField("mSelectionArgsBackReferences");
+        field.setAccessible(true);
+        field.set(builder, selArgsBackRef);
+    }
+
+    private void builderSetValuesBackReferences(
+            ContentProviderOperation.Builder builder, ContentValues valuesBackReferences)
+            throws NoSuchFieldException, IllegalAccessException {
+        Field field;
+        field = CLASS_BUILDER.getDeclaredField("mValuesBackReferences");
+        field.setAccessible(true);
+        field.set(builder, valuesBackReferences);
+    }
+
+    private void builderSetSelection(
+            ContentProviderOperation.Builder builder, String selection)
+            throws NoSuchFieldException, IllegalAccessException {
+        Field field;
+        field = CLASS_BUILDER.getDeclaredField("mSelection");
+        field.setAccessible(true);
+        field.set(builder, selection);
+    }
+
+    private void builderSetSelectionArgs(
+            ContentProviderOperation.Builder builder, String[] selArgs)
+            throws NoSuchFieldException, IllegalAccessException {
+        Field field;
+        field = CLASS_BUILDER.getDeclaredField("mSelectionArgs");
+        field.setAccessible(true);
+        field.set(builder, selArgs);
+    }
+
+    private void builderSetValues(
+            ContentProviderOperation.Builder builder, ContentValues values)
+            throws NoSuchFieldException, IllegalAccessException {
+        Field field;
+        field = CLASS_BUILDER.getDeclaredField("mValues");
+        field.setAccessible(true);
+        field.set(builder, values);
+    }
+
+    private void builderSetEntity(
+            ContentProviderOperation.Builder builder, Entity entity)
+            throws NoSuchFieldException, IllegalAccessException {
+        Field field;
+        field = CLASS_BUILDER.getDeclaredField("mEntity");
+        field.setAccessible(true);
+        field.set(builder, entity);
+    }
+
+    private void builderSetExpectedCount(
+            ContentProviderOperation.Builder builder, Integer expectedCount)
+            throws NoSuchFieldException, IllegalAccessException {
+        Field field;
+        field = CLASS_BUILDER.getDeclaredField("mExpectedCount");
+        field.setAccessible(true);
+        field.set(builder, expectedCount);
+    }
+
+    private int operationGetType(ContentProviderOperation operation)
+            throws NoSuchFieldException, IllegalAccessException {
+        final Field field = CLASS_OPERATION.getDeclaredField("mType");
+        field.setAccessible(true);
+        return field.getInt(operation);
+    }
+
+    private Uri operationGetUri(ContentProviderOperation operation)
+            throws NoSuchFieldException, IllegalAccessException {
+        final Field field = CLASS_OPERATION.getDeclaredField("mUri");
+        field.setAccessible(true);
+        return (Uri) field.get(operation);
+    }
+
+    private String operationGetSelection(ContentProviderOperation operation)
+            throws NoSuchFieldException, IllegalAccessException {
+        final Field field = CLASS_OPERATION.getDeclaredField("mSelection");
+        field.setAccessible(true);
+        return (String) field.get(operation);
+    }
+
+    private String[] operationGetSelectionArgs(ContentProviderOperation operation)
+            throws NoSuchFieldException, IllegalAccessException {
+        final Field field = CLASS_OPERATION.getDeclaredField("mSelectionArgs");
+        field.setAccessible(true);
+        return (String[]) field.get(operation);
+    }
+
+    private ContentValues operationGetValues(ContentProviderOperation operation)
+            throws NoSuchFieldException, IllegalAccessException {
+        final Field field = CLASS_OPERATION.getDeclaredField("mValues");
+        field.setAccessible(true);
+        return (ContentValues) field.get(operation);
+    }
+
+    private Entity operationGetEntity(ContentProviderOperation operation)
+            throws NoSuchFieldException, IllegalAccessException {
+        final Field field = CLASS_OPERATION.getDeclaredField("mEntity");
+        field.setAccessible(true);
+        return (Entity) field.get(operation);
+    }
+
+    private Integer operationGetExpectedCount(ContentProviderOperation operation)
+            throws NoSuchFieldException, IllegalAccessException {
+        final Field field = CLASS_OPERATION.getDeclaredField("mExpectedCount");
+        field.setAccessible(true);
+        return (Integer) field.get(operation);
+    }
+
+    private ContentValues operationGetValuesBackReferences(ContentProviderOperation operation)
+            throws NoSuchFieldException, IllegalAccessException {
+        final Field field = CLASS_OPERATION.getDeclaredField("mValuesBackReferences");
+        field.setAccessible(true);
+        return (ContentValues) field.get(operation);
+    }
+
+    private Map<Integer, Integer> operationGetSelectionArgsBackReferences(
+            ContentProviderOperation operation)
+            throws NoSuchFieldException, IllegalAccessException {
+        final Field field = CLASS_OPERATION.getDeclaredField("mSelectionArgsBackReferences");
+        field.setAccessible(true);
+        return (Map<Integer, Integer>) field.get(operation);
+    }
+
+    public void testParcelingResult() {
+        Parcel parcel = Parcel.obtain();
+        ContentProviderResult result1;
+        ContentProviderResult result2;
+        try {
+            result1 = new ContentProviderResult(Uri.parse("content://goo/bar"));
+            result1.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            result2 = ContentProviderResult.CREATOR.createFromParcel(parcel);
+            assertEquals("content://goo/bar", result2.uri.toString());
+            assertNull(result2.count);
+        } finally {
+            parcel.recycle();
+        }
+
+        parcel = Parcel.obtain();
+        try {
+            result1 = new ContentProviderResult(42);
+            result1.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            result2 = ContentProviderResult.CREATOR.createFromParcel(parcel);
+            assertEquals(Integer.valueOf(42), result2.count);
+            assertNull(result2.uri);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    static class TestContentProvider extends ContentProvider {
+        public boolean onCreate() {
+            throw new UnsupportedOperationException();
+        }
+
+        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+                String sortOrder) {
+            throw new UnsupportedOperationException();
+        }
+
+        public String getType(Uri uri) {
+            throw new UnsupportedOperationException();
+        }
+
+        public Uri insert(Uri uri, ContentValues values) {
+            throw new UnsupportedOperationException();
+        }
+
+        public int delete(Uri uri, String selection, String[] selectionArgs) {
+            throw new UnsupportedOperationException();
+        }
+
+        public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 3b96412..e8c7395 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -15,7 +15,7 @@
 #include <utils/String8.h>
 #include <utils/Vector.h>
 #include <utils/RefBase.h>
-#include <utils/ZipFile.h>
+#include "ZipFile.h"
 
 #include "Bundle.h"
 #include "SourcePos.h"
@@ -128,7 +128,9 @@
         {
             //printf("new AaptFile created %s\n", (const char*)sourceFile);
         }
-    virtual ~AaptFile() { }
+    virtual ~AaptFile() {
+        free(mData);
+    }
 
     const String8& getPath() const { return mPath; }
     const AaptGroupEntry& getGroupEntry() const { return mGroupEntry; }
@@ -444,7 +446,13 @@
     AaptSymbolEntry                                 mDefSymbol;
 };
 
-class ResourceTypeSet;
+class ResourceTypeSet : public RefBase,
+                        public KeyedVector<String8,sp<AaptGroup> >
+{
+public:
+    ResourceTypeSet();
+};
+
 
 /**
  * Asset hierarchy being operated on.
@@ -452,8 +460,8 @@
 class AaptAssets : public AaptDir
 {
 public:
-    AaptAssets() : AaptDir(String8(), String8()), mHaveIncludedAssets(false) { }
-    virtual ~AaptAssets() { }
+    AaptAssets() : AaptDir(String8(), String8()), mHaveIncludedAssets(false), mRes(NULL) { }
+    virtual ~AaptAssets() { delete mRes; }
 
     const String8& getPackage() const { return mPackage; }
     void setPackage(const String8& package) { mPackage = package; mSymbolsPrivatePackage = package; }
@@ -501,7 +509,7 @@
     
     inline KeyedVector<String8, sp<ResourceTypeSet> >* getResources() { return mRes; }
     inline void 
-        setResources(KeyedVector<String8, sp<ResourceTypeSet> >* res) { mRes = res; }
+        setResources(KeyedVector<String8, sp<ResourceTypeSet> >* res) { delete mRes; mRes = res; }
 
 private:
     String8 mPackage;
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index fdc859c..2d8973d 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -17,7 +17,10 @@
 	ResourceTable.cpp \
 	Images.cpp \
 	Resource.cpp \
-    SourcePos.cpp
+    SourcePos.cpp \
+    ZipEntry.cpp \
+    ZipFile.cpp
+
 
 LOCAL_CFLAGS += -Wno-format-y2k
 
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index a6fedf3..a671bd7 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -7,7 +7,10 @@
 #define __BUNDLE_H
 
 #include <stdlib.h>
-#include <utils.h>      // android
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index e04491d..c0ae592 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -8,8 +8,10 @@
 #include "ResourceTable.h"
 #include "XMLNode.h"
 
-#include <utils.h>
-#include <utils/ZipFile.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
 
 #include <fcntl.h>
 #include <errno.h>
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 0a4c68b..f2414dd 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -44,6 +44,9 @@
             }
             free(allocRows);
         }
+        free(info9Patch.xDivs);
+        free(info9Patch.yDivs);
+        free(info9Patch.colors);
     }
 
     png_uint_32 width;
@@ -833,6 +836,7 @@
     int i;
 
     png_unknown_chunk unknowns[1];
+    unknowns[0].data = NULL;
 
     png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * png_sizeof(png_bytep));
     if (outRows == (png_bytepp) 0) {
@@ -939,6 +943,7 @@
         free(outRows[i]);
     }
     free(outRows);
+    free(unknowns[0].data);
 
     png_get_IHDR(write_ptr, write_info, &width, &height,
        &bit_depth, &color_type, &interlace_type,
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 12a0445..882714c 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -6,8 +6,10 @@
 #include "Main.h"
 #include "Bundle.h"
 
-#include <utils.h>
-#include <utils/ZipFile.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
 
 #include <stdlib.h>
 #include <getopt.h>
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index 65c0a8a..34ca5e5 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -6,10 +6,13 @@
 #ifndef __MAIN_H
 #define __MAIN_H
 
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
 #include "Bundle.h"
 #include "AaptAssets.h"
-#include <utils/ZipFile.h>
+#include "ZipFile.h"
 
 extern int doVersion(Bundle* bundle);
 extern int doList(Bundle* bundle);
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index eb7d6f5..8424169 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -7,8 +7,10 @@
 #include "AaptAssets.h"
 #include "ResourceTable.h"
 
-#include <utils.h>
-#include <utils/ZipFile.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
 
 #include <sys/types.h>
 #include <dirent.h>
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 027e3ab..81db323 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -45,13 +45,6 @@
     }
 }
 
-class ResourceTypeSet : public RefBase,
-                        public KeyedVector<String8,sp<AaptGroup> >
-{
-public:
-    ResourceTypeSet();
-};
-
 ResourceTypeSet::ResourceTypeSet()
     :RefBase(),
      KeyedVector<String8,sp<AaptGroup> >()
@@ -1118,6 +1111,7 @@
                 printf("  Writing public definitions to %s.\n", bundle->getPublicOutputFile());
             }
             table.writePublicDefinitions(String16(assets->getPackage()), fp);
+            fclose(fp);
         }
 
         NOISY(
@@ -1135,7 +1129,6 @@
             return err;
         }
     }
-
     return err;
 }
 
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 2a85bc7..832ba6c 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -486,6 +486,7 @@
 XMLNode::XMLNode(const String8& filename)
     : mFilename(filename)
 {
+    memset(&mCharsValue, 0, sizeof(mCharsValue));
 }
 
 XMLNode::type XMLNode::getType() const
diff --git a/libs/utils/ZipEntry.cpp b/tools/aapt/ZipEntry.cpp
similarity index 99%
rename from libs/utils/ZipEntry.cpp
rename to tools/aapt/ZipEntry.cpp
index 96f9fc4..bed0333 100644
--- a/libs/utils/ZipEntry.cpp
+++ b/tools/aapt/ZipEntry.cpp
@@ -20,7 +20,7 @@
 
 #define LOG_TAG "zip"
 
-#include <utils/ZipEntry.h>
+#include "ZipEntry.h"
 #include <utils/Log.h>
 
 #include <stdio.h>
diff --git a/include/utils/ZipEntry.h b/tools/aapt/ZipEntry.h
similarity index 99%
rename from include/utils/ZipEntry.h
rename to tools/aapt/ZipEntry.h
index e4698df..7f721b4 100644
--- a/include/utils/ZipEntry.h
+++ b/tools/aapt/ZipEntry.h
@@ -22,7 +22,7 @@
 #ifndef __LIBS_ZIPENTRY_H
 #define __LIBS_ZIPENTRY_H
 
-#include "Errors.h"
+#include <utils/Errors.h>
 
 #include <stdlib.h>
 #include <stdio.h>
diff --git a/libs/utils/ZipFile.cpp b/tools/aapt/ZipFile.cpp
similarity index 91%
rename from libs/utils/ZipFile.cpp
rename to tools/aapt/ZipFile.cpp
index 6f27d17..62c9383 100644
--- a/libs/utils/ZipFile.cpp
+++ b/tools/aapt/ZipFile.cpp
@@ -20,10 +20,11 @@
 
 #define LOG_TAG "zip"
 
-#include <utils/ZipFile.h>
 #include <utils/ZipUtils.h>
 #include <utils/Log.h>
 
+#include "ZipFile.h"
+
 #include <zlib.h>
 #define DEF_MEM_LEVEL 8                // normally in zutil.h?
 
@@ -94,10 +95,10 @@
     }
     mZipFp = fopen(zipFileName, openflags);
     if (mZipFp == NULL) {
-		int err = errno;
-		LOGD("fopen failed: %d\n", err);
+        int err = errno;
+        LOGD("fopen failed: %d\n", err);
         return errnoToStatus(err);
-	}
+    }
 
     status_t result;
     if (!newArchive) {
@@ -221,8 +222,8 @@
 
     buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch];
     if (buf == NULL) {
-		LOGD("Failure allocating %d bytes for EOCD search",
-			 EndOfCentralDir::kMaxEOCDSearch);
+        LOGD("Failure allocating %d bytes for EOCD search",
+             EndOfCentralDir::kMaxEOCDSearch);
         result = NO_MEMORY;
         goto bail;
     }
@@ -235,7 +236,7 @@
         readAmount = (long) fileLength;
     }
     if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
-		LOGD("Failure seeking to end of zip at %ld", (long) seekStart);
+        LOGD("Failure seeking to end of zip at %ld", (long) seekStart);
         result = UNKNOWN_ERROR;
         goto bail;
     }
@@ -265,9 +266,9 @@
     /* extract eocd values */
     result = mEOCD.readBuf(buf + i, readAmount - i);
     if (result != NO_ERROR) {
-		LOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
+        LOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
         goto bail;
-	}
+    }
     //mEOCD.dump();
 
     if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
@@ -293,8 +294,8 @@
      * we're hoping to preserve.
      */
     if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
-		LOGD("Failure seeking to central dir offset %ld\n",
-			 mEOCD.mCentralDirOffset);
+        LOGD("Failure seeking to central dir offset %ld\n",
+             mEOCD.mCentralDirOffset);
         result = UNKNOWN_ERROR;
         goto bail;
     }
@@ -743,61 +744,61 @@
     const void* data, size_t size, unsigned long* pCRC32)
 {
     status_t result = NO_ERROR;
-	const size_t kBufSize = 32768;
-	unsigned char* inBuf = NULL;
-	unsigned char* outBuf = NULL;
-	z_stream zstream;
+    const size_t kBufSize = 32768;
+    unsigned char* inBuf = NULL;
+    unsigned char* outBuf = NULL;
+    z_stream zstream;
     bool atEof = false;     // no feof() aviailable yet
-	unsigned long crc;
-	int zerr;
+    unsigned long crc;
+    int zerr;
 
-	/*
-	 * Create an input buffer and an output buffer.
-	 */
-	inBuf = new unsigned char[kBufSize];
-	outBuf = new unsigned char[kBufSize];
-	if (inBuf == NULL || outBuf == NULL) {
-		result = NO_MEMORY;
-		goto bail;
-	}
+    /*
+     * Create an input buffer and an output buffer.
+     */
+    inBuf = new unsigned char[kBufSize];
+    outBuf = new unsigned char[kBufSize];
+    if (inBuf == NULL || outBuf == NULL) {
+        result = NO_MEMORY;
+        goto bail;
+    }
 
-	/*
-	 * Initialize the zlib stream.
-	 */
-	memset(&zstream, 0, sizeof(zstream));
-	zstream.zalloc = Z_NULL;
-	zstream.zfree = Z_NULL;
-	zstream.opaque = Z_NULL;
-	zstream.next_in = NULL;
-	zstream.avail_in = 0;
-	zstream.next_out = outBuf;
-	zstream.avail_out = kBufSize;
-	zstream.data_type = Z_UNKNOWN;
+    /*
+     * Initialize the zlib stream.
+     */
+    memset(&zstream, 0, sizeof(zstream));
+    zstream.zalloc = Z_NULL;
+    zstream.zfree = Z_NULL;
+    zstream.opaque = Z_NULL;
+    zstream.next_in = NULL;
+    zstream.avail_in = 0;
+    zstream.next_out = outBuf;
+    zstream.avail_out = kBufSize;
+    zstream.data_type = Z_UNKNOWN;
 
-	zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION,
-		Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
-	if (zerr != Z_OK) {
-		result = UNKNOWN_ERROR;
-		if (zerr == Z_VERSION_ERROR) {
-			LOGE("Installed zlib is not compatible with linked version (%s)\n",
-				ZLIB_VERSION);
-		} else {
-			LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
-		}
-		goto bail;
-	}
+    zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION,
+        Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+    if (zerr != Z_OK) {
+        result = UNKNOWN_ERROR;
+        if (zerr == Z_VERSION_ERROR) {
+            LOGE("Installed zlib is not compatible with linked version (%s)\n",
+                ZLIB_VERSION);
+        } else {
+            LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
+        }
+        goto bail;
+    }
 
- 	crc = crc32(0L, Z_NULL, 0);
+    crc = crc32(0L, Z_NULL, 0);
 
-	/*
-	 * Loop while we have data.
-	 */
-	do {
-		size_t getSize;
-		int flush;
+    /*
+     * Loop while we have data.
+     */
+    do {
+        size_t getSize;
+        int flush;
 
-		/* only read if the input buffer is empty */
-		if (zstream.avail_in == 0 && !atEof) {
+        /* only read if the input buffer is empty */
+        if (zstream.avail_in == 0 && !atEof) {
             LOGV("+++ reading %d bytes\n", (int)kBufSize);
             if (data) {
                 getSize = size > kBufSize ? kBufSize : size;
@@ -817,54 +818,54 @@
                 atEof = true;
             }
 
-			crc = crc32(crc, inBuf, getSize);
+            crc = crc32(crc, inBuf, getSize);
 
-			zstream.next_in = inBuf;
-			zstream.avail_in = getSize;
-		}
+            zstream.next_in = inBuf;
+            zstream.avail_in = getSize;
+        }
 
-		if (atEof)
-			flush = Z_FINISH;       /* tell zlib that we're done */
-		else
-			flush = Z_NO_FLUSH;     /* more to come! */
+        if (atEof)
+            flush = Z_FINISH;       /* tell zlib that we're done */
+        else
+            flush = Z_NO_FLUSH;     /* more to come! */
 
-		zerr = deflate(&zstream, flush);
-		if (zerr != Z_OK && zerr != Z_STREAM_END) {
-			LOGD("zlib deflate call failed (zerr=%d)\n", zerr);
-			result = UNKNOWN_ERROR;
-			goto z_bail;
-		}
+        zerr = deflate(&zstream, flush);
+        if (zerr != Z_OK && zerr != Z_STREAM_END) {
+            LOGD("zlib deflate call failed (zerr=%d)\n", zerr);
+            result = UNKNOWN_ERROR;
+            goto z_bail;
+        }
 
-		/* write when we're full or when we're done */
-		if (zstream.avail_out == 0 ||
-			(zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize))
-		{
-			LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
+        /* write when we're full or when we're done */
+        if (zstream.avail_out == 0 ||
+            (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize))
+        {
+            LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
             if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) !=
                 (size_t)(zstream.next_out - outBuf))
             {
-				LOGD("write %d failed in deflate\n",
+                LOGD("write %d failed in deflate\n",
                     (int) (zstream.next_out - outBuf));
-				goto z_bail;
-			}
+                goto z_bail;
+            }
 
-			zstream.next_out = outBuf;
-			zstream.avail_out = kBufSize;
-		}
-	} while (zerr == Z_OK);
+            zstream.next_out = outBuf;
+            zstream.avail_out = kBufSize;
+        }
+    } while (zerr == Z_OK);
 
-	assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
+    assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
 
-	*pCRC32 = crc;
+    *pCRC32 = crc;
 
 z_bail:
-	deflateEnd(&zstream);        /* free up any allocated structures */
+    deflateEnd(&zstream);        /* free up any allocated structures */
 
 bail:
-	delete[] inBuf;
-	delete[] outBuf;
+    delete[] inBuf;
+    delete[] outBuf;
 
-	return result;
+    return result;
 }
 
 /*
@@ -1206,7 +1207,7 @@
 
 /*
  * ===========================================================================
- *		ZipFile::EndOfCentralDir
+ *      ZipFile::EndOfCentralDir
  * ===========================================================================
  */
 
diff --git a/include/utils/ZipFile.h b/tools/aapt/ZipFile.h
similarity index 98%
rename from include/utils/ZipFile.h
rename to tools/aapt/ZipFile.h
index 44df5bb..dbbd072 100644
--- a/include/utils/ZipFile.h
+++ b/tools/aapt/ZipFile.h
@@ -21,11 +21,12 @@
 #ifndef __LIBS_ZIPFILE_H
 #define __LIBS_ZIPFILE_H
 
-#include "ZipEntry.h"
-#include "Vector.h"
-#include "Errors.h"
+#include <utils/Vector.h>
+#include <utils/Errors.h>
 #include <stdio.h>
 
+#include "ZipEntry.h"
+
 namespace android {
 
 /*
diff --git a/tools/aidl/AST.cpp b/tools/aidl/AST.cpp
index 91802a9..1856cb9 100755
--- a/tools/aidl/AST.cpp
+++ b/tools/aidl/AST.cpp
@@ -841,23 +841,6 @@
         fprintf(to, "package %s;\n", this->package.c_str());
     }
 
-    // gather the types for the import statements
-    set<Type*> types;
-    N = this->classes.size();
-    for (i=0; i<N; i++) {
-        Class* c = this->classes[i];
-        c->GatherTypes(&types);
-    }
-    
-    set<Type*>::iterator it;
-    for (it=types.begin(); it!=types.end(); it++) {
-        Type* t = *it;
-        string pkg = t->Package();
-        if (pkg.length() != 0 && pkg != this->package) {
-            fprintf(to, "import %s;\n", t->ImportType().c_str());
-        }
-    }
-
     N = this->classes.size();
     for (i=0; i<N; i++) {
         Class* c = this->classes[i];
diff --git a/tools/aidl/generate_java.cpp b/tools/aidl/generate_java.cpp
index e3c0af0..da20d1f 100644
--- a/tools/aidl/generate_java.cpp
+++ b/tools/aidl/generate_java.cpp
@@ -1,6 +1,7 @@
 #include "generate_java.h"
 #include "AST.h"
 #include "Type.h"
+#include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -133,7 +134,7 @@
 
     Method* m = new Method;
         m->comment = "/**\n * Cast an IBinder object into an ";
-        m->comment += interfaceType->Name();
+        m->comment += interfaceType->QualifiedName();
         m->comment += " interface,\n";
         m->comment += " * generating a proxy if needed.\n */";
         m->modifiers = PUBLIC | STATIC;
@@ -323,7 +324,7 @@
     transactCodeName += method->name.data;
 
     char transactCodeValue[50];
-    sprintf(transactCodeValue, "(IBinder.FIRST_CALL_TRANSACTION + %d)", index);
+    sprintf(transactCodeValue, "(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)", index);
 
     Field* transactCode = new Field(STATIC | FINAL,
                             new Variable(INT_TYPE, transactCodeName));
@@ -517,7 +518,7 @@
                             new LiteralExpression("Stub." + transactCodeName),
                             _data, _reply ? _reply : NULL_VALUE,
                             new LiteralExpression(
-                                oneway ? "IBinder.FLAG_ONEWAY" : "0"));
+                                oneway ? "android.os.IBinder.FLAG_ONEWAY" : "0"));
     tryStatement->statements->Add(call);
 
     // throw back exceptions.
diff --git a/tools/aidl/options.h b/tools/aidl/options.h
index e9bf0f7..d88d988 100644
--- a/tools/aidl/options.h
+++ b/tools/aidl/options.h
@@ -1,6 +1,7 @@
 #ifndef DEVICE_TOOLS_AIDL_H
 #define DEVICE_TOOLS_AIDL_H
 
+#include <string.h>
 #include <string>
 #include <vector>
 
diff --git a/tools/localize/Perforce.cpp b/tools/localize/Perforce.cpp
index 1c644ed..ae11231 100644
--- a/tools/localize/Perforce.cpp
+++ b/tools/localize/Perforce.cpp
@@ -6,7 +6,10 @@
 #include <sstream>
 #include <sys/types.h>
 #include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/wait.h>
+#include <cstdio>
 
 using namespace std;
 
diff --git a/tools/localize/SourcePos.cpp b/tools/localize/SourcePos.cpp
index 2533f0a..184bfe0a 100644
--- a/tools/localize/SourcePos.cpp
+++ b/tools/localize/SourcePos.cpp
@@ -3,6 +3,7 @@
 #include <stdarg.h>
 #include <cstdio>
 #include <set>
+#include <cstdio>
 
 using namespace std;
 
diff --git a/tools/localize/XMLHandler.h b/tools/localize/XMLHandler.h
index 1130710..324385f 100644
--- a/tools/localize/XMLHandler.h
+++ b/tools/localize/XMLHandler.h
@@ -3,6 +3,7 @@
 
 #include "SourcePos.h"
 
+#include <algorithm>
 #include <string>
 #include <vector>
 #include <map>
diff --git a/tools/localize/file_utils.cpp b/tools/localize/file_utils.cpp
index 293e50e..775ce2f 100644
--- a/tools/localize/file_utils.cpp
+++ b/tools/localize/file_utils.cpp
@@ -8,6 +8,9 @@
 #include <sys/fcntl.h>
 #include <sys/stat.h>
 #include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <cstdio>
 #include "log.h"
 
 using namespace android;
diff --git a/tools/localize/file_utils.h b/tools/localize/file_utils.h
index 3b3fa21..7706587 100644
--- a/tools/localize/file_utils.h
+++ b/tools/localize/file_utils.h
@@ -4,6 +4,7 @@
 #include "ValuesFile.h"
 #include "Configuration.h"
 #include <string>
+#include <cstdio>
 
 using namespace std;
 
diff --git a/tools/localize/localize.cpp b/tools/localize/localize.cpp
index c0d84cc..68c03b6 100644
--- a/tools/localize/localize.cpp
+++ b/tools/localize/localize.cpp
@@ -15,6 +15,7 @@
 #include <sstream>
 #include <stdio.h>
 #include <string.h>
+#include <stdlib.h>
 
 using namespace std;
 
diff --git a/tools/localize/localize_test.cpp b/tools/localize/localize_test.cpp
index 678cad8..1d0ac9a 100644
--- a/tools/localize/localize_test.cpp
+++ b/tools/localize/localize_test.cpp
@@ -1,3 +1,4 @@
+#include <cstdio>
 #include "XLIFFFile.h"
 #include "ValuesFile.h"
 #include "localize.h"
diff --git a/tools/localize/merge_res_and_xliff_test.cpp b/tools/localize/merge_res_and_xliff_test.cpp
index e4ab562..6fe2629 100644
--- a/tools/localize/merge_res_and_xliff_test.cpp
+++ b/tools/localize/merge_res_and_xliff_test.cpp
@@ -1,3 +1,4 @@
+#include <cstdio>
 #include "merge_res_and_xliff.h"
 #include <stdio.h>
 
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 3d65d3c..c31577c 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -58,7 +58,7 @@
 
     int getNumAllowedChannels();
 
-    boolean setNumAllowedChannels(int numChannels);
+    boolean setNumAllowedChannels(int numChannels, boolean persist);
 
     int[] getValidChannelCounts();
     
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 5b8ced6..c4dff6a 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -538,14 +538,15 @@
      * for some reason.
      * @param numChannels the number of allowed channels. Must be greater than 0
      * and less than or equal to 16.
+     * @param persist {@code true} if you want this remembered
      * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
      * {@code numChannels} is out of range.
      *
      * @hide pending API council
      */
-    public boolean setNumAllowedChannels(int numChannels) {
+    public boolean setNumAllowedChannels(int numChannels, boolean persist) {
         try {
-            return mService.setNumAllowedChannels(numChannels);
+            return mService.setNumAllowedChannels(numChannels, persist);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 3851ac0..0920567 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -79,6 +79,8 @@
 
     public native static int getRssiCommand();
 
+    public native static int getRssiApproxCommand();
+
     public native static int getLinkSpeedCommand();
 
     public native static String getMacAddressCommand();
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 2fbc779..f84bccc 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -183,6 +183,10 @@
     private boolean mUseStaticIp = false;
     private int mReconnectCount;
 
+    // used to store the (non-persisted) num determined during device boot 
+    // (from mcc or other phone info) before the driver is started.
+    private int mNumAllowedChannels = 0;
+
     // Variables relating to the 'available networks' notification
     
     /**
@@ -571,9 +575,12 @@
         try {
             return setNumAllowedChannels(
                     Settings.Secure.getInt(mContext.getContentResolver(),
-                                           Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
+                    Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
         } catch (Settings.SettingNotFoundException e) {
-            // if setting doesn't exist, stick with the driver default
+            if (mNumAllowedChannels != 0) {
+                WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels);
+            }
+            // otherwise, use the driver default
         }
         return true;
     }
@@ -587,6 +594,7 @@
      * {@code numChannels} is outside the valid range.
      */
     public synchronized boolean setNumAllowedChannels(int numChannels) {
+        mNumAllowedChannels = numChannels;
         return WifiNative.setNumAllowedChannelsCommand(numChannels);
     }
 
@@ -1021,7 +1029,7 @@
 
             case EVENT_POLL_INTERVAL:
                 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
-                    requestPolledInfo(mWifiInfo);
+                    requestPolledInfo(mWifiInfo, true);
                     if (mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED) {
                         setPollTimer();
                     }
@@ -1268,7 +1276,7 @@
      */
     public WifiInfo requestConnectionInfo() {
         requestConnectionStatus(mWifiInfo);
-        requestPolledInfo(mWifiInfo);
+        requestPolledInfo(mWifiInfo, false);
         return mWifiInfo;
     }
 
@@ -1323,10 +1331,14 @@
      * Get the dynamic information that is not reported via events.
      * @param info the object into which the information should be captured.
      */
-    private synchronized void requestPolledInfo(WifiInfo info)
+    private synchronized void requestPolledInfo(WifiInfo info, boolean polling)
     {
         int newRssi = WifiNative.getRssiCommand();
-        if (newRssi != -1 && -200 < newRssi && newRssi < 100) { // screen out invalid values
+        if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
+            /* some implementations avoid negative values by adding 256
+             * so we need to adjust for that here.
+             */
+            if (newRssi > 0) newRssi -= 256;
             info.setRssi(newRssi);
             /*
              * Rather then sending the raw RSSI out every time it