Merge "docs: add docs for xhdpi and xlarge screen support update screen ranges figure bug: 3099462" into gingerbread
diff --git a/Android.mk b/Android.mk
index 1c8051b..83bdc0e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -435,7 +435,9 @@
 		-samplecode $(sample_dir)/WiktionarySimple \
 		            resources/samples/WiktionarySimple "Wiktionary (Simplified)" \
 		-samplecode $(sample_dir)/VoiceRecognitionService \
-		            resources/samples/VoiceRecognitionService "Voice Recognition Service"
+		            resources/samples/VoiceRecognitionService "Voice Recognition Service" \
+		-samplecode $(sample_dir)/NFCDemo \
+		            resources/samples/NFCDemo "NFC Demo"
 
 ## SDK version identifiers used in the published docs
   # major[.minor] version for current SDK. (full releases only)
diff --git a/api/9.xml b/api/9.xml
index 27629b7..3b9ab1a 100644
--- a/api/9.xml
+++ b/api/9.xml
@@ -93863,7 +93863,7 @@
  type="android.net.SSLCertificateSocketFactory"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="handshakeTimeoutMillis" type="int">
@@ -95963,7 +95963,7 @@
  type="android.net.http.SslCertificate"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="issuedTo" type="java.lang.String">
@@ -96030,7 +96030,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -96052,7 +96052,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -172464,7 +172464,7 @@
  abstract="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <constructor name="EventLogTags"
@@ -202795,7 +202795,7 @@
  synchronized="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -203341,7 +203341,7 @@
  synchronized="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="flag" type="boolean">
@@ -205466,7 +205466,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="view" type="android.webkit.WebView">
@@ -213999,7 +213999,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -224573,7 +224573,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
@@ -326646,7 +326646,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="toCopyIn" type="E[]">
+<parameter name="array" type="E[]">
 </parameter>
 </constructor>
 <method name="add"
@@ -326674,7 +326674,7 @@
 >
 <parameter name="index" type="int">
 </parameter>
-<parameter name="element" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="addAll"
@@ -326802,7 +326802,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="java.lang.Object">
+<parameter name="e" type="E">
+</parameter>
+<parameter name="index" type="int">
 </parameter>
 </method>
 <method name="indexOf"
@@ -326815,9 +326817,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="e" type="E">
-</parameter>
-<parameter name="index" type="int">
+<parameter name="o" type="java.lang.Object">
 </parameter>
 </method>
 <method name="isEmpty"
@@ -326852,7 +326852,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="java.lang.Object">
+<parameter name="e" type="E">
+</parameter>
+<parameter name="index" type="int">
 </parameter>
 </method>
 <method name="lastIndexOf"
@@ -326865,9 +326867,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="e" type="E">
-</parameter>
-<parameter name="index" type="int">
+<parameter name="o" type="java.lang.Object">
 </parameter>
 </method>
 <method name="listIterator"
@@ -326958,7 +326958,7 @@
 >
 <parameter name="index" type="int">
 </parameter>
-<parameter name="element" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="size"
@@ -411704,7 +411704,7 @@
  abstract="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <method name="getLength"
@@ -412193,7 +412193,7 @@
  abstract="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <method name="characters"
@@ -412408,7 +412408,7 @@
  abstract="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <implements name="org.xml.sax.DTDHandler">
@@ -412875,7 +412875,7 @@
  abstract="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <method name="parse"
@@ -414307,7 +414307,7 @@
  abstract="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <implements name="org.xml.sax.AttributeList">
@@ -415796,7 +415796,7 @@
  abstract="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <method name="makeParser"
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 127ed68..99a6843 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -47,6 +47,15 @@
     public static final String POWER_CPU_IDLE = "cpu.idle";
 
     /**
+     * Power consumption when CPU is awake (when a wake lock is held).  This
+     * should be 0 on devices that can go into full CPU power collapse even
+     * when a wake lock is held.  Otherwise, this is the power consumption in
+     * addition to POWERR_CPU_IDLE due to a wake lock being held but with no
+     * CPU activity.
+     */
+    public static final String POWER_CPU_AWAKE = "cpu.awake";
+
+    /**
      * Power consumption when CPU is in power collapse mode.
      */
     public static final String POWER_CPU_ACTIVE = "cpu.active";
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 45fd5a0..ec172bb 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -580,6 +580,7 @@
                 code->env->CallVoidMethod(code->clazz,
                         gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
                 checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent");
+                code->env->DeleteLocalRef(inputEventObj);
                 code->nativeInputQueue->finishEvent(keyEvent, true);
             }
             int seq;
@@ -589,6 +590,7 @@
                 code->env->CallVoidMethod(code->clazz,
                         gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
                 checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent");
+                code->env->DeleteLocalRef(inputEventObj);
             }
         } break;
         case CMD_FINISH: {
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 9d20dff..5b1d127 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -864,7 +864,7 @@
     <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"отмечено"</string>
     <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"не проверено"</string>
     <string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Одна или несколько программ требуют разрешения для доступа к вашему аккаунту сейчас и в дальнейшем."</string>
-    <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Разрешить этот запрос?"</string>
+    <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Разрешить доступ?"</string>
     <string name="grant_permissions_header_text" msgid="2722567482180797717">"Запрос доступа"</string>
     <string name="allow" msgid="7225948811296386551">"Разрешить"</string>
     <string name="deny" msgid="2081879885755434506">"Отклонить"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index f2ea744..976055d 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -549,7 +549,7 @@
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tryck på Menu om du vill låsa upp."</string>
     <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Rita grafiskt lösenord för att låsa upp"</string>
     <string name="lockscreen_emergency_call" msgid="5347633784401285225">"Nödsamtal"</string>
-    <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Återgå till samtalet"</string>
+    <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Tillbaka till samtal"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Korrekt!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Försök igen"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Laddar (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index f8d2b4b..2810fdd 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -599,7 +599,7 @@
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"寫入瀏覽器的記錄與書籤"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"允許應用程式修改儲存在電話上的瀏覽記錄或書籤。請注意:惡意應用程式可能會使用此選項來清除或修改您瀏覽器的資料。"</string>
     <string name="permlab_setAlarm" msgid="5924401328803615165">"在鬧鐘應用程式中設定鬧鈴"</string>
-    <string name="permdesc_setAlarm" msgid="5966966598149875082">"允許應用程式設定您所安裝的鬧鐘應用程式執行鬧鈴功能 (有些鬧鐘應用程式並不支援此功能)。"</string>
+    <string name="permdesc_setAlarm" msgid="5966966598149875082">"允許應用程式在安裝的鬧鐘應用程式中設定鬧鐘,某些鬧鐘應用程式可能無法執行這項功能。"</string>
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"修改瀏覽器地理資訊的權限"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"允許應用程式修改瀏覽器的地理位置權限,惡意應用程式可能會透過此方式允許將您的位置資訊任意傳送給某些網站。"</string>
     <string name="save_password_message" msgid="767344687139195790">"是否記住此密碼?"</string>
diff --git a/core/tests/ConnectivityManagerTest/assets/accesspoints.xml b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml
new file mode 100755
index 0000000..2b0e4af
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/assets/accesspoints.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <accesspoint>
+    <ssid>opennet</ssid>
+    <security>NONE</security>
+  </accesspoint>
+    <accesspoint>
+    <ssid>GoogleGuest</ssid>
+    <security>NONE</security>
+  </accesspoint>
+  <accesspoint>
+    <ssid>securenetdhcp</ssid>
+    <security>PSK</security>
+    <password>androidwifi</password>
+  </accesspoint>
+  <accesspoint>
+    <ssid>botnet</ssid>
+    <security>EAP</security>
+    <eap>PEAP</eap>
+    <phase2>MSCHAPV2</phase2>
+    <identity>donut</identity>
+    <password>android</password>
+  </accesspoint>
+</resources>
+
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
new file mode 100644
index 0000000..863fbe6
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2010, 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.connectivitymanagertest;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.AuthAlgorithm;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+
+import android.util.Log;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Help class to process configurations of access points saved in an XML file.
+ * The configurations of an access point is included in tag
+ * <accesspoint></accesspoint>. The supported configuration includes: ssid,
+ * security, eap, phase2, identity, password, anonymousidentity, cacert, usercert,
+ * in which each is included in the corresponding tags. All access points have to be
+ * enclosed in tags of <resources></resources>.
+ *
+ * The following is a sample configuration file for an access point using EAP-PEAP with MSCHAP2.
+ * <resources>
+ *   <accesspoint>
+ *   <ssid>testnet</ssid>
+ *   <security>EAP</security>
+ *   <eap>PEAP</eap>
+ *   <phase2>MSCHAP2</phase2>
+ *   <identity>donut</identity</identity>
+ *   <password>abcdefgh</password>
+ *   </accesspoint>
+ * </resources>
+ */
+public class AccessPointParserHelper {
+    private static final String KEYSTORE_SPACE = "keystore://";
+    private static final String TAG = "AccessPointParserHelper";
+    static final int NONE = 0;
+    static final int WEP = 1;
+    static final int PSK = 2;
+    static final int EAP = 3;
+
+    List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
+
+    private int getSecurityType (String security) {
+        if (security.equalsIgnoreCase("NONE")) {
+            return NONE;
+        } else if (security.equalsIgnoreCase("WEP")) {
+            return WEP;
+        } else if (security.equalsIgnoreCase("PSK")) {
+            return PSK;
+        } else if (security.equalsIgnoreCase("EAP")) {
+            return EAP;
+        } else {
+            return -1;
+        }
+    }
+
+    private boolean validateEapValue(String value) {
+        if (value.equalsIgnoreCase("PEAP") ||
+                value.equalsIgnoreCase("TLS") ||
+                value.equalsIgnoreCase("TTLS")) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    DefaultHandler mHandler = new DefaultHandler() {
+
+        boolean ssid = false;
+        boolean security = false;
+        boolean password = false;
+        boolean ip = false;
+        boolean subnetmask = false;
+        boolean gateway = false;
+        boolean dns = false;
+        boolean eap = false;
+        boolean phase2 = false;
+        boolean identity = false;
+        boolean anonymousidentity = false;
+        boolean cacert = false;
+        boolean usercert = false;
+        WifiConfiguration config = null;
+        int securityType = NONE;
+
+        @Override
+        public void startElement(String uri, String localName, String tagName,
+                Attributes attributes) throws SAXException {
+            if (tagName.equalsIgnoreCase("accesspoint")) {
+                config = new WifiConfiguration();
+            }
+            if (tagName.equalsIgnoreCase("ssid")) {
+                ssid = true;
+            }
+            if (tagName.equalsIgnoreCase("security")) {
+                security = true;
+            }
+            if (tagName.equalsIgnoreCase("password")) {
+                password = true;
+            }
+            if (tagName.equalsIgnoreCase("eap")) {
+                eap = true;
+            }
+            if (tagName.equalsIgnoreCase("phase2")) {
+                phase2 = true;
+            }
+            if (tagName.equalsIgnoreCase("identity")) {
+                identity = true;
+            }
+            if (tagName.equalsIgnoreCase("anonymousidentity")) {
+                anonymousidentity = true;
+            }
+            if (tagName.equalsIgnoreCase("cacert")) {
+                cacert = true;
+            }
+            if (tagName.equalsIgnoreCase("usercert")) {
+                usercert = true;
+            }
+        }
+
+        @Override
+        public void endElement(String uri, String localName, String tagName) throws SAXException {
+            Log.v(TAG, "endElement: " + tagName);
+            if (tagName.equalsIgnoreCase("accesspoint")) {
+                networks.add(config);
+            }
+        }
+
+        @Override
+        public void characters(char ch[], int start, int length) throws SAXException {
+            if (ssid) {
+                config.SSID = new String(ch, start, length);
+                Log.v(TAG, "ssid: " + config.SSID);
+                ssid = false;
+            }
+            if (security) {
+                String securityStr = (new String(ch, start, length)).toUpperCase();
+                Log.v(TAG, "security: " + securityStr);
+                securityType = getSecurityType(securityStr);
+                Log.v(TAG, "securityType = " + securityType);
+                switch (securityType) {
+                    case NONE:
+                        config.allowedKeyManagement.set(KeyMgmt.NONE);
+                        break;
+                    case WEP:
+                        config.allowedKeyManagement.set(KeyMgmt.NONE);
+                        config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
+                        config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
+                        break;
+                    case PSK:
+                        config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+                        break;
+                    case EAP:
+                        config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
+                        config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
+                        break;
+                    default:
+                        throw new SAXException();
+                }
+                security = false;
+            }
+            if (password) {
+                String passwordStr = new String(ch, start, length);
+                int len = passwordStr.length();
+                if (len == 0) {
+                    throw new SAXException();
+                }
+                Log.v(TAG, "passwordStr:" + passwordStr);
+                if (securityType == WEP) {
+                    if ((len == 10 || len == 26 || len == 58) &&
+                            passwordStr.matches("[0-9A-Fa-f]*")) {
+                        config.wepKeys[0] = passwordStr;
+                    } else {
+                        config.wepKeys[0] = '"' + passwordStr + '"';
+                    }
+                } else if (securityType == PSK) {
+                    if (passwordStr.matches("[0-9A-Fa-f]{64}")) {
+                        config.preSharedKey = passwordStr;
+                    } else {
+                        config.preSharedKey = '"' + passwordStr + '"';
+                    }
+                } else if (securityType == EAP) {
+                    config.password.setValue(passwordStr);
+                } else {
+                    throw new SAXException();
+                }
+                password = false;
+            }
+            if (eap) {
+                String eapValue = new String(ch, start, length);
+                if (!validateEapValue(eapValue)) {
+                    throw new SAXException();
+                }
+                config.eap.setValue(eapValue);
+                eap = false;
+            }
+            if (phase2) {
+                String phase2Value = new String(ch, start, length);
+                config.phase2.setValue("auth=" + phase2Value);
+                phase2 = false;
+            }
+            if (identity) {
+                String identityValue = new String(ch, start, length);
+                config.identity.setValue(identityValue);
+                identity = false;
+            }
+            if (anonymousidentity) {
+                String anonyId = new String(ch, start, length);
+                config.anonymous_identity.setValue(anonyId);
+                anonymousidentity = false;
+            }
+            if (cacert) {
+                String cacertValue = new String(ch, start, length);
+                // need to install the credentail to "keystore://"
+                config.ca_cert.setValue(KEYSTORE_SPACE);
+                cacert = false;
+            }
+            if (usercert) {
+                String usercertValue = new String(ch, start, length);
+                config.client_cert.setValue(KEYSTORE_SPACE);
+                usercert = false;
+            }
+        }
+    };
+
+    public AccessPointParserHelper() {
+    }
+
+    /**
+     * Process the accesspoint.xml file
+     * @return List of WifiConfiguration
+     * @throws Exception when parsing the XML file
+     */
+    public List<WifiConfiguration> processAccessPoint(InputStream in) throws Exception {
+        SAXParserFactory factory = SAXParserFactory.newInstance();
+        SAXParser saxParser = factory.newSAXParser();
+        saxParser.parse(in, mHandler);
+        return networks;
+    }
+}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index e42b657..7c46e7a 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -16,8 +16,10 @@
 
 package com.android.connectivitymanagertest;
 
+import com.android.connectivitymanagertest.R;
 import android.app.Activity;
 import android.content.Context;
+import android.content.res.Resources;
 import android.content.BroadcastReceiver;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -25,19 +27,22 @@
 import android.provider.Settings;
 import android.util.Log;
 import android.view.KeyEvent;
+
+import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.List;
 import android.widget.LinearLayout;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.State;
 
+import android.net.wifi.SupplicantState;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 
-
 /**
  * An activity registered with connectivity manager broadcast
  * provides network connectivity information and
@@ -46,8 +51,11 @@
 public class ConnectivityManagerTestActivity extends Activity {
 
     public static final String LOG_TAG = "ConnectivityManagerTestActivity";
-    public static final int WAIT_FOR_SCAN_RESULT = 5 * 1000; //5 seconds
+    public static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds
     public static final int WIFI_SCAN_TIMEOUT = 20 * 1000;
+    public static final int SHORT_TIMEOUT = 5 * 1000;
+    public static final long LONG_TIMEOUT = 50 * 1000;
+    private static final String ACCESS_POINT_FILE = "accesspoints.xml";
     public ConnectivityReceiver mConnectivityReceiver = null;
     public WifiReceiver mWifiReceiver = null;
     /*
@@ -175,6 +183,7 @@
         mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
         mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
         registerReceiver(mWifiReceiver, mIntentFilter);
 
         // Get an instance of ConnectivityManager
@@ -185,10 +194,26 @@
 
         if (mWifiManager.isWifiEnabled()) {
             Log.v(LOG_TAG, "Clear Wifi before we start the test.");
-            clearWifi();
+            removeConfiguredNetworksAndDisableWifi();
         }
      }
 
+    public List<WifiConfiguration> loadNetworkConfigurations() throws Exception {
+        InputStream in = getAssets().open(ACCESS_POINT_FILE);
+        AccessPointParserHelper parseHelper = new AccessPointParserHelper();
+        return parseHelper.processAccessPoint(in);
+    }
+
+    private void printNetConfig(String[] configuration) {
+        for (int i = 0; i < configuration.length; i++) {
+            if (i == 0) {
+                Log.v(LOG_TAG, "SSID: " + configuration[0]);
+            } else {
+                Log.v(LOG_TAG, "      " + configuration[i]);
+            }
+        }
+    }
+
     // for each network type, initialize network states to UNKNOWN, and no verification flag is set
     public void initializeNetworkStates() {
         for (int networkType = NUM_NETWORK_TYPES - 1; networkType >=0; networkType--) {
@@ -245,6 +270,68 @@
         }
     }
 
+    // Wait for network connectivity state: CONNECTING, CONNECTED, SUSPENDED,
+    //                                      DISCONNECTING, DISCONNECTED, UNKNOWN
+    public boolean waitForNetworkState(int networkType, State expectedState, long timeout) {
+        long startTime = System.currentTimeMillis();
+        while (true) {
+            if ((System.currentTimeMillis() - startTime) > timeout) {
+                if (mCM.getNetworkInfo(networkType).getState() != expectedState) {
+                    return false;
+                } else {
+                    // the broadcast has been sent out. the state has been changed.
+                    Log.v(LOG_TAG, "networktype: " + networkType + " state: " +
+                            mCM.getNetworkInfo(networkType));
+                    return true;
+                }
+            }
+            Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType +
+                    " to be " + expectedState.toString());
+            synchronized (connectivityObject) {
+                try {
+                    connectivityObject.wait(SHORT_TIMEOUT);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                if ((mNetworkInfo.getType() != networkType) ||
+                    (mNetworkInfo.getState() != expectedState)) {
+                    Log.v(LOG_TAG, "network state for " + mNetworkInfo.getType() +
+                            "is: " + mNetworkInfo.getState());
+                    continue;
+                }
+                return true;
+            }
+        }
+    }
+
+    // Wait for Wifi state: WIFI_STATE_DISABLED, WIFI_STATE_DISABLING, WIFI_STATE_ENABLED,
+    //                      WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
+    public boolean waitForWifiState(int expectedState, long timeout) {
+        long startTime = System.currentTimeMillis();
+        while (true) {
+            if ((System.currentTimeMillis() - startTime) > timeout) {
+                if (mWifiState != expectedState) {
+                    return false;
+                } else {
+                    return true;
+                }
+            }
+            Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState);
+            synchronized (wifiObject) {
+                try {
+                    wifiObject.wait(SHORT_TIMEOUT);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                if (mWifiState != expectedState) {
+                    Log.v(LOG_TAG, "Wifi state is: " + mWifiNetworkInfo.getState());
+                    continue;
+                }
+                return true;
+            }
+        }
+    }
+
     // Return true if device is currently connected to mobile network
     public boolean isConnectedToMobile() {
         return (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
@@ -265,6 +352,22 @@
      * We don't verify whether the connection is successful or not, leave this to the test
      */
     public boolean connectToWifi(String knownSSID) {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = knownSSID;
+        config.allowedKeyManagement.set(KeyMgmt.NONE);
+        return connectToWifiWithConfiguration(config);
+    }
+
+    /**
+     * Connect to Wi-Fi with the given configuration. Note the SSID in the configuration
+     * is pure string, we need to convert it to quoted string.
+     * @param config
+     * @return
+     */
+    public boolean connectToWifiWithConfiguration(WifiConfiguration config) {
+        String ssid = config.SSID;
+        config.SSID = convertToQuotedString(ssid);
+
         //If Wifi is not enabled, enable it
         if (!mWifiManager.isWifiEnabled()) {
             Log.v(LOG_TAG, "Wifi is not enabled, enable it");
@@ -273,6 +376,7 @@
 
         List<ScanResult> netList = mWifiManager.getScanResults();
         if (netList == null) {
+            Log.v(LOG_TAG, "scan results are null");
             // if no scan results are available, start active scan
             mWifiManager.startScanActive();
             mScanResultIsAvailable = false;
@@ -299,17 +403,20 @@
         }
 
         netList = mWifiManager.getScanResults();
+
         for (int i = 0; i < netList.size(); i++) {
             ScanResult sr= netList.get(i);
-            if (sr.SSID.equals(knownSSID)) {
-                Log.v(LOG_TAG, "found " + knownSSID + " in the scan result list");
-                WifiConfiguration config = new WifiConfiguration();
-                config.SSID = convertToQuotedString(sr.SSID);
-                config.allowedKeyManagement.set(KeyMgmt.NONE);
+            if (sr.SSID.equals(ssid)) {
+                Log.v(LOG_TAG, "found " + ssid + " in the scan result list");
                 int networkId = mWifiManager.addNetwork(config);
                 // Connect to network by disabling others.
                 mWifiManager.enableNetwork(networkId, true);
                 mWifiManager.saveConfiguration();
+                List<WifiConfiguration> wifiNetworks = mWifiManager.getConfiguredNetworks();
+                for (WifiConfiguration netConfig : wifiNetworks) {
+                    Log.v(LOG_TAG, netConfig.toString());
+                }
+
                 mWifiManager.reconnect();
                 break;
            }
@@ -317,14 +424,14 @@
 
         List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
         if (netConfList.size() <= 0) {
-            Log.v(LOG_TAG, knownSSID + " is not available");
+            Log.v(LOG_TAG, ssid + " is not available");
             return false;
         }
         return true;
     }
 
     /*
-     * Disconnect from the current AP
+     * Disconnect from the current AP and remove configured networks.
      */
     public boolean disconnectAP() {
         if (mWifiManager.isWifiEnabled()) {
@@ -360,9 +467,9 @@
     }
 
     /**
-     * Disconnect from the current Wifi and clear the configuration list
+     * Remove configured networks and disable wifi
      */
-    public boolean clearWifi() {
+    public boolean removeConfiguredNetworksAndDisableWifi() {
             if (!disconnectAP()) {
                 return false;
             }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
index 592be92..3d4dc3d 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
@@ -21,6 +21,7 @@
 import android.test.InstrumentationTestSuite;
 import android.util.Log;
 import com.android.connectivitymanagertest.functional.ConnectivityManagerMobileTest;
+import com.android.connectivitymanagertest.functional.WifiConnectionTest;
 
 import junit.framework.TestSuite;
 
@@ -38,6 +39,7 @@
     public TestSuite getAllTests() {
         TestSuite suite = new InstrumentationTestSuite(this);
         suite.addTestSuite(ConnectivityManagerMobileTest.class);
+        suite.addTestSuite(WifiConnectionTest.class);
         return suite;
     }
 
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
index ad8d444..5959cf3 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
@@ -41,8 +41,6 @@
     extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> {
     private static final String LOG_TAG = "ConnectivityManagerMobileTest";
     private static final String PKG_NAME = "com.android.connectivitymanagertest";
-    private static final long STATE_TRANSITION_SHORT_TIMEOUT = 5 * 1000;
-    private static final long STATE_TRANSITION_LONG_TIMEOUT = 30 * 1000;
 
     private String TEST_ACCESS_POINT;
     private ConnectivityManagerTestActivity cmActivity;
@@ -64,9 +62,14 @@
         wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "CMWakeLock");
         wl.acquire();
         // Each test case will start with cellular connection
-        waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
-        verifyCellularConnection();
+        if (!cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT)) {
+            // Note: When the test fails in setUp(), tearDown is not called. In that case,
+            // the activity is destroyed which blocks the next test at "getActivity()".
+            // tearDown() is called hear to avoid that situation.
+            tearDown();
+            fail("Device is not connected to Mobile, setUp failed");
+        }
     }
 
     @Override
@@ -74,86 +77,26 @@
         cmActivity.finish();
         Log.v(LOG_TAG, "tear down ConnectivityManagerTestActivity");
         wl.release();
-        cmActivity.clearWifi();
+        cmActivity.removeConfiguredNetworksAndDisableWifi();
         super.tearDown();
     }
 
     // help function to verify 3G connection
     public void verifyCellularConnection() {
-        NetworkInfo extraNetInfo = cmActivity.mNetworkInfo;
+        NetworkInfo extraNetInfo = cmActivity.mCM.getActiveNetworkInfo();
         assertEquals("network type is not MOBILE", ConnectivityManager.TYPE_MOBILE,
-            extraNetInfo.getType());
+                extraNetInfo.getType());
         assertTrue("not connected to cellular network", extraNetInfo.isConnected());
         assertTrue("no data connection", cmActivity.mState.equals(State.CONNECTED));
     }
 
-    // Wait for network connectivity state: CONNECTING, CONNECTED, SUSPENDED,
-    //                                      DISCONNECTING, DISCONNECTED, UNKNOWN
-    private void waitForNetworkState(int networkType, State expectedState, long timeout) {
-        long startTime = System.currentTimeMillis();
-        while (true) {
-            if ((System.currentTimeMillis() - startTime) > timeout) {
-                if (cmActivity.mCM.getNetworkInfo(networkType).getState() != expectedState) {
-                    assertFalse("Wait for network state timeout", true);
-                } else {
-                    // the broadcast has been sent out. the state has been changed.
-                    return;
-                }
-            }
-            Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType +
-                    " to be " + expectedState.toString());
-            synchronized (cmActivity.connectivityObject) {
-                try {
-                    cmActivity.connectivityObject.wait(STATE_TRANSITION_SHORT_TIMEOUT);
-                } catch (InterruptedException e) {
-                    e.printStackTrace();
-                }
-                if ((cmActivity.mNetworkInfo.getType() != networkType) ||
-                    (cmActivity.mNetworkInfo.getState() != expectedState)) {
-                    Log.v(LOG_TAG, "network state for " + cmActivity.mNetworkInfo.getType() +
-                            "is: " + cmActivity.mNetworkInfo.getState());
-                    continue;
-                }
-                break;
-            }
-        }
-    }
-
-    // Wait for Wifi state: WIFI_STATE_DISABLED, WIFI_STATE_DISABLING, WIFI_STATE_ENABLED,
-    //                      WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
-    private void waitForWifiState(int expectedState, long timeout) {
-        long startTime = System.currentTimeMillis();
-        while (true) {
-            if ((System.currentTimeMillis() - startTime) > timeout) {
-                if (cmActivity.mWifiState != expectedState) {
-                    assertFalse("Wait for Wifi state timeout", true);
-                } else {
-                    return;
-                }
-            }
-            Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState);
-            synchronized (cmActivity.wifiObject) {
-                try {
-                    cmActivity.wifiObject.wait(5*1000);
-                } catch (InterruptedException e) {
-                    e.printStackTrace();
-                }
-                if (cmActivity.mWifiState != expectedState) {
-                    Log.v(LOG_TAG, "Wifi state is: " + cmActivity.mWifiNetworkInfo.getState());
-                    continue;
-                }
-                break;
-            }
-        }
-    }
-
     // Test case 1: Test enabling Wifi without associating with any AP
     @LargeTest
     public void test3GToWifiNotification() {
         // To avoid UNKNOWN state when device boots up
         cmActivity.enableWifi();
         try {
-            Thread.sleep(2 * STATE_TRANSITION_SHORT_TIMEOUT);
+            Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
         } catch (Exception e) {
             Log.v(LOG_TAG, "exception: " + e.toString());
         }
@@ -170,7 +113,7 @@
         // Eanble Wifi
         cmActivity.enableWifi();
         try {
-            Thread.sleep(2 * STATE_TRANSITION_SHORT_TIMEOUT);
+            Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
         } catch (Exception e) {
             Log.v(LOG_TAG, "exception: " + e.toString());
         }
@@ -208,12 +151,13 @@
         assertTrue("failed to connect to " + TEST_ACCESS_POINT,
                 cmActivity.connectToWifi(TEST_ACCESS_POINT));
 
-        waitForWifiState(WifiManager.WIFI_STATE_ENABLED, STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
         Log.v(LOG_TAG, "wifi state is enabled");
-        waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
-        waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         // validate states
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
@@ -237,12 +181,13 @@
         // Connect to TEST_ACCESS_POINT
         assertTrue("failed to connect to " + TEST_ACCESS_POINT,
                 cmActivity.connectToWifi(TEST_ACCESS_POINT));
-        waitForWifiState(WifiManager.WIFI_STATE_ENABLED, STATE_TRANSITION_LONG_TIMEOUT);
-        waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         try {
-            Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT);
+            Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
         } catch (Exception e) {
             Log.v(LOG_TAG, "exception: " + e.toString());
         }
@@ -255,11 +200,12 @@
         }
 
         // Wait for the Wifi state to be DISABLED
-        waitForWifiState(WifiManager.WIFI_STATE_DISABLED, STATE_TRANSITION_LONG_TIMEOUT);
-        waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
-        waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_DISABLED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         //Prepare for connectivity state verification
         NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
@@ -275,10 +221,10 @@
         cmActivity.enableWifi();
 
         // Wait for Wifi to be connected and mobile to be disconnected
-        waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
-        waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         // validate wifi states
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
@@ -298,12 +244,12 @@
         assertTrue("failed to connect to " + TEST_ACCESS_POINT,
                    cmActivity.connectToWifi(TEST_ACCESS_POINT));
 
-        waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-            STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+            ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         // Wait for a few seconds to avoid the state that both Mobile and Wifi is connected
         try {
-            Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT);
+            Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
         } catch (Exception e) {
             Log.v(LOG_TAG, "exception: " + e.toString());
         }
@@ -318,12 +264,12 @@
                 NetworkState.TO_DISCONNECTION, State.DISCONNECTED);
 
         // clear Wifi
-        cmActivity.clearWifi();
+        cmActivity.removeConfiguredNetworksAndDisableWifi();
 
-        waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
-        waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         // validate states
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
@@ -357,7 +303,7 @@
         // Enable airplane mode
         cmActivity.setAirplaneMode(getInstrumentation().getContext(), true);
         try {
-            Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT);
+            Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
         } catch (Exception e) {
             Log.v(LOG_TAG, "exception: " + e.toString());
         }
@@ -389,8 +335,8 @@
         // disable airplane mode
         cmActivity.setAirplaneMode(getInstrumentation().getContext(), false);
 
-        waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         // Validate the state transition
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
@@ -414,8 +360,8 @@
         // Eanble airplane mode
         cmActivity.setAirplaneMode(getInstrumentation().getContext(), true);
 
-        waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
         cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE,
@@ -429,8 +375,8 @@
         // Connect to Wifi
         assertTrue("failed to connect to " + TEST_ACCESS_POINT,
                    cmActivity.connectToWifi(TEST_ACCESS_POINT));
-        waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                            STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+                            ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         // validate state and broadcast
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
@@ -457,11 +403,11 @@
         assertTrue("failed to connect to " + TEST_ACCESS_POINT,
                 cmActivity.connectToWifi(TEST_ACCESS_POINT));
 
-        waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         try {
-            Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT);
+            Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
         } catch (Exception e) {
             Log.v(LOG_TAG, "exception: " + e.toString());
         }
@@ -469,11 +415,11 @@
         // Enable airplane mode without clearing Wifi
         cmActivity.setAirplaneMode(getInstrumentation().getContext(), true);
 
-        waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         try {
-            Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT);
+            Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
         } catch (Exception e) {
             Log.v(LOG_TAG, "exception: " + e.toString());
         }
@@ -487,10 +433,10 @@
         // Disable airplane mode
         cmActivity.setAirplaneMode(getInstrumentation().getContext(), false);
 
-        waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                            STATE_TRANSITION_LONG_TIMEOUT);
-        waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
-                            STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+                            ConnectivityManagerTestActivity.LONG_TIMEOUT));
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.DISCONNECTED,
+                            ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         // validate the state transition
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
@@ -508,14 +454,15 @@
         //Connect to TEST_ACCESS_POINT
         assertTrue("failed to connect to " + TEST_ACCESS_POINT,
                    cmActivity.connectToWifi(TEST_ACCESS_POINT));
-        waitForWifiState(WifiManager.WIFI_STATE_ENABLED, STATE_TRANSITION_LONG_TIMEOUT);
-        waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                            STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+                            ConnectivityManagerTestActivity.LONG_TIMEOUT));
         assertNotNull("Not associated with any AP",
                       cmActivity.mWifiManager.getConnectionInfo().getBSSID());
 
         try {
-            Thread.sleep(STATE_TRANSITION_SHORT_TIMEOUT);
+            Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
         } catch (Exception e) {
             Log.v(LOG_TAG, "exception: " + e.toString());
         }
@@ -527,13 +474,14 @@
         }
 
         // Verify the connectivity state for Wifi is DISCONNECTED
-        waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
-                STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         if (!cmActivity.disableWifi()) {
             Log.v(LOG_TAG, "disable Wifi failed");
             return;
         }
-        waitForWifiState(WifiManager.WIFI_STATE_DISABLED, STATE_TRANSITION_LONG_TIMEOUT);
+        assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_DISABLED,
+                ConnectivityManagerTestActivity.LONG_TIMEOUT));
     }
 }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
new file mode 100644
index 0000000..69eb5db
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010, 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.connectivitymanagertest.functional;
+
+import com.android.connectivitymanagertest.ConnectivityManagerTestActivity;
+import com.android.connectivitymanagertest.NetworkState;
+
+import android.R;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test Wi-Fi connection with different configuration
+ * To run this tests:
+ *     adb shell am instrument -e class
+ *          com.android.connectivitymanagertest.functional.WifiConnectionTest
+ *          -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner
+ */
+public class WifiConnectionTest
+    extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> {
+    private static final String TAG = "WifiConnectionTest";
+    private static final boolean DEBUG = true;
+    private static final String PKG_NAME = "com.android.connectivitymanagertests";
+    private List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
+    private ConnectivityManagerTestActivity mAct;
+
+    public WifiConnectionTest() {
+        super(PKG_NAME, ConnectivityManagerTestActivity.class);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mAct = getActivity();
+        networks = mAct.loadNetworkConfigurations();
+        if (DEBUG) {
+            printNetworkConfigurations();
+        }
+
+        // enable Wifi and verify wpa_supplicant is started
+        assertTrue("enable Wifi failed", mAct.enableWifi());
+        try {
+            Thread.sleep( 2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
+        } catch (Exception e) {
+            fail("interrupted while waiting for WPA_SUPPLICANT to start");
+        }
+        WifiInfo mConnection = mAct.mWifiManager.getConnectionInfo();
+        assertNotNull(mConnection);
+        assertTrue("wpa_supplicant is not started ", mAct.mWifiManager.pingSupplicant());
+    }
+
+    private void printNetworkConfigurations() {
+        Log.v(TAG, "==== print network configurations parsed from XML file ====");
+        Log.v(TAG, "number of access points: " + networks.size());
+        for (WifiConfiguration config : networks) {
+            Log.v(TAG, config.toString());
+        }
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mAct.removeConfiguredNetworksAndDisableWifi();
+        super.tearDown();
+    }
+
+    /**
+     * Connect to the provided Wi-Fi network
+     * @param config is the network configuration
+     * @return true if the connection is successful.
+     */
+    private void connectToWifi(WifiConfiguration config) {
+        // step 1: connect to the test access point
+        assertTrue("failed to connect to " + config.SSID,
+                mAct.connectToWifiWithConfiguration(config));
+
+        // step 2: verify Wifi state and network state;
+        assertTrue(mAct.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
+                ConnectivityManagerTestActivity.SHORT_TIMEOUT));
+        assertTrue(mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI,
+                State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT));
+
+        // step 3: verify the current connected network is the given SSID
+        if (DEBUG) {
+            Log.v(TAG, "config.SSID = " + config.SSID);
+            Log.v(TAG, "mAct.mWifiManager.getConnectionInfo.getSSID()" +
+                    mAct.mWifiManager.getConnectionInfo().getSSID());
+        }
+        assertTrue(config.SSID.contains(mAct.mWifiManager.getConnectionInfo().getSSID()));
+
+        // Maintain the connection for 50 seconds before switching
+        try {
+            Thread.sleep(50*1000);
+        } catch (Exception e) {
+            fail("interrupted while waiting for WPA_SUPPLICANT to start");
+        }
+    }
+
+    @LargeTest
+    public void testWifiConnections() {
+        for (int i = 0; i < networks.size(); i++) {
+            connectToWifi(networks.get(i));
+            mAct.removeConfiguredNetworksAndDisableWifi();
+        }
+    }
+}
diff --git a/data/sounds/notifications/Altair.ogg b/data/sounds/notifications/Altair.ogg
old mode 100755
new mode 100644
index 8b56f91..660c800
--- a/data/sounds/notifications/Altair.ogg
+++ b/data/sounds/notifications/Altair.ogg
Binary files differ
diff --git a/data/sounds/notifications/Capella.ogg b/data/sounds/notifications/Capella.ogg
old mode 100755
new mode 100644
index 22e1d37..ae4f3c5
--- a/data/sounds/notifications/Capella.ogg
+++ b/data/sounds/notifications/Capella.ogg
Binary files differ
diff --git a/data/sounds/notifications/Castor.ogg b/data/sounds/notifications/Castor.ogg
old mode 100755
new mode 100644
index 4ad22d0..92de8e7
--- a/data/sounds/notifications/Castor.ogg
+++ b/data/sounds/notifications/Castor.ogg
Binary files differ
diff --git a/data/sounds/notifications/Deneb.ogg b/data/sounds/notifications/Deneb.ogg
old mode 100755
new mode 100644
index 096ff0a..3b17e28
--- a/data/sounds/notifications/Deneb.ogg
+++ b/data/sounds/notifications/Deneb.ogg
Binary files differ
diff --git a/data/sounds/notifications/Electra.ogg b/data/sounds/notifications/Electra.ogg
old mode 100755
new mode 100644
index 313cb6f..9335d8d
--- a/data/sounds/notifications/Electra.ogg
+++ b/data/sounds/notifications/Electra.ogg
Binary files differ
diff --git a/data/sounds/notifications/Fomalhaut.ogg b/data/sounds/notifications/Fomalhaut.ogg
old mode 100755
new mode 100644
index 39c71ad..9448c18
--- a/data/sounds/notifications/Fomalhaut.ogg
+++ b/data/sounds/notifications/Fomalhaut.ogg
Binary files differ
diff --git a/data/sounds/notifications/Polaris.ogg b/data/sounds/notifications/Polaris.ogg
old mode 100755
new mode 100644
index 8104178..d5b991f
--- a/data/sounds/notifications/Polaris.ogg
+++ b/data/sounds/notifications/Polaris.ogg
Binary files differ
diff --git a/data/sounds/notifications/Pollux.ogg b/data/sounds/notifications/Pollux.ogg
old mode 100755
new mode 100644
index ede9e27..d37c75c
--- a/data/sounds/notifications/Pollux.ogg
+++ b/data/sounds/notifications/Pollux.ogg
Binary files differ
diff --git a/data/sounds/notifications/Procyon.ogg b/data/sounds/notifications/Procyon.ogg
old mode 100755
new mode 100644
index 02dbc3f..93d1557
--- a/data/sounds/notifications/Procyon.ogg
+++ b/data/sounds/notifications/Procyon.ogg
Binary files differ
diff --git a/data/sounds/notifications/arcturus.ogg b/data/sounds/notifications/arcturus.ogg
index 7b61963..9d73103 100644
--- a/data/sounds/notifications/arcturus.ogg
+++ b/data/sounds/notifications/arcturus.ogg
Binary files differ
diff --git a/data/sounds/notifications/regulus.ogg b/data/sounds/notifications/regulus.ogg
index dc52054..4f28d9e 100644
--- a/data/sounds/notifications/regulus.ogg
+++ b/data/sounds/notifications/regulus.ogg
Binary files differ
diff --git a/data/sounds/notifications/sirius.ogg b/data/sounds/notifications/sirius.ogg
index a54481b..78c9991 100644
--- a/data/sounds/notifications/sirius.ogg
+++ b/data/sounds/notifications/sirius.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ANDROMEDA.ogg b/data/sounds/ringtones/ANDROMEDA.ogg
index 33e43e3..8f6bd3e 100644
--- a/data/sounds/ringtones/ANDROMEDA.ogg
+++ b/data/sounds/ringtones/ANDROMEDA.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Aquila.ogg b/data/sounds/ringtones/Aquila.ogg
old mode 100755
new mode 100644
index 3f9266d..b391be9
--- a/data/sounds/ringtones/Aquila.ogg
+++ b/data/sounds/ringtones/Aquila.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ArgoNavis.ogg b/data/sounds/ringtones/ArgoNavis.ogg
old mode 100755
new mode 100644
index 11c6092..b4202ac
--- a/data/sounds/ringtones/ArgoNavis.ogg
+++ b/data/sounds/ringtones/ArgoNavis.ogg
Binary files differ
diff --git a/data/sounds/ringtones/BOOTES.ogg b/data/sounds/ringtones/BOOTES.ogg
index 485a9f6..0716a4f 100644
--- a/data/sounds/ringtones/BOOTES.ogg
+++ b/data/sounds/ringtones/BOOTES.ogg
Binary files differ
diff --git a/data/sounds/ringtones/CANISMAJOR.ogg b/data/sounds/ringtones/CANISMAJOR.ogg
index e51d6e0..177d3de 100644
--- a/data/sounds/ringtones/CANISMAJOR.ogg
+++ b/data/sounds/ringtones/CANISMAJOR.ogg
Binary files differ
diff --git a/data/sounds/ringtones/CASSIOPEIA.ogg b/data/sounds/ringtones/CASSIOPEIA.ogg
index 0456a59..c4a7948 100644
--- a/data/sounds/ringtones/CASSIOPEIA.ogg
+++ b/data/sounds/ringtones/CASSIOPEIA.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Carina.ogg b/data/sounds/ringtones/Carina.ogg
old mode 100755
new mode 100644
index 25cf36e..aeb9b36
--- a/data/sounds/ringtones/Carina.ogg
+++ b/data/sounds/ringtones/Carina.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Centaurus.ogg b/data/sounds/ringtones/Centaurus.ogg
old mode 100755
new mode 100644
index 58b413d..404bdbc
--- a/data/sounds/ringtones/Centaurus.ogg
+++ b/data/sounds/ringtones/Centaurus.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Cygnus.ogg b/data/sounds/ringtones/Cygnus.ogg
old mode 100755
new mode 100644
index 763926e..b2e1e65
--- a/data/sounds/ringtones/Cygnus.ogg
+++ b/data/sounds/ringtones/Cygnus.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Draco.ogg b/data/sounds/ringtones/Draco.ogg
old mode 100755
new mode 100644
index 8749b3a..88d5a04
--- a/data/sounds/ringtones/Draco.ogg
+++ b/data/sounds/ringtones/Draco.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Eridani.ogg b/data/sounds/ringtones/Eridani.ogg
old mode 100755
new mode 100644
index a290a6e..b665a29
--- a/data/sounds/ringtones/Eridani.ogg
+++ b/data/sounds/ringtones/Eridani.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Lyra.ogg b/data/sounds/ringtones/Lyra.ogg
old mode 100755
new mode 100644
index 16d47e9..696f278
--- a/data/sounds/ringtones/Lyra.ogg
+++ b/data/sounds/ringtones/Lyra.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Machina.ogg b/data/sounds/ringtones/Machina.ogg
old mode 100755
new mode 100644
index ab0105e..ac16f7e
--- a/data/sounds/ringtones/Machina.ogg
+++ b/data/sounds/ringtones/Machina.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Orion.ogg b/data/sounds/ringtones/Orion.ogg
old mode 100755
new mode 100644
index 8e36024..807f107
--- a/data/sounds/ringtones/Orion.ogg
+++ b/data/sounds/ringtones/Orion.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Pegasus.ogg b/data/sounds/ringtones/Pegasus.ogg
old mode 100755
new mode 100644
index 17baaed..66c4970
--- a/data/sounds/ringtones/Pegasus.ogg
+++ b/data/sounds/ringtones/Pegasus.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Scarabaeus.ogg b/data/sounds/ringtones/Scarabaeus.ogg
old mode 100755
new mode 100644
index 50b9fab..e70fc69
--- a/data/sounds/ringtones/Scarabaeus.ogg
+++ b/data/sounds/ringtones/Scarabaeus.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Sceptrum.ogg b/data/sounds/ringtones/Sceptrum.ogg
old mode 100755
new mode 100644
index 9be353e..fc50aef
--- a/data/sounds/ringtones/Sceptrum.ogg
+++ b/data/sounds/ringtones/Sceptrum.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Solarium.ogg b/data/sounds/ringtones/Solarium.ogg
old mode 100755
new mode 100644
index 2aed475..d27f141
--- a/data/sounds/ringtones/Solarium.ogg
+++ b/data/sounds/ringtones/Solarium.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Testudo.ogg b/data/sounds/ringtones/Testudo.ogg
old mode 100755
new mode 100644
index 063dacf..0ca8d6b
--- a/data/sounds/ringtones/Testudo.ogg
+++ b/data/sounds/ringtones/Testudo.ogg
Binary files differ
diff --git a/data/sounds/ringtones/Vespa.ogg b/data/sounds/ringtones/Vespa.ogg
old mode 100755
new mode 100644
index eaac1bd..4423bbb
--- a/data/sounds/ringtones/Vespa.ogg
+++ b/data/sounds/ringtones/Vespa.ogg
Binary files differ
diff --git a/data/sounds/ringtones/hydra.ogg b/data/sounds/ringtones/hydra.ogg
index b48614f..edde14f 100644
--- a/data/sounds/ringtones/hydra.ogg
+++ b/data/sounds/ringtones/hydra.ogg
Binary files differ
diff --git a/docs/html/guide/appendix/media-formats.jd b/docs/html/guide/appendix/media-formats.jd
index 94a6471..8709994 100644
--- a/docs/html/guide/appendix/media-formats.jd
+++ b/docs/html/guide/appendix/media-formats.jd
@@ -22,7 +22,7 @@
 <tr>
 <td rowspan="9">Audio</td>
 <td>AAC LC/LTP</td>
-<td>&nbsp;</td>
+<td style="text-align: center;">X</td>
 <td style="text-align: center;">X</td>
 <td rowspan="3">Mono/Stereo content in any combination of standard bit rates up to 160 kbps and sampling rates from 8 to 48kHz</td>
 <td rowspan="3">3GPP (.3gp) and MPEG-4 (.mp4, .m4a). No support for raw AAC (.aac)</td>
@@ -51,7 +51,7 @@
 
 <tr>
 <td>AMR-WB</td>
-<td>&nbsp;</td>
+<td style="text-align: center;">X</td>
 <td style="text-align: center;">X</td>
 <td>9 rates from 6.60 kbit/s to 23.85 kbit/s sampled @ 16kHz</td>
 <td>3GPP (.3gp)</td>
@@ -109,7 +109,7 @@
 
 <tr>
 <td>PNG</td>
-<td>&nbsp;</td>
+<td style="text-align: center;">X</td>
 <td style="text-align: center;">X</td>
 <td>&nbsp;</td>
 <td>PNG (.png)</td>
diff --git a/docs/html/guide/topics/manifest/manifest-element.jd b/docs/html/guide/topics/manifest/manifest-element.jd
index a35c5a1..7f21e6b 100644
--- a/docs/html/guide/topics/manifest/manifest-element.jd
+++ b/docs/html/guide/topics/manifest/manifest-element.jd
@@ -159,6 +159,9 @@
 storage. However, the system will not allow the user to move the application to external storage if
 this attribute is set to {@code internalOnly}, which is the default setting.</p>
 
+<p>Read <a href="{@docRoot}guide/appendix/install-location.html">App Install Location</a> for
+more information about using this attribute (including how to maintain backward compatibility).</p>
+
 <p>Introduced in: API Level 8.</p>
 
 
@@ -173,7 +176,7 @@
 
 <p>
 <dt>see also:</dt>
-<dd><a href="{@docRoot}guide/appendix/install-location.html">App Install Location</a><br/>
+<dd>
 <code><a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a></code></dd>
 
 </dl>
diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd
index 45f4a66..b5b30f6 100644
--- a/docs/html/guide/topics/manifest/uses-feature-element.jd
+++ b/docs/html/guide/topics/manifest/uses-feature-element.jd
@@ -472,11 +472,11 @@
 Package</strong>. Select a destination filename and path and click
 <strong>OK</strong>. </li>
 <li>Next, locate the <code>aapt</code> tool, if it is not already in your PATH.
-If you are using SDK Tools r7 or earlier, you can find <code>aapt</code> in the
-<code>&lt;<em>SDK</em>&gt;/platforms/android-&lt;<em>platform</em>&gt;/tools/</code> directory.
+If you are using SDK Tools r8 or higher, you can find <code>aapt</code> in the
+<code>&lt;<em>SDK</em>&gt;/platform-tools/</code> directory.
 <p class="note"><strong>Note:</strong> You must use the version of
-<code>aapt</code> that is provided for the latest platform release available. If
-you do not have the latest platform release, download it using the <a
+<code>aapt</code> that is provided for the latest Platform-Tools component available. If
+you do not have the latest Platform-Tools component, download it using the <a
 href="{@docRoot}sdk/adding-components.html">Android SDK and AVD Manager</a>.
 </p></li>
 <li>Run <code>aapt</code> using this syntax: </li>
@@ -522,6 +522,14 @@
        <th>Comments</th>
     </tr>
     <tr>
+       <td>Audio</td>
+       <td><code>android.hardware.audio.low_latency</td>
+       <td>The application uses a low-latency audio pipeline on the device and
+is sensitive to delays or lag in sound input or output.</td>
+<td>
+</td>
+    </tr>
+    <tr>
        <td>Bluetooth</td>
        <td><code>android.hardware.bluetooth</td>
        <td>The application uses Bluetooth radio features in the device.</td>
@@ -529,7 +537,7 @@
 </td>
     </tr>
     <tr>
-       <td rowspan="3">Camera</td>
+       <td rowspan="4">Camera</td>
        <td><code>android.hardware.camera</code></td>
        <td>The application uses the device's camera. If the device supports
            multiple cameras, the application uses the camera that facing
@@ -539,7 +547,7 @@
 <tr>
   <td><code>android.hardware.camera.autofocus</code></td>
   <td>Subfeature. The application uses the device camera's autofocus capability.</td>
-  <td rowspan="2">If declared with the <code>"android:required="true"</code>
+  <td rowspan="3">If declared with the <code>"android:required="true"</code>
 attribute, these subfeatures implicitly declare the
 <code>android.hardware.camera</code> parent feature. </td>
 </tr>
@@ -547,6 +555,10 @@
   <td><code>android.hardware.camera.flash</code></td>
   <td>Subfeature. The application uses the device camera's flash.</td>
 </tr>
+<tr>
+  <td><code>android.hardware.camera.front</code></td>
+  <td>Subfeature. The application uses a front-facing camera on the device.</td>
+</tr>
 
 <tr>
   <td rowspan="3">Location</td>
@@ -568,21 +580,43 @@
   <td>Subfeature. The application uses precise location coordinates obtained
 from a Global Positioning System receiver on the device. </td>
 </tr>
-
 <tr>
-  <td rowspan="4">Sensors</td>
+  <td>Microphone</td>
+  <td><code>android.hardware.microphone</code></td>
+  <td>The application uses a microphone on the device.
+  </td>
+  <td></td>
+</tr>
+<tr>
+  <td>Near Field Communications</td>
+  <td><code>android.hardware.nfc</td>
+  <td>The application uses NFC radio features in the device.</td>
+  <td></td>
+</tr>
+<tr>
+  <td rowspan="6">Sensors</td>
   <td><code>android.hardware.sensor.accelerometer</code></td>
   <td>The application uses motion readings from an accelerometer on the
 device.</td>
   <td></td>
 </tr>
 <tr>
+  <td><code>android.hardware.sensor.barometer</code></td>
+  <td>The application uses the device's barometer.</td>
+  <td></td>
+</tr>
+<tr>
   <td><code>android.hardware.sensor.compass</code></td>
   <td>The application uses directional readings from a magnetometer (compass) on
 the device.</td>
   <td></td>
 </tr>
 <tr>
+  <td><code>android.hardware.sensor.gyroscope</code></td>
+  <td>The application uses the device's gyroscope sensor.</td>
+  <td></td>
+</tr>
+<tr>
   <td><code>android.hardware.sensor.light</code></td>
   <td>The application uses the device's light sensor.</td>
   <td></td>
@@ -593,12 +627,20 @@
   <td></td>
 </tr>
 <tr>
-  <td>Microphone</td>
-  <td><code>android.hardware.microphone</code></td>
-  <td>The application uses a microphone on the device.
+  <td rowspan="2">SIP/VOIP</td>
+  <td><code>android.hardware.sip</code></td>
+  <td>The application uses SIP service on the device.
   </td>
   <td></td>
 </tr>
+<tr>
+  <td><code>android.hardware.sip.voip</code></td>
+  <td>Subfeature. The application uses SIP-based VOIP service on the device.
+  </td>
+  <td>If declared with the <code>"android:required="true"</code> attribute, this
+subfeature implicitly declares the <code>android.hardware.sip</code>
+parent feature.</td>
+</tr>
 
 <tr>
   <td rowspan="3">Telephony</td>
@@ -622,14 +664,14 @@
 </tr>
 
 <tr>
-  <td rowspan="3">Touchscreen</td>
+  <td rowspan="4">Touchscreen</td>
   <td><code>android.hardware.touchscreen</code></td>
   <td>The application uses touchscreen capabilities on the device.</td>
   <td></td>
 </tr>
 <tr>
   <td><code>android.hardware.touchscreen.multitouch</code></td>
-  <td>The application uses basic two-point multitouch capabilities on the device
+  <td>Subfeature. The application uses basic two-point multitouch capabilities on the device
 screen.</td>
   <td>If declared with the <code>"android:required="true"</code> attribute, this
 subfeature implicitly declares the <code>android.hardware.touchscreen</code>
@@ -640,10 +682,16 @@
   <td>Subfeature. The application uses advanced multipoint multitouch
 capabilities on the device screen, such as for tracking two or more points fully
 independently.</td>
-  <td>If declared with the <code>"android:required="true"</code> attribute, this
+  <td rowspan="2">If declared with the <code>"android:required="true"</code> attribute, this
 subfeature implicitly declares the
 <code>android.hardware.touchscreen.multitouch</code> parent feature. </td>
 </tr>
+<tr>
+  <td><code>android.hardware.touchscreen.multitouch.jazzhand</code></td>
+  <td>Subfeature. The application uses advanced multipoint multitouch
+capabilities on the device screen, for tracking up to five points fully
+independently.</td>
+</tr>
 
 <tr>
   <td>Wifi</td>
diff --git a/docs/html/images/sdk_manager_packages.png b/docs/html/images/sdk_manager_packages.png
index 463be8f..027cce7 100755
--- a/docs/html/images/sdk_manager_packages.png
+++ b/docs/html/images/sdk_manager_packages.png
Binary files differ
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index 117ecfb..ed23c7c 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -227,6 +227,9 @@
           <li><a href="<?cs var:toroot ?>resources/samples/MultiResolution/index.html">
                 <span class="en">Multiple Resolutions</span>
               </a></li>
+          <li><a href="<?cs var:toroot ?>resources/samples/NFCDemo/index.html">
+                <span class="en">NFCDemo</span>
+                </a> <span class="new">new!</span></li>
           <li><a href="<?cs var:toroot ?>resources/samples/NotePad/index.html">
                 <span class="en">Note Pad</span>
               </a></li>
diff --git a/docs/html/resources/samples/images/NfcDemo.png b/docs/html/resources/samples/images/NfcDemo.png
new file mode 100644
index 0000000..c175d12
--- /dev/null
+++ b/docs/html/resources/samples/images/NfcDemo.png
Binary files differ
diff --git a/docs/html/resources/samples/index.jd b/docs/html/resources/samples/index.jd
index cbe6ca3..5749728 100644
--- a/docs/html/resources/samples/index.jd
+++ b/docs/html/resources/samples/index.jd
@@ -58,6 +58,9 @@
   <dd>A sample application that shows how to use resource directory qualifiers to
   provide different resources for different screen configurations.</dd>
 
+ <dt><a href="NFCDemo/index.html">NFCDemo</a></dt>
+  <dd>An application for reading NFC Forum Type 2 Tags using platform NFC apis.</a>.</dd>
+
  <dt><a href="NotePad/index.html">Note Pad</a></dt>
   <dd>An application for saving notes. Similar (but not identical) to the 
     <a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad tutorial</a>.</dd>
diff --git a/docs/html/sdk/adding-components.jd b/docs/html/sdk/adding-components.jd
index 05be0d6..d2e55d8 100644
--- a/docs/html/sdk/adding-components.jd
+++ b/docs/html/sdk/adding-components.jd
@@ -98,20 +98,20 @@
   This will reveal all of the components that are currently available for download
   from the SDK repository.</li>
   <li>Select the component(s) you'd like to install and click <strong>Install
-  Selected</strong>. If you aren't sure which packages to select, read <a
-  href="installing.html#which">Recommended Components</a>.</li>
-  <li>Verify and accept the components you want and click <strong>Install
-  Accepted</strong>. The components will now be installed into your existing
-  Android SDK directories.</li>
+  Selected</strong>. (If you aren't sure which packages to select, read <a
+  href="installing.html#which">Recommended Components</a>.)</li>
+  <li>Verify and accept the components you want (ensure each one is selected with a green
+checkmark) and click <strong>Install</strong>. The components will now be installed into
+your existing Android SDK directories.</li>
 </ol>
 
 <p>New platforms are automatically saved into the
-<code>&lt;<em>sdk</em>&gt;/platforms/</code> directory of your SDK;
-new add-ons are saved in the <code>&lt;<em>sdk</em>&gt;/add-ons/</code>
+<code>&lt;sdk&gt;/platforms/</code> directory of your SDK;
+new add-ons are saved in the <code>&lt;sdk&gt;/add-ons/</code>
 directory; samples are saved in the
-<code>&lt;<em>sdk</em>&gt;/samples/android-&lt;<em>level</em>&gt;/</code>;
+<code>&lt;sdk&gt;/samples/android-&lt;level&gt;/</code>;
 and new documentation is saved in the existing
-<code>&lt;<em>sdk</em>&gt;/docs/</code> directory (old docs are replaced).</p>
+<code>&lt;sdk&gt;/docs/</code> directory (old docs are replaced).</p>
 
 
 <h2 id="UpdatingComponents">Updating SDK Components</h2>
@@ -150,27 +150,32 @@
 
 <p>For example, there may be a dependency between the ADT Plugin for Eclipse and
 the SDK Tools component. When you install the SDK Tools
-component, you would then need to upgrade to the required version of ADT (if you
-are developing in Eclipse). In this case, you would find dependencies listed in
-"Revisions" section of the <a href="{@docRoot}sdk/eclipse-adt.html#notes">ADT
-Plugin Notes</a> and <a href="{@docRoot}sdk/tools-notes.html#notes">SDK Tools
-Notes</a> documents. </p>
+component, you should also upgrade to the required version of ADT (if you
+are developing in Eclipse). In this case,  the major version number for your ADT plugin should
+always match the revision number of your SDK Tools (for example, ADT 8.x requires SDK Tools r8).
+</p>
 
-<p>Additionally, the development tools will notify you with debug warnings
-if there is dependency that you need to address. </p>
+<p>Also make sure that, each time you install a new version of the Android platform, you have
+the latest version of the SDK Platform-tools component. The SDK Platform-tools contain
+tools that are backward compatible with all versions of the Android platform and are
+often updated to support new features in the latest version of the Android platform.</p>
+
+<p>The development tools will notify you with debug warnings if there is dependency that you need to
+address. The SDK and AVD Manager also enforces dependencies by requiring that you download any
+components that are needed by those you have selected.</p>
 
 
 <h2 id="AddingSites">Adding New Sites</h2>
 
-<p>By default, <strong>Available Packages</strong> only shows the default
-repository site, which offers platforms, SDK tools, documentation, the
-Google APIs Add-on, and other components. You can add other sites that host
+<p>By default, <strong>Available Packages</strong> displays components available from the
+<em>Android Repository</em> and <em>Third party Add-ons</em>. You can add other sites that host
 their own Android SDK add-ons, then download the SDK add-ons
 from those sites.</p>
 
 <p>For example, a mobile carrier or device manufacturer might offer additional
 API libraries that are supported by their own Android-powered devices. In order
-to develop using their libraries, you must install their Android SDK add-on. </p>
+to develop using their libraries, you must install their Android SDK add-on, if it's not already
+available under <em>Third party Add-ons</em>. </p>
 
 <p>If a carrier or device manufacturer has hosted an SDK add-on repository file
 on their web site, follow these steps to add their site to the SDK and AVD
@@ -178,11 +183,12 @@
 
 <ol>
   <li>Select <strong>Available Packages</strong> in the left panel.</li>
-  <li>Click <strong>Add Site</strong> and enter the URL of the
+  <li>Click <strong>Add Add-on Site</strong> and enter the URL of the
 {@code repository.xml} file. Click <strong>OK</strong>.</li>
 </ol>
-<p>Any SDK components available from the site will now be listed under
-<strong>Available Packages</strong>.</p>
+<p>Any SDK components available from the site will now be listed under a new item named
+<strong>User Add-ons</strong>.</p>
+
 
 <h2 id="troubleshooting">Troubleshooting</h2>
 
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 3dda5ee..8b77303 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -23,7 +23,8 @@
 
 <ol>
   <li>Prepare your development computer and ensure it meets the system requirements.</li>
-  <li>Install the SDK starter package from the table above.</li>
+  <li>Install the SDK starter package from the table above. (If you're on Windows, download the
+installer for help with the initial setup.)</li>
   <li>Install the ADT Plugin for Eclipse (if you'll be developing in Eclipse).</li>
   <li>Add Android platforms and other components to your SDK.</li>
   <li>Explore the contents of the Android SDK (optional).</li>
diff --git a/docs/html/sdk/installing.jd b/docs/html/sdk/installing.jd
index 2f19181..4cb1bb2 100644
--- a/docs/html/sdk/installing.jd
+++ b/docs/html/sdk/installing.jd
@@ -56,6 +56,7 @@
     <li><a href="#InstallingADT">3. Installing the ADT Plugin for Eclipse</a></li>
     <li><a href="#components">4. Adding Platforms and Other Components</a>
       <ol>
+        <li><a href="#components">Available Components</a></li>
         <li><a href="#which">Recommended Components</a></li>
       </ol></li>
     <li><a href="#sdkContents">5. Exploring the SDK (Optional)</a></li>
@@ -119,52 +120,14 @@
 it to a safe location on your machine. By default, the SDK files are unpacked
 into a directory named <code>android-sdk-&lt;machine-platform&gt;</code>.</p>
 
-<p>Make a note of the name and
-location of the unpacked SDK directory on your system&mdash;you will need to
+<p>If you downloaded the Windows installer ({@code .exe} file), run it now and it will check
+whether the proper Java SE Development Kit (JDK) is installed (installing it, if necessary), then
+install the SDK Tools into a default location (which you can modify).</p>
+
+<p>Make a note of the name and location of the SDK directory on your system&mdash;you will need to
 refer to the SDK directory later, when setting up the ADT plugin and when using
 the SDK tools from command line.</p>
 
-<p>Optionally, you might want to add the location of the SDK's
-<code>tools/</code> directory and {@code platform-tools/} directory  to your system
-<code>PATH</code>. Both tool directories are located at the root of the SDK folder.
-
-
-<div class="toggleable closed">
-  <a href="#" onclick="return toggleDiv(this)">
-        <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px"
-width="9px" />
-        How to update your PATH</a>
-  <div class="toggleme">
-
-<p>Adding
-<code>tools/</code> and {@code platform-tools/} to your path lets you run Android Debug Bridge (adb)
-and the other command line <a
-href="{@docRoot}guide/developing/tools/index.html">tools</a> without needing to
-supply the full path to the tool directories. </p>
-
-<ul>
-    <li>On Linux, edit your <code>~/.bash_profile</code> or <code>~/.bashrc</code> file. Look
-    for a line that sets the PATH environment variable and add the
-    full path to the <code>tools/</code> and {@code platform-tools/} directories to it. If you don't
-    see a line setting the path, you can add one:
-
-    <pre>export PATH=${PATH}:&lt;your_sdk_dir&gt;/tools:&lt;your_sdk_dir&gt;/platform-tools</pre>
-    </li>
-
-    <li>On a Mac OS X, look in your home directory for <code>.bash_profile</code> and
-    proceed as for Linux. You can create the <code>.bash_profile</code> if
-    you haven't already set one up on your machine. </li>
-
-    <li>On Windows, right-click on My Computer, and select Properties.
-  Under the Advanced tab, hit the Environment Variables button, and in the
-  dialog that comes up, double-click on Path (under System Variables). Add the full path to the
-  <code>tools/</code> and {@code platform-tools/} directories to the path. </li>
-  </ul>
-
-</div><!-- end toggleme -->
-</div><!-- end toggleable -->
-
-
 
 <h2 id="InstallingADT">Step 3. Installing the ADT Plugin for Eclipse</h2>
 
@@ -204,28 +167,40 @@
 (tools that the latest platform depend upon). However, downloading
 additional components is highly recommended.</p>
 
+<p>If you used the Windows installer, when you complete the installation wizard, it will launch the
+Android SDK and AVD Manager with a default set of platforms and other components selected
+for you to install. Simply click <strong>Install</strong> to accept the recommended set of
+components and install them. You can then skip to <a href="#sdkContents">Step 5</a>, but we
+recommend you first read the section about the <a href="#components">Available Components</a> to
+better understand the components available from the Android SDK and AVD Manager.</p>
+
 <p>You can launch the Android SDK and AVD Manager in one of the following ways:</p>
 <ul>
   <li>From within Eclipse, select <strong>Window &gt; Android SDK and AVD Manager</strong>.</li>
   <li>On Windows, double-click the <code>SDK Manager.ext</code> file at the root of the Android
 SDK directory.</li>
   <li>On Mac or Linux, open a terminal and navigate to the <code>tools/</code> directory in the
-Android SDK, then execute: <pre>android update sdk</pre> <p>This will automatically select
-the required and recommended components for you to install.</p></li>
+Android SDK, then execute: <pre>android</pre> </li>
 </ul>
 
 <p>To download components, use the graphical UI of the Android SDK and AVD
 Manager, shown in Figure 1, to browse the SDK repository and select new or updated
 components. The Android SDK and AVD Manager will install the selected components in
 your SDK environment. For information about which components you should download, see the following
-section about <a href="#which">Recommended Components</a></p>
+section about <a href="#which">Recommended Components</a>.</p>
 
 <img src="/images/sdk_manager_packages.png" />
 <p class="img-caption"><strong>Figure 1.</strong> The Android SDK and AVD Manager's
 <strong>Available Packages</strong> panel, which shows the SDK components that are
 available for you to download into your environment.</p>
 
-<p>The SDK repository offers these types of components:</p>
+
+<h3 id="components">Available Components</h3>
+
+<p>By default, there are two repositories of components for your SDK: <em>Android
+Repository</em> and <em>Third party Add-ons</em>.</p>
+
+<p>The <em>Android Repository</em> offers these types of components:</p>
 
 <ul>
 <li><strong>SDK Tools</strong> (pre-installed in the Android SDK starter
@@ -249,20 +224,14 @@
 detailed information about each platform, see the overview documents available
 under the section "Downloadable SDK Components," at left. </li>
 
-<li><strong>SDK Add-Ons</strong> &mdash; SDK add-ons provide a development
-environment for specific Android external
-library or a customized (but fully compliant) Android system image.  The Android
-SDK repository offers the Google APIs Add-On, which gives your application
-access to powerful mapping capabilities through the
-<code>com.google.android.maps</code> library. You can also add additional
-repositories, so that you can download other SDK add-ons, where available. </li>
-
-<li><strong>USB Driver for Windows</strong> &mdash; Contains driver files
+<li><strong>USB Driver for Windows</strong> (Windows only) &mdash; Contains driver files
 that you can install on your Windows computer, so that you can run and debug
 your applications on an actual device. You <em>do not</em> need the USB driver unless
 you plan to debug your application on an actual Android-powered device. If you
 develop on Mac OS X or Linux, you do not need a special driver to debug
-your application on an Android-powered device.</li>
+your application on an Android-powered device. (See <a
+href="{@docRoot}guide/developing/device.html">Developing on a Device</a> for more information
+about developing on a real device.)</li>
 
 <li><strong>Samples</strong> &mdash; Contains the sample code and apps available
 for each Android development platform. If you are just getting started with
@@ -275,6 +244,11 @@
 multiversion documentation for the Android framework API. </li>
 </ul>
 
+<p>The <em>Third party Add-ons</em> provide components that allow you to create a development
+environment using a specific Android external library (such as the Google Maps library) or a
+customized (but fully compliant) Android system image. You can add additional Add-on repositories,
+by clicking <strong>Add Add-on Site</strong>.</p>
+
 
 <h3 id="which">Recommended Components</h3>
 
@@ -496,6 +470,47 @@
 
 </table>
 
+
+<p>Optionally, you might want to add the location of the SDK's <code>tools/</code> and
+<code>platform-tools</code> to your <code>PATH</code> environment variable, to provide easy
+access to the tools.</p>
+
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)">
+        <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px"
+width="9px" />
+        How to update your PATH</a>
+  <div class="toggleme">
+
+<p>Adding both <code>tools/</code> and <code>platform-tools/</code> to your PATH lets you run
+command line <a href="{@docRoot}guide/developing/tools/index.html">tools</a> without needing to
+supply the full path to the tool directories. Depending on your operating system, you can
+include these directories in your PATH in the following way:</p>
+
+<ul>
+
+  <li>On Windows, right-click on My Computer, and select Properties.
+  Under the Advanced tab, hit the Environment Variables button, and in the
+  dialog that comes up, double-click on Path (under System Variables). Add the full path to the
+  <code>tools/</code> and <code>platform-tools/</code> directories to the path. </li>
+
+  <li>On Linux, edit your <code>~/.bash_profile</code> or <code>~/.bashrc</code> file. Look
+  for a line that sets the PATH environment variable and add the
+  full path to the <code>tools/</code> and <code>platform-tools</code> directories to it. If you
+  don't see a line setting the path, you can add one:
+  <pre>export PATH=${PATH}:&lt;sdk&gt;/tools:&lt;sdk&gt;/platform-tools</pre>
+  </li>
+
+  <li>On a Mac OS X, look in your home directory for <code>.bash_profile</code> and
+  proceed as for Linux. You can create the <code>.bash_profile</code> if
+  you don't already have one. </li>
+</ul>
+
+</div><!-- end toggleme -->
+</div><!-- end toggleable -->
+
+
 <h2 id="NextSteps">Next Steps</h2>
 <p>Once you have completed installation, you are ready to
 begin developing applications. Here are a few ways you can get started: </p>
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index c0b27fe..28ccc43 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -619,6 +619,38 @@
 bool InputDispatcher::dispatchKeyLocked(
         nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
         DropReason* dropReason, nsecs_t* nextWakeupTime) {
+    // Preprocessing.
+    if (! entry->dispatchInProgress) {
+        if (entry->repeatCount == 0
+                && entry->action == AKEY_EVENT_ACTION_DOWN
+                && (entry->policyFlags & POLICY_FLAG_TRUSTED)
+                && !entry->isInjected()) {
+            if (mKeyRepeatState.lastKeyEntry
+                    && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+                // We have seen two identical key downs in a row which indicates that the device
+                // driver is automatically generating key repeats itself.  We take note of the
+                // repeat here, but we disable our own next key repeat timer since it is clear that
+                // we will not need to synthesize key repeats ourselves.
+                entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
+                resetKeyRepeatLocked();
+                mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
+            } else {
+                // Not a repeat.  Save key down state in case we do see a repeat later.
+                resetKeyRepeatLocked();
+                mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
+            }
+            mKeyRepeatState.lastKeyEntry = entry;
+            entry->refCount += 1;
+        } else if (! entry->syntheticRepeat) {
+            resetKeyRepeatLocked();
+        }
+
+        entry->dispatchInProgress = true;
+        resetTargetsLocked();
+
+        logOutboundKeyDetailsLocked("dispatchKey - ", entry);
+    }
+
     // Give the policy a chance to intercept the key.
     if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
         if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
@@ -647,38 +679,6 @@
         return true;
     }
 
-    // Preprocessing.
-    if (! entry->dispatchInProgress) {
-        logOutboundKeyDetailsLocked("dispatchKey - ", entry);
-
-        if (entry->repeatCount == 0
-                && entry->action == AKEY_EVENT_ACTION_DOWN
-                && (entry->policyFlags & POLICY_FLAG_TRUSTED)
-                && !entry->isInjected()) {
-            if (mKeyRepeatState.lastKeyEntry
-                    && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
-                // We have seen two identical key downs in a row which indicates that the device
-                // driver is automatically generating key repeats itself.  We take note of the
-                // repeat here, but we disable our own next key repeat timer since it is clear that
-                // we will not need to synthesize key repeats ourselves.
-                entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
-                resetKeyRepeatLocked();
-                mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
-            } else {
-                // Not a repeat.  Save key down state in case we do see a repeat later.
-                resetKeyRepeatLocked();
-                mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
-            }
-            mKeyRepeatState.lastKeyEntry = entry;
-            entry->refCount += 1;
-        } else if (! entry->syntheticRepeat) {
-            resetKeyRepeatLocked();
-        }
-
-        entry->dispatchInProgress = true;
-        resetTargetsLocked();
-    }
-
     // Identify targets.
     if (! mCurrentInputTargetsValid) {
         int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
@@ -705,16 +705,24 @@
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
             "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
-            "downTime=%lld",
+            "repeatCount=%d, downTime=%lld",
             prefix,
             entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
             entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
-            entry->downTime);
+            entry->repeatCount, entry->downTime);
 #endif
 }
 
 bool InputDispatcher::dispatchMotionLocked(
         nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
+    // Preprocessing.
+    if (! entry->dispatchInProgress) {
+        entry->dispatchInProgress = true;
+        resetTargetsLocked();
+
+        logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
+    }
+
     // Clean up if dropping the event.
     if (*dropReason != DROP_REASON_NOT_DROPPED) {
         resetTargetsLocked();
@@ -723,14 +731,6 @@
         return true;
     }
 
-    // Preprocessing.
-    if (! entry->dispatchInProgress) {
-        logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
-
-        entry->dispatchInProgress = true;
-        resetTargetsLocked();
-    }
-
     bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
 
     // Identify targets.
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index d167439..3197ab2 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -934,7 +934,7 @@
             ssize_t keyDownIndex = findKeyDownLocked(scanCode);
             if (keyDownIndex >= 0) {
                 // key repeat, be sure to use same keycode as before in case of rotation
-                keyCode = mLocked.keyDowns.top().keyCode;
+                keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
             } else {
                 // key down
                 mLocked.keyDowns.push();
@@ -949,7 +949,7 @@
             ssize_t keyDownIndex = findKeyDownLocked(scanCode);
             if (keyDownIndex >= 0) {
                 // key up, be sure to use same keycode as before in case of rotation
-                keyCode = mLocked.keyDowns.top().keyCode;
+                keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
                 mLocked.keyDowns.removeAt(size_t(keyDownIndex));
             } else {
                 // key was not actually down
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index a277bcb..43936a4 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -111,7 +111,6 @@
 import android.media.IAudioService;
 import android.media.AudioManager;
 
-import java.io.File;
 import java.util.ArrayList;
 
 /**
@@ -2115,12 +2114,8 @@
                     return getCurrentPortraitRotation(lastRotation);
             }
 
-            if (new File("/system/etc/allow_all_orientations").exists()) {
-                mOrientationListener.setAllow180Rotation(true);
-            } else {
-                mOrientationListener.setAllow180Rotation(
-                        orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
-            }
+            mOrientationListener.setAllow180Rotation(
+                    orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
 
             // case for nosensor meaning ignore sensor and consider only lid
             // or orientation sensor disabled
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
index 75f690f..7e17fdd 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -2,7 +2,13 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-    SensorService.cpp
+	GravitySensor.cpp \
+	LinearAccelerationSensor.cpp \
+	RotationVectorSensor.cpp \
+    SensorService.cpp \
+    SensorInterface.cpp \
+    SensorDevice.cpp \
+    SecondOrderLowPassFilter.cpp
 
 LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\"
 
diff --git a/services/sensorservice/GravitySensor.cpp b/services/sensorservice/GravitySensor.cpp
new file mode 100644
index 0000000..18bd359
--- /dev/null
+++ b/services/sensorservice/GravitySensor.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2010 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 <math.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+
+#include <hardware/sensors.h>
+
+#include "GravitySensor.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+GravitySensor::GravitySensor(sensor_t const* list, size_t count)
+    : mSensorDevice(SensorDevice::getInstance()),
+      mEnabled(false), mAccTime(0),
+      mLowPass(M_SQRT1_2, 1),
+      mX(mLowPass), mY(mLowPass), mZ(mLowPass)
+
+{
+    for (size_t i=0 ; i<count ; i++) {
+        if (list[i].type == SENSOR_TYPE_ACCELEROMETER) {
+            mAccelerometer = Sensor(list + i);
+            break;
+        }
+    }
+}
+
+bool GravitySensor::process(sensors_event_t* outEvent,
+        const sensors_event_t& event)
+{
+    const static double NS2S = 1.0 / 1000000000.0;
+    if (event.type == SENSOR_TYPE_ACCELEROMETER) {
+        float x, y, z;
+        const double now = event.timestamp * NS2S;
+        if (mAccTime == 0) {
+            x = mX.init(event.acceleration.x);
+            y = mY.init(event.acceleration.y);
+            z = mZ.init(event.acceleration.z);
+        } else {
+            double dT = now - mAccTime;
+            mLowPass.setSamplingPeriod(dT);
+            x = mX(event.acceleration.x);
+            y = mY(event.acceleration.y);
+            z = mZ(event.acceleration.z);
+        }
+        mAccTime = now;
+        *outEvent = event;
+        outEvent->data[0] = x;
+        outEvent->data[1] = y;
+        outEvent->data[2] = z;
+        outEvent->sensor = '_grv';
+        outEvent->type = SENSOR_TYPE_GRAVITY;
+        return true;
+    }
+    return false;
+}
+
+bool GravitySensor::isEnabled() const {
+    return mEnabled;
+}
+
+status_t GravitySensor::activate(void* ident, bool enabled) {
+    status_t err = mSensorDevice.activate(this, mAccelerometer.getHandle(), enabled);
+    if (err == NO_ERROR) {
+        mEnabled = enabled;
+        if (enabled) {
+            mAccTime = 0;
+        }
+    }
+    return err;
+}
+
+status_t GravitySensor::setDelay(void* ident, int handle, int64_t ns)
+{
+    return mSensorDevice.setDelay(this, mAccelerometer.getHandle(), ns);
+}
+
+Sensor GravitySensor::getSensor() const {
+    sensor_t hwSensor;
+    hwSensor.name       = "Gravity Sensor";
+    hwSensor.vendor     = "Google Inc.";
+    hwSensor.version    = 1;
+    hwSensor.handle     = '_grv';
+    hwSensor.type       = SENSOR_TYPE_GRAVITY;
+    hwSensor.maxRange   = mAccelerometer.getMaxValue();
+    hwSensor.resolution = mAccelerometer.getResolution();
+    hwSensor.power      = mAccelerometer.getPowerUsage();
+    hwSensor.minDelay   = mAccelerometer.getMinDelay();
+    Sensor sensor(&hwSensor);
+    return sensor;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/services/sensorservice/GravitySensor.h b/services/sensorservice/GravitySensor.h
new file mode 100644
index 0000000..f9850b7
--- /dev/null
+++ b/services/sensorservice/GravitySensor.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 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_GRAVITY_SENSOR_H
+#define ANDROID_GRAVITY_SENSOR_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/Sensor.h>
+
+#include "SensorDevice.h"
+#include "SensorInterface.h"
+#include "SecondOrderLowPassFilter.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class GravitySensor : public SensorInterface {
+    SensorDevice& mSensorDevice;
+    Sensor mAccelerometer;
+    bool mEnabled;
+    double mAccTime;
+
+    SecondOrderLowPassFilter mLowPass;
+    BiquadFilter mX, mY, mZ;
+
+public:
+    GravitySensor(sensor_t const* list, size_t count);
+    virtual bool process(sensors_event_t* outEvent,
+            const sensors_event_t& event);
+    virtual bool isEnabled() const;
+    virtual status_t activate(void* ident, bool enabled);
+    virtual status_t setDelay(void* ident, int handle, int64_t ns);
+    virtual Sensor getSensor() const;
+    virtual bool isVirtual() const { return true; }
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GRAVITY_SENSOR_H
diff --git a/services/sensorservice/LinearAccelerationSensor.cpp b/services/sensorservice/LinearAccelerationSensor.cpp
new file mode 100644
index 0000000..2dc12dc
--- /dev/null
+++ b/services/sensorservice/LinearAccelerationSensor.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 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 <math.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+
+#include <hardware/sensors.h>
+
+#include "LinearAccelerationSensor.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+LinearAccelerationSensor::LinearAccelerationSensor(sensor_t const* list, size_t count)
+    : mSensorDevice(SensorDevice::getInstance()),
+      mGravitySensor(list, count)
+{
+    mData[0] = mData[1] = mData[2] = 0;
+}
+
+bool LinearAccelerationSensor::process(sensors_event_t* outEvent,
+        const sensors_event_t& event)
+{
+    bool result = mGravitySensor.process(outEvent, event);
+    if (result) {
+        if (event.type == SENSOR_TYPE_ACCELEROMETER) {
+            mData[0] = event.acceleration.x;
+            mData[1] = event.acceleration.y;
+            mData[2] = event.acceleration.z;
+        }
+        outEvent->data[0] = mData[0] - outEvent->data[0];
+        outEvent->data[1] = mData[1] - outEvent->data[1];
+        outEvent->data[2] = mData[2] - outEvent->data[2];
+        outEvent->sensor = '_lin';
+        outEvent->type = SENSOR_TYPE_LINEAR_ACCELERATION;
+    }
+    return result;
+}
+
+bool LinearAccelerationSensor::isEnabled() const {
+    return mGravitySensor.isEnabled();
+}
+
+status_t LinearAccelerationSensor::activate(void* ident, bool enabled) {
+    return mGravitySensor.activate(ident, enabled);
+}
+
+status_t LinearAccelerationSensor::setDelay(void* ident, int handle, int64_t ns) {
+    return mGravitySensor.setDelay(ident, handle, ns);
+}
+
+Sensor LinearAccelerationSensor::getSensor() const {
+    Sensor gsensor(mGravitySensor.getSensor());
+    sensor_t hwSensor;
+    hwSensor.name       = "Linear Acceleration Sensor";
+    hwSensor.vendor     = "Google Inc.";
+    hwSensor.version    = 1;
+    hwSensor.handle     = '_lin';
+    hwSensor.type       = SENSOR_TYPE_LINEAR_ACCELERATION;
+    hwSensor.maxRange   = gsensor.getMaxValue();
+    hwSensor.resolution = gsensor.getResolution();
+    hwSensor.power      = gsensor.getPowerUsage();
+    hwSensor.minDelay   = gsensor.getMinDelay();
+    Sensor sensor(&hwSensor);
+    return sensor;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/services/sensorservice/LinearAccelerationSensor.h b/services/sensorservice/LinearAccelerationSensor.h
new file mode 100644
index 0000000..ee918ce
--- /dev/null
+++ b/services/sensorservice/LinearAccelerationSensor.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 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_LINEAR_ACCELERATION_SENSOR_H
+#define ANDROID_LINEAR_ACCELERATION_SENSOR_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/Sensor.h>
+
+#include "SensorDevice.h"
+#include "SensorInterface.h"
+#include "GravitySensor.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class LinearAccelerationSensor : public SensorInterface {
+    SensorDevice& mSensorDevice;
+    GravitySensor mGravitySensor;
+    float mData[3];
+
+    virtual bool process(sensors_event_t* outEvent,
+            const sensors_event_t& event);
+public:
+    LinearAccelerationSensor(sensor_t const* list, size_t count);
+    virtual bool isEnabled() const;
+    virtual status_t activate(void* ident, bool enabled);
+    virtual status_t setDelay(void* ident, int handle, int64_t ns);
+    virtual Sensor getSensor() const;
+    virtual bool isVirtual() const { return true; }
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_LINEAR_ACCELERATION_SENSOR_H
diff --git a/services/sensorservice/RotationVectorSensor.cpp b/services/sensorservice/RotationVectorSensor.cpp
new file mode 100644
index 0000000..6f4b8be
--- /dev/null
+++ b/services/sensorservice/RotationVectorSensor.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 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 <math.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+
+#include <hardware/sensors.h>
+
+#include "RotationVectorSensor.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+template <typename T>
+static inline T clamp(T v) {
+    return v < 0 ? 0 : v;
+}
+
+RotationVectorSensor::RotationVectorSensor(sensor_t const* list, size_t count)
+    : mSensorDevice(SensorDevice::getInstance()),
+      mEnabled(false),
+      mALowPass(M_SQRT1_2, 5.0f),
+      mAX(mALowPass), mAY(mALowPass), mAZ(mALowPass),
+      mMLowPass(M_SQRT1_2, 2.5f),
+      mMX(mMLowPass), mMY(mMLowPass), mMZ(mMLowPass)
+{
+    for (size_t i=0 ; i<count ; i++) {
+        if (list[i].type == SENSOR_TYPE_ACCELEROMETER) {
+            mAcc = Sensor(list + i);
+        }
+        if (list[i].type == SENSOR_TYPE_MAGNETIC_FIELD) {
+            mMag = Sensor(list + i);
+        }
+    }
+    memset(mMagData, 0, sizeof(mMagData));
+}
+
+bool RotationVectorSensor::process(sensors_event_t* outEvent,
+        const sensors_event_t& event)
+{
+    const static double NS2S = 1.0 / 1000000000.0;
+    if (event.type == SENSOR_TYPE_MAGNETIC_FIELD) {
+        const double now = event.timestamp * NS2S;
+        if (mMagTime == 0) {
+            mMagData[0] = mMX.init(event.magnetic.x);
+            mMagData[1] = mMY.init(event.magnetic.y);
+            mMagData[2] = mMZ.init(event.magnetic.z);
+        } else {
+            double dT = now - mMagTime;
+            mMLowPass.setSamplingPeriod(dT);
+            mMagData[0] = mMX(event.magnetic.x);
+            mMagData[1] = mMY(event.magnetic.y);
+            mMagData[2] = mMZ(event.magnetic.z);
+        }
+        mMagTime = now;
+    }
+    if (event.type == SENSOR_TYPE_ACCELEROMETER) {
+        const double now = event.timestamp * NS2S;
+        float Ax, Ay, Az;
+        if (mAccTime == 0) {
+            Ax = mAX.init(event.acceleration.x);
+            Ay = mAY.init(event.acceleration.y);
+            Az = mAZ.init(event.acceleration.z);
+        } else {
+            double dT = now - mAccTime;
+            mALowPass.setSamplingPeriod(dT);
+            Ax = mAX(event.acceleration.x);
+            Ay = mAY(event.acceleration.y);
+            Az = mAZ(event.acceleration.z);
+        }
+        mAccTime = now;
+        const float Ex = mMagData[0];
+        const float Ey = mMagData[1];
+        const float Ez = mMagData[2];
+        float Hx = Ey*Az - Ez*Ay;
+        float Hy = Ez*Ax - Ex*Az;
+        float Hz = Ex*Ay - Ey*Ax;
+        const float normH = sqrtf(Hx*Hx + Hy*Hy + Hz*Hz);
+        if (normH < 0.1f) {
+            // device is close to free fall (or in space?), or close to
+            // magnetic north pole. Typical values are  > 100.
+            return false;
+        }
+        const float invH = 1.0f / normH;
+        const float invA = 1.0f / sqrtf(Ax*Ax + Ay*Ay + Az*Az);
+        Hx *= invH;
+        Hy *= invH;
+        Hz *= invH;
+        Ax *= invA;
+        Ay *= invA;
+        Az *= invA;
+        const float Mx = Ay*Hz - Az*Hy;
+        const float My = Az*Hx - Ax*Hz;
+        const float Mz = Ax*Hy - Ay*Hx;
+
+        // matrix to rotation vector (normalized quaternion)
+        float qw = sqrtf( clamp( Hx + My + Az + 1) * 0.25f );
+        float qx = sqrtf( clamp( Hx - My - Az + 1) * 0.25f );
+        float qy = sqrtf( clamp(-Hx + My - Az + 1) * 0.25f );
+        float qz = sqrtf( clamp(-Hx - My + Az + 1) * 0.25f );
+        const float n = 1.0f / (qw*qw + qx*qx + qy*qy + qz*qz);
+        qx = copysignf(qx, Ay - Mz) * n;
+        qy = copysignf(qy, Hz - Ax) * n;
+        qz = copysignf(qz, Mx - Hy) * n;
+
+        *outEvent = event;
+        outEvent->data[0] = qx;
+        outEvent->data[1] = qy;
+        outEvent->data[2] = qz;
+        outEvent->sensor = '_rov';
+        outEvent->type = SENSOR_TYPE_ROTATION_VECTOR;
+        return true;
+    }
+    return false;
+}
+
+bool RotationVectorSensor::isEnabled() const {
+    return mEnabled;
+}
+
+status_t RotationVectorSensor::activate(void* ident, bool enabled) {
+    if (mEnabled != enabled) {
+        mSensorDevice.activate(this, mAcc.getHandle(), enabled);
+        mSensorDevice.activate(this, mMag.getHandle(), enabled);
+        mEnabled = enabled;
+        if (enabled) {
+            mMagTime = 0;
+            mAccTime = 0;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t RotationVectorSensor::setDelay(void* ident, int handle, int64_t ns)
+{
+    mSensorDevice.setDelay(this, mAcc.getHandle(), ns);
+    mSensorDevice.setDelay(this, mMag.getHandle(), ns);
+    return NO_ERROR;
+}
+
+Sensor RotationVectorSensor::getSensor() const {
+    sensor_t hwSensor;
+    hwSensor.name       = "Rotation Vector Sensor";
+    hwSensor.vendor     = "Google Inc.";
+    hwSensor.version    = 1;
+    hwSensor.handle     = '_rov';
+    hwSensor.type       = SENSOR_TYPE_ROTATION_VECTOR;
+    hwSensor.maxRange   = 1;
+    hwSensor.resolution = 1.0f / (1<<24);
+    hwSensor.power      = mAcc.getPowerUsage() + mMag.getPowerUsage();
+    hwSensor.minDelay   = mAcc.getMinDelay();
+    Sensor sensor(&hwSensor);
+    return sensor;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/services/sensorservice/RotationVectorSensor.h b/services/sensorservice/RotationVectorSensor.h
new file mode 100644
index 0000000..e7f28c9
--- /dev/null
+++ b/services/sensorservice/RotationVectorSensor.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010 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_ROTATION_VECTOR_SENSOR_H
+#define ANDROID_ROTATION_VECTOR_SENSOR_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/Sensor.h>
+
+#include "SensorDevice.h"
+#include "SensorInterface.h"
+#include "SecondOrderLowPassFilter.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class RotationVectorSensor : public SensorInterface {
+    SensorDevice& mSensorDevice;
+    Sensor mAcc;
+    Sensor mMag;
+    bool mEnabled;
+    float mMagData[3];
+    double mAccTime;
+    double mMagTime;
+    SecondOrderLowPassFilter mALowPass;
+    BiquadFilter mAX, mAY, mAZ;
+    SecondOrderLowPassFilter mMLowPass;
+    BiquadFilter mMX, mMY, mMZ;
+
+public:
+    RotationVectorSensor(sensor_t const* list, size_t count);
+    virtual bool process(sensors_event_t* outEvent,
+            const sensors_event_t& event);
+    virtual bool isEnabled() const;
+    virtual status_t activate(void* ident, bool enabled);
+    virtual status_t setDelay(void* ident, int handle, int64_t ns);
+    virtual Sensor getSensor() const;
+    virtual bool isVirtual() const { return true; }
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_ROTATION_VECTOR_SENSOR_H
diff --git a/services/sensorservice/SecondOrderLowPassFilter.cpp b/services/sensorservice/SecondOrderLowPassFilter.cpp
new file mode 100644
index 0000000..e13e136
--- /dev/null
+++ b/services/sensorservice/SecondOrderLowPassFilter.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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 <math.h>
+
+#include <cutils/log.h>
+
+#include "SecondOrderLowPassFilter.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+SecondOrderLowPassFilter::SecondOrderLowPassFilter(float Q, float fc)
+    : iQ(1.0f / Q), fc(fc)
+{
+}
+
+void SecondOrderLowPassFilter::setSamplingPeriod(float dT)
+{
+    K = tanf(float(M_PI) * fc * dT);
+    iD = 1.0f / (K*K + K*iQ + 1);
+    a0 = K*K*iD;
+    a1 = 2.0f * a0;
+    b1 = 2.0f*(K*K - 1)*iD;
+    b2 = (K*K - K*iQ + 1)*iD;
+}
+
+// ---------------------------------------------------------------------------
+
+BiquadFilter::BiquadFilter(const SecondOrderLowPassFilter& s)
+    : s(s)
+{
+}
+
+float BiquadFilter::init(float x)
+{
+    x1 = x2 = x;
+    y1 = y2 = x;
+    return x;
+}
+
+float BiquadFilter::operator()(float x)
+{
+    float y = (x + x2)*s.a0 + x1*s.a1 - y1*s.b1 - y2*s.b2;
+    x2 = x1;
+    y2 = y1;
+    x1 = x;
+    y1 = y;
+    return y;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/sensorservice/SecondOrderLowPassFilter.h b/services/sensorservice/SecondOrderLowPassFilter.h
new file mode 100644
index 0000000..998ca35b9
--- /dev/null
+++ b/services/sensorservice/SecondOrderLowPassFilter.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 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_SECOND_ORDER_LOW_PASS_FILTER_H
+#define ANDROID_SECOND_ORDER_LOW_PASS_FILTER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class BiquadFilter;
+
+/*
+ * State of a 2nd order low-pass IIR filter
+ */
+class SecondOrderLowPassFilter {
+    friend class BiquadFilter;
+    float iQ, fc;
+    float K, iD;
+    float a0, a1;
+    float b1, b2;
+public:
+    SecondOrderLowPassFilter(float Q, float fc);
+    void setSamplingPeriod(float dT);
+};
+
+/*
+ * Implements a Biquad IIR filter
+ */
+class BiquadFilter {
+    float x1, x2;
+    float y1, y2;
+    const SecondOrderLowPassFilter& s;
+public:
+    BiquadFilter(const SecondOrderLowPassFilter& s);
+    float init(float in);
+    float operator()(float in);
+};
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SECOND_ORDER_LOW_PASS_FILTER_H
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
new file mode 100644
index 0000000..73f85ba
--- /dev/null
+++ b/services/sensorservice/SensorDevice.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2010 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 <math.h>
+#include <sys/types.h>
+
+#include <utils/Atomic.h>
+#include <utils/Errors.h>
+#include <utils/Singleton.h>
+
+#include <binder/BinderService.h>
+#include <binder/Parcel.h>
+#include <binder/IServiceManager.h>
+
+#include <hardware/sensors.h>
+
+#include "SensorDevice.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+class BatteryService : public Singleton<BatteryService> {
+    static const int TRANSACTION_noteStartSensor = IBinder::FIRST_CALL_TRANSACTION + 3;
+    static const int TRANSACTION_noteStopSensor = IBinder::FIRST_CALL_TRANSACTION + 4;
+    static const String16 DESCRIPTOR;
+
+    friend class Singleton<BatteryService>;
+    sp<IBinder> mBatteryStatService;
+
+    BatteryService() {
+        const sp<IServiceManager> sm(defaultServiceManager());
+        if (sm != NULL) {
+            const String16 name("batteryinfo");
+            mBatteryStatService = sm->getService(name);
+        }
+    }
+
+    status_t noteStartSensor(int uid, int handle) {
+        Parcel data, reply;
+        data.writeInterfaceToken(DESCRIPTOR);
+        data.writeInt32(uid);
+        data.writeInt32(handle);
+        status_t err = mBatteryStatService->transact(
+                TRANSACTION_noteStartSensor, data, &reply, 0);
+        err = reply.readExceptionCode();
+        return err;
+    }
+
+    status_t noteStopSensor(int uid, int handle) {
+        Parcel data, reply;
+        data.writeInterfaceToken(DESCRIPTOR);
+        data.writeInt32(uid);
+        data.writeInt32(handle);
+        status_t err = mBatteryStatService->transact(
+                TRANSACTION_noteStopSensor, data, &reply, 0);
+        err = reply.readExceptionCode();
+        return err;
+    }
+
+public:
+    void enableSensor(int handle) {
+        if (mBatteryStatService != 0) {
+            int uid = IPCThreadState::self()->getCallingUid();
+            int64_t identity = IPCThreadState::self()->clearCallingIdentity();
+            noteStartSensor(uid, handle);
+            IPCThreadState::self()->restoreCallingIdentity(identity);
+        }
+    }
+    void disableSensor(int handle) {
+        if (mBatteryStatService != 0) {
+            int uid = IPCThreadState::self()->getCallingUid();
+            int64_t identity = IPCThreadState::self()->clearCallingIdentity();
+            noteStopSensor(uid, handle);
+            IPCThreadState::self()->restoreCallingIdentity(identity);
+        }
+    }
+};
+
+const String16 BatteryService::DESCRIPTOR("com.android.internal.app.IBatteryStats");
+
+ANDROID_SINGLETON_STATIC_INSTANCE(BatteryService)
+
+// ---------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE(SensorDevice)
+
+SensorDevice::SensorDevice()
+    :  mSensorDevice(0),
+       mSensorModule(0)
+{
+    status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
+            (hw_module_t const**)&mSensorModule);
+
+    LOGE_IF(err, "couldn't load %s module (%s)",
+            SENSORS_HARDWARE_MODULE_ID, strerror(-err));
+
+    if (mSensorModule) {
+        err = sensors_open(&mSensorModule->common, &mSensorDevice);
+
+        LOGE_IF(err, "couldn't open device for module %s (%s)",
+                SENSORS_HARDWARE_MODULE_ID, strerror(-err));
+
+        if (mSensorDevice) {
+            sensor_t const* list;
+            ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);
+            mActivationCount.setCapacity(count);
+            Info model;
+            for (size_t i=0 ; i<size_t(count) ; i++) {
+                mActivationCount.add(list[i].handle, model);
+                mSensorDevice->activate(mSensorDevice, list[i].handle, 0);
+            }
+        }
+    }
+}
+
+void SensorDevice::dump(String8& result, char* buffer, size_t SIZE)
+{
+    if (!mSensorModule) return;
+    sensor_t const* list;
+    ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);
+
+    snprintf(buffer, SIZE, "%d h/w sensors:\n", int(count));
+    result.append(buffer);
+
+    Mutex::Autolock _l(mLock);
+    for (size_t i=0 ; i<size_t(count) ; i++) {
+        snprintf(buffer, SIZE, "handle=0x%08x, active-count=%d / %d\n",
+                list[i].handle,
+                mActivationCount.valueFor(list[i].handle).count,
+                mActivationCount.valueFor(list[i].handle).rates.size());
+        result.append(buffer);
+    }
+}
+
+ssize_t SensorDevice::getSensorList(sensor_t const** list) {
+    if (!mSensorModule) return NO_INIT;
+    ssize_t count = mSensorModule->get_sensors_list(mSensorModule, list);
+    return count;
+}
+
+status_t SensorDevice::initCheck() const {
+    return mSensorDevice && mSensorModule ? NO_ERROR : NO_INIT;
+}
+
+ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
+    if (!mSensorDevice) return NO_INIT;
+    return mSensorDevice->poll(mSensorDevice, buffer, count);
+}
+
+status_t SensorDevice::activate(void* ident, int handle, int enabled)
+{
+    if (!mSensorDevice) return NO_INIT;
+    status_t err(NO_ERROR);
+    bool actuateHardware = false;
+
+    Info& info( mActivationCount.editValueFor(handle) );
+    int32_t& count(info.count);
+    if (enabled) {
+        if (android_atomic_inc(&count) == 0) {
+            actuateHardware = true;
+        }
+        Mutex::Autolock _l(mLock);
+        if (info.rates.indexOfKey(ident) < 0) {
+            info.rates.add(ident, DEFAULT_EVENTS_PERIOD);
+        }
+    } else {
+        if (android_atomic_dec(&count) == 1) {
+            actuateHardware = true;
+        }
+        Mutex::Autolock _l(mLock);
+        info.rates.removeItem(ident);
+    }
+    if (actuateHardware) {
+        err = mSensorDevice->activate(mSensorDevice, handle, enabled);
+        if (enabled) {
+            LOGE_IF(err, "Error activating sensor %d (%s)", handle, strerror(-err));
+            if (err == 0) {
+                BatteryService::getInstance().enableSensor(handle);
+            }
+        } else {
+            if (err == 0) {
+                BatteryService::getInstance().disableSensor(handle);
+            }
+        }
+    }
+
+    if (!actuateHardware || enabled) {
+        Mutex::Autolock _l(mLock);
+        nsecs_t ns = info.rates.valueAt(0);
+        for (size_t i=1 ; i<info.rates.size() ; i++) {
+            if (info.rates.valueAt(i) < ns) {
+                nsecs_t cur = info.rates.valueAt(i);
+                if (cur < ns) {
+                    ns = cur;
+                }
+            }
+        }
+        mSensorDevice->setDelay(mSensorDevice, handle, ns);
+    }
+
+    return err;
+}
+
+status_t SensorDevice::setDelay(void* ident, int handle, int64_t ns)
+{
+    if (!mSensorDevice) return NO_INIT;
+    Info& info( mActivationCount.editValueFor(handle) );
+    { // scope for lock
+        Mutex::Autolock _l(mLock);
+        ssize_t index = info.rates.indexOfKey(ident);
+        if (index < 0) return BAD_INDEX;
+        info.rates.editValueAt(index) = ns;
+        ns = info.rates.valueAt(0);
+        for (size_t i=1 ; i<info.rates.size() ; i++) {
+            nsecs_t cur = info.rates.valueAt(i);
+            if (cur < ns) {
+                ns = cur;
+            }
+        }
+    }
+    return mSensorDevice->setDelay(mSensorDevice, handle, ns);
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
new file mode 100644
index 0000000..63ecbcd
--- /dev/null
+++ b/services/sensorservice/SensorDevice.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 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_SENSOR_DEVICE_H
+#define ANDROID_SENSOR_DEVICE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/KeyedVector.h>
+#include <utils/Singleton.h>
+#include <utils/String8.h>
+
+#include <gui/Sensor.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+static const nsecs_t DEFAULT_EVENTS_PERIOD = 200000000; //    5 Hz
+
+class SensorDevice : public Singleton<SensorDevice> {
+    friend class Singleton<SensorDevice>;
+    struct sensors_poll_device_t* mSensorDevice;
+    struct sensors_module_t* mSensorModule;
+    Mutex mLock; // protect mActivationCount[].rates
+    // fixed-size array after construction
+    struct Info {
+        Info() : count(0) { }
+        int32_t count;
+        KeyedVector<void*, nsecs_t> rates;
+    };
+    DefaultKeyedVector<int, Info> mActivationCount;
+
+    SensorDevice();
+public:
+    ssize_t getSensorList(sensor_t const** list);
+    status_t initCheck() const;
+    ssize_t poll(sensors_event_t* buffer, size_t count);
+    status_t activate(void* ident, int handle, int enabled);
+    status_t setDelay(void* ident, int handle, int64_t ns);
+    void dump(String8& result, char* buffer, size_t SIZE);
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SENSOR_DEVICE_H
diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp
new file mode 100644
index 0000000..93d23d9
--- /dev/null
+++ b/services/sensorservice/SensorInterface.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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 <cutils/log.h>
+
+#include "SensorInterface.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+SensorInterface::~SensorInterface()
+{
+}
+
+// ---------------------------------------------------------------------------
+
+HardwareSensor::HardwareSensor(const sensor_t& sensor)
+    : mSensorDevice(SensorDevice::getInstance()),
+      mSensor(&sensor), mEnabled(false)
+{
+    LOGI("%s", sensor.name);
+}
+
+HardwareSensor::~HardwareSensor() {
+}
+
+bool HardwareSensor::process(sensors_event_t* outEvent,
+        const sensors_event_t& event) {
+    *outEvent = event;
+    return true;
+}
+
+bool HardwareSensor::isEnabled() const {
+    return mEnabled;
+}
+
+status_t HardwareSensor::activate(void* ident,bool enabled) {
+    status_t err = mSensorDevice.activate(ident, mSensor.getHandle(), enabled);
+    if (err == NO_ERROR)
+        mEnabled = enabled;
+    return err;
+}
+
+status_t HardwareSensor::setDelay(void* ident, int handle, int64_t ns) {
+    return mSensorDevice.setDelay(ident, handle, ns);
+}
+
+Sensor HardwareSensor::getSensor() const {
+    return mSensor;
+}
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h
new file mode 100644
index 0000000..eebd563
--- /dev/null
+++ b/services/sensorservice/SensorInterface.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 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_SENSOR_INTERFACE_H
+#define ANDROID_SENSOR_INTERFACE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Singleton.h>
+
+#include <gui/Sensor.h>
+
+#include "SensorDevice.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class SensorInterface {
+public:
+    virtual ~SensorInterface();
+
+    virtual bool process(sensors_event_t* outEvent,
+            const sensors_event_t& event) = 0;
+
+    virtual bool isEnabled() const = 0;
+    virtual status_t activate(void* ident, bool enabled) = 0;
+    virtual status_t setDelay(void* ident, int handle, int64_t ns) = 0;
+    virtual Sensor getSensor() const = 0;
+    virtual bool isVirtual() const = 0;
+};
+
+// ---------------------------------------------------------------------------
+
+class HardwareSensor : public SensorInterface
+{
+    SensorDevice& mSensorDevice;
+    Sensor mSensor;
+    bool mEnabled;
+
+public:
+    HardwareSensor(const sensor_t& sensor);
+
+    virtual ~HardwareSensor();
+
+    virtual bool process(sensors_event_t* outEvent,
+            const sensors_event_t& event);
+
+    virtual bool isEnabled() const;
+    virtual status_t activate(void* ident, bool enabled);
+    virtual status_t setDelay(void* ident, int handle, int64_t ns);
+    virtual Sensor getSensor() const;
+    virtual bool isVirtual() const { return false; }
+};
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SENSOR_INTERFACE_H
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index c9ab992..ea5e5cc 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <math.h>
 #include <sys/types.h>
 
 #include <utils/SortedVector.h>
@@ -35,80 +36,15 @@
 #include <hardware/sensors.h>
 
 #include "SensorService.h"
+#include "GravitySensor.h"
+#include "LinearAccelerationSensor.h"
+#include "RotationVectorSensor.h"
 
 namespace android {
 // ---------------------------------------------------------------------------
 
-class BatteryService : public Singleton<BatteryService> {
-    static const int TRANSACTION_noteStartSensor = IBinder::FIRST_CALL_TRANSACTION + 3;
-    static const int TRANSACTION_noteStopSensor = IBinder::FIRST_CALL_TRANSACTION + 4;
-    static const String16 DESCRIPTOR;
-
-    friend class Singleton<BatteryService>;
-    sp<IBinder> mBatteryStatService;
-
-    BatteryService() {
-        const sp<IServiceManager> sm(defaultServiceManager());
-        if (sm != NULL) {
-            const String16 name("batteryinfo");
-            mBatteryStatService = sm->getService(name);
-        }
-    }
-
-    status_t noteStartSensor(int uid, int handle) {
-        Parcel data, reply;
-        data.writeInterfaceToken(DESCRIPTOR);
-        data.writeInt32(uid);
-        data.writeInt32(handle);
-        status_t err = mBatteryStatService->transact(
-                TRANSACTION_noteStartSensor, data, &reply, 0);
-        err = reply.readExceptionCode();
-        return err;
-    }
-
-    status_t noteStopSensor(int uid, int handle) {
-        Parcel data, reply;
-        data.writeInterfaceToken(DESCRIPTOR);
-        data.writeInt32(uid);
-        data.writeInt32(handle);
-        status_t err = mBatteryStatService->transact(
-                TRANSACTION_noteStopSensor, data, &reply, 0);
-        err = reply.readExceptionCode();
-        return err;
-    }
-
-public:
-    void enableSensor(int handle) {
-        if (mBatteryStatService != 0) {
-            int uid = IPCThreadState::self()->getCallingUid();
-            int64_t identity = IPCThreadState::self()->clearCallingIdentity();
-            noteStartSensor(uid, handle);
-            IPCThreadState::self()->restoreCallingIdentity(identity);
-        }
-    }
-    void disableSensor(int handle) {
-        if (mBatteryStatService != 0) {
-            int uid = IPCThreadState::self()->getCallingUid();
-            int64_t identity = IPCThreadState::self()->clearCallingIdentity();
-            noteStopSensor(uid, handle);
-            IPCThreadState::self()->restoreCallingIdentity(identity);
-        }
-    }
-};
-
-const String16 BatteryService::DESCRIPTOR("com.android.internal.app.IBatteryStats");
-
-ANDROID_SINGLETON_STATIC_INSTANCE(BatteryService)
-
-// ---------------------------------------------------------------------------
-
-// 100 events/s max
-static const nsecs_t MINIMUM_EVENT_PERIOD = ms2ns(10);
-
 SensorService::SensorService()
     : Thread(false),
-      mSensorDevice(0),
-      mSensorModule(0),
       mDump("android.permission.DUMP"),
       mInitCheck(NO_INIT)
 {
@@ -118,43 +54,66 @@
 {
     LOGD("nuSensorService starting...");
 
-    status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
-            (hw_module_t const**)&mSensorModule);
+    SensorDevice& dev(SensorDevice::getInstance());
 
-    LOGE_IF(err, "couldn't load %s module (%s)",
-            SENSORS_HARDWARE_MODULE_ID, strerror(-err));
-
-    if (mSensorModule) {
-        err = sensors_open(&mSensorModule->common, &mSensorDevice);
-
-        LOGE_IF(err, "couldn't open device for module %s (%s)",
-                SENSORS_HARDWARE_MODULE_ID, strerror(-err));
-
-        sensors_event_t event;
-        memset(&event, 0, sizeof(event));
-
-        struct sensor_t const* list;
-        int count = mSensorModule->get_sensors_list(mSensorModule, &list);
+    if (dev.initCheck() == NO_ERROR) {
+        uint32_t virtualSensorsNeeds =
+                (1<<SENSOR_TYPE_GRAVITY) |
+                (1<<SENSOR_TYPE_LINEAR_ACCELERATION) |
+                (1<<SENSOR_TYPE_ROTATION_VECTOR);
+        sensor_t const* list;
+        int count = dev.getSensorList(&list);
         mLastEventSeen.setCapacity(count);
         for (int i=0 ; i<count ; i++) {
-            Sensor sensor(list + i);
-            LOGI("%s", sensor.getName().string());
-            mSensorList.add(sensor);
-            if (mSensorDevice) {
-                mSensorDevice->activate(mSensorDevice, sensor.getHandle(), 0);
+            registerSensor( new HardwareSensor(list[i]) );
+            switch (list[i].type) {
+                case SENSOR_TYPE_GRAVITY:
+                case SENSOR_TYPE_LINEAR_ACCELERATION:
+                case SENSOR_TYPE_ROTATION_VECTOR:
+                    virtualSensorsNeeds &= ~(1<<list[i].type);
+                    break;
             }
-            mLastEventSeen.add(sensor.getHandle(), event);
         }
 
-        if (mSensorDevice) {
-            run("SensorService", PRIORITY_URGENT_DISPLAY);
-            mInitCheck = NO_ERROR;
+        if (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) {
+            registerVirtualSensor( new GravitySensor(list, count) );
         }
+        if (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) {
+            registerVirtualSensor( new LinearAccelerationSensor(list, count) );
+        }
+        if (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) {
+            registerVirtualSensor( new RotationVectorSensor(list, count) );
+        }
+
+        run("SensorService", PRIORITY_URGENT_DISPLAY);
+        mInitCheck = NO_ERROR;
     }
 }
 
+void SensorService::registerSensor(SensorInterface* s)
+{
+    sensors_event_t event;
+    memset(&event, 0, sizeof(event));
+
+    const Sensor sensor(s->getSensor());
+    // add to the sensor list (returned to clients)
+    mSensorList.add(sensor);
+    // add to our handle->SensorInterface mapping
+    mSensorMap.add(sensor.getHandle(), s);
+    // create an entry in the mLastEventSeen array
+    mLastEventSeen.add(sensor.getHandle(), event);
+}
+
+void SensorService::registerVirtualSensor(SensorInterface* s)
+{
+    registerSensor(s);
+    mVirtualSensorList.add( s );
+}
+
 SensorService::~SensorService()
 {
+    for (size_t i=0 ; i<mSensorMap.size() ; i++)
+        delete mSensorMap.valueAt(i);
 }
 
 status_t SensorService::dump(int fd, const Vector<String16>& args)
@@ -175,7 +134,7 @@
         for (size_t i=0 ; i<mSensorList.size() ; i++) {
             const Sensor& s(mSensorList[i]);
             const sensors_event_t& e(mLastEventSeen.valueFor(s.getHandle()));
-            snprintf(buffer, SIZE, "%s (vendor=%s, handle=%d, maxRate=%.2fHz, last=<%5.1f,%5.1f,%5.1f>)\n",
+            snprintf(buffer, SIZE, "%-48s| %-32s | 0x%08x | maxRate=%7.2fHz | last=<%5.1f,%5.1f,%5.1f>\n",
                     s.getName().string(),
                     s.getVendor().string(),
                     s.getHandle(),
@@ -183,6 +142,7 @@
                     e.data[0], e.data[1], e.data[2]);
             result.append(buffer);
         }
+        SensorDevice::getInstance().dump(result, buffer, SIZE);
 
         snprintf(buffer, SIZE, "%d active connections\n",
                 mActiveConnections.size());
@@ -191,7 +151,7 @@
         result.append(buffer);
         for (size_t i=0 ; i<mActiveSensors.size() ; i++) {
             int handle = mActiveSensors.keyAt(i);
-            snprintf(buffer, SIZE, "%s (handle=%d, connections=%d)\n",
+            snprintf(buffer, SIZE, "%s (handle=0x%08x, connections=%d)\n",
                     getSensorName(handle).string(),
                     handle,
                     mActiveSensors.valueAt(i)->getNumConnections());
@@ -206,51 +166,96 @@
 {
     LOGD("nuSensorService thread starting...");
 
-    sensors_event_t buffer[16];
-    sensors_event_t scratch[16];
-    struct sensors_poll_device_t* device = mSensorDevice;
-    ssize_t count;
+    const size_t numEventMax = 16 * (1 + mVirtualSensorList.size());
+    sensors_event_t buffer[numEventMax];
+    sensors_event_t scratch[numEventMax];
+    SensorDevice& device(SensorDevice::getInstance());
+    const size_t vcount = mVirtualSensorList.size();
 
+    ssize_t count;
     do {
-        count = device->poll(device, buffer, sizeof(buffer)/sizeof(*buffer));
+        count = device.poll(buffer, numEventMax);
         if (count<0) {
             LOGE("sensor poll failed (%s)", strerror(-count));
             break;
         }
 
-        const SortedVector< wp<SensorEventConnection> > activeConnections(
-                getActiveConnections());
+        recordLastValue(buffer, count);
 
-        size_t numConnections = activeConnections.size();
-        if (numConnections) {
-            Mutex::Autolock _l(mLock);
-
-            // record the last event for each sensor
-            int32_t prev = buffer[0].sensor;
-            for (ssize_t i=1 ; i<count ; i++) {
-                // record the last event of each sensor type in this buffer
-                int32_t curr = buffer[i].sensor;
-                if (curr != prev) {
-                    mLastEventSeen.editValueFor(prev) = buffer[i-1];
-                    prev = curr;
+        // handle virtual sensors
+        if (count && vcount) {
+            const DefaultKeyedVector<int, SensorInterface*> virtualSensors(
+                    getActiveVirtualSensors());
+            const size_t activeVirtualSensorCount = virtualSensors.size();
+            if (activeVirtualSensorCount) {
+                size_t k = 0;
+                for (size_t i=0 ; i<size_t(count) ; i++) {
+                    sensors_event_t const * const event = buffer;
+                    for (size_t j=0 ; j<activeVirtualSensorCount ; j++) {
+                        sensors_event_t out;
+                        if (virtualSensors.valueAt(j)->process(&out, event[i])) {
+                            buffer[count + k] = out;
+                            k++;
+                        }
+                    }
                 }
-            }
-            mLastEventSeen.editValueFor(prev) = buffer[count-1];
-
-            for (size_t i=0 ; i<numConnections ; i++) {
-                sp<SensorEventConnection> connection(activeConnections[i].promote());
-                if (connection != 0) {
-                    connection->sendEvents(buffer, count, scratch);
+                if (k) {
+                    // record the last synthesized values
+                    recordLastValue(&buffer[count], k);
+                    count += k;
+                    // sort the buffer by time-stamps
+                    sortEventBuffer(buffer, count);
                 }
             }
         }
 
+        // send our events to clients...
+        const SortedVector< wp<SensorEventConnection> > activeConnections(
+                getActiveConnections());
+        size_t numConnections = activeConnections.size();
+        for (size_t i=0 ; i<numConnections ; i++) {
+            sp<SensorEventConnection> connection(
+                    activeConnections[i].promote());
+            if (connection != 0) {
+                connection->sendEvents(buffer, count, scratch);
+            }
+        }
     } while (count >= 0 || Thread::exitPending());
 
     LOGW("Exiting SensorService::threadLoop!");
     return false;
 }
 
+void SensorService::recordLastValue(
+        sensors_event_t const * buffer, size_t count)
+{
+    Mutex::Autolock _l(mLock);
+
+    // record the last event for each sensor
+    int32_t prev = buffer[0].sensor;
+    for (size_t i=1 ; i<count ; i++) {
+        // record the last event of each sensor type in this buffer
+        int32_t curr = buffer[i].sensor;
+        if (curr != prev) {
+            mLastEventSeen.editValueFor(prev) = buffer[i-1];
+            prev = curr;
+        }
+    }
+    mLastEventSeen.editValueFor(prev) = buffer[count-1];
+}
+
+void SensorService::sortEventBuffer(sensors_event_t* buffer, size_t count)
+{
+    struct compar {
+        static int cmp(void const* lhs, void const* rhs) {
+            sensors_event_t const* l = static_cast<sensors_event_t const*>(lhs);
+            sensors_event_t const* r = static_cast<sensors_event_t const*>(rhs);
+            return r->timestamp - l->timestamp;
+        }
+    };
+    qsort(buffer, count, sizeof(sensors_event_t), compar::cmp);
+}
+
 SortedVector< wp<SensorService::SensorEventConnection> >
 SensorService::getActiveConnections() const
 {
@@ -258,6 +263,13 @@
     return mActiveConnections;
 }
 
+DefaultKeyedVector<int, SensorInterface*>
+SensorService::getActiveVirtualSensors() const
+{
+    Mutex::Autolock _l(mLock);
+    return mActiveVirtualSensors;
+}
+
 String8 SensorService::getSensorName(int handle) const {
     size_t count = mSensorList.size();
     for (size_t i=0 ; i<count ; i++) {
@@ -288,8 +300,13 @@
     for (size_t i=0 ; i<size ; ) {
         SensorRecord* rec = mActiveSensors.valueAt(i);
         if (rec && rec->removeConnection(connection)) {
-            mSensorDevice->activate(mSensorDevice, mActiveSensors.keyAt(i), 0);
+            int handle = mActiveSensors.keyAt(i);
+            SensorInterface* sensor = mSensorMap.valueFor( handle );
+            if (sensor) {
+                sensor->activate(connection.unsafe_get(), false);
+            }
             mActiveSensors.removeItemsAt(i, 1);
+            mActiveVirtualSensors.removeItem(handle);
             delete rec;
             size--;
         } else {
@@ -305,39 +322,38 @@
     if (mInitCheck != NO_ERROR)
         return mInitCheck;
 
-    status_t err = NO_ERROR;
     Mutex::Autolock _l(mLock);
-    SensorRecord* rec = mActiveSensors.valueFor(handle);
-    if (rec == 0) {
-        rec = new SensorRecord(connection);
-        mActiveSensors.add(handle, rec);
-        err = mSensorDevice->activate(mSensorDevice, handle, 1);
-        LOGE_IF(err, "Error activating sensor %d (%s)", handle, strerror(-err));
-        if (err == 0) {
-            BatteryService::getInstance().enableSensor(handle);
-        }
-    } else {
-        if (rec->addConnection(connection)) {
-            // this sensor is already activated, but we are adding a
-            // connection that uses it. Immediately send down the last
-            // known value of the requested sensor.
-            sensors_event_t scratch;
-            sensors_event_t& event(mLastEventSeen.editValueFor(handle));
-            if (event.version == sizeof(sensors_event_t)) {
-                connection->sendEvents(&event, 1);
-            }
-        }
-    }
+    SensorInterface* sensor = mSensorMap.valueFor(handle);
+    status_t err = sensor ? sensor->activate(connection.get(), true) : status_t(BAD_VALUE);
     if (err == NO_ERROR) {
-        // connection now active
-        if (connection->addSensor(handle)) {
-            // the sensor was added (which means it wasn't already there)
-            // so, see if this connection becomes active
-            if (mActiveConnections.indexOf(connection) < 0) {
-                mActiveConnections.add(connection);
+        SensorRecord* rec = mActiveSensors.valueFor(handle);
+        if (rec == 0) {
+            rec = new SensorRecord(connection);
+            mActiveSensors.add(handle, rec);
+            if (sensor->isVirtual()) {
+                mActiveVirtualSensors.add(handle, sensor);
             }
-            // this could change the sensor event delivery speed
-            recomputeEventsPeriodLocked(handle);
+        } else {
+            if (rec->addConnection(connection)) {
+                // this sensor is already activated, but we are adding a
+                // connection that uses it. Immediately send down the last
+                // known value of the requested sensor.
+                sensors_event_t scratch;
+                sensors_event_t& event(mLastEventSeen.editValueFor(handle));
+                if (event.version == sizeof(sensors_event_t)) {
+                    connection->sendEvents(&event, 1);
+                }
+            }
+        }
+        if (err == NO_ERROR) {
+            // connection now active
+            if (connection->addSensor(handle)) {
+                // the sensor was added (which means it wasn't already there)
+                // so, see if this connection becomes active
+                if (mActiveConnections.indexOf(connection) < 0) {
+                    mActiveConnections.add(connection);
+                }
+            }
         }
     }
     return err;
@@ -361,15 +377,11 @@
         // see if this sensor becomes inactive
         if (rec->removeConnection(connection)) {
             mActiveSensors.removeItem(handle);
+            mActiveVirtualSensors.removeItem(handle);
             delete rec;
-            err = mSensorDevice->activate(mSensorDevice, handle, 0);
-            if (err == 0) {
-                BatteryService::getInstance().disableSensor(handle);
-            }
         }
-    }
-    if (err == NO_ERROR) {
-        recomputeEventsPeriodLocked(handle);
+        SensorInterface* sensor = mSensorMap.valueFor(handle);
+        err = sensor ? sensor->activate(connection.get(), false) : status_t(BAD_VALUE);
     }
     return err;
 }
@@ -386,30 +398,9 @@
     if (ns < MINIMUM_EVENTS_PERIOD)
         ns = MINIMUM_EVENTS_PERIOD;
 
-    Mutex::Autolock _l(mLock);
-    status_t err = connection->setEventRateLocked(handle, ns);
-    if (err == NO_ERROR) {
-        recomputeEventsPeriodLocked(handle);
-    }
-    return err;
-}
-
-status_t SensorService::recomputeEventsPeriodLocked(int32_t handle)
-{
-    status_t err = NO_ERROR;
-    nsecs_t wanted = ms2ns(1000);
-    size_t count = mActiveConnections.size();
-    for (size_t i=0 ; i<count ; i++) {
-        sp<SensorEventConnection> connection(mActiveConnections[i].promote());
-        if (connection != NULL) {
-            nsecs_t ns = connection->getEventRateForSensor(handle);
-            if (ns) {
-                wanted = wanted < ns ? wanted : ns;
-            }
-        }
-    }
-    err = mSensorDevice->setDelay(mSensorDevice, handle, wanted);
-    return err;
+    SensorInterface* sensor = mSensorMap.valueFor(handle);
+    if (!sensor) return BAD_VALUE;
+    return sensor->setDelay(connection.get(), handle, ns);
 }
 
 // ---------------------------------------------------------------------------
@@ -458,41 +449,32 @@
 }
 
 bool SensorService::SensorEventConnection::addSensor(int32_t handle) {
-    if (mSensorInfo.indexOfKey(handle) <= 0) {
-        SensorInfo info;
-        mSensorInfo.add(handle, info);
+    Mutex::Autolock _l(mConnectionLock);
+    if (mSensorInfo.indexOf(handle) <= 0) {
+        mSensorInfo.add(handle);
         return true;
     }
     return false;
 }
 
 bool SensorService::SensorEventConnection::removeSensor(int32_t handle) {
-    if (mSensorInfo.removeItem(handle) >= 0) {
+    Mutex::Autolock _l(mConnectionLock);
+    if (mSensorInfo.remove(handle) >= 0) {
         return true;
     }
     return false;
 }
 
 bool SensorService::SensorEventConnection::hasSensor(int32_t handle) const {
-    return mSensorInfo.indexOfKey(handle) >= 0;
+    Mutex::Autolock _l(mConnectionLock);
+    return mSensorInfo.indexOf(handle) >= 0;
 }
 
 bool SensorService::SensorEventConnection::hasAnySensor() const {
+    Mutex::Autolock _l(mConnectionLock);
     return mSensorInfo.size() ? true : false;
 }
 
-status_t SensorService::SensorEventConnection::setEventRateLocked(
-        int handle, nsecs_t ns)
-{
-    ssize_t index = mSensorInfo.indexOfKey(handle);
-    if (index >= 0) {
-        SensorInfo& info = mSensorInfo.editValueFor(handle);
-        info.ns = ns;
-        return NO_ERROR;
-    }
-    return status_t(index);
-}
-
 status_t SensorService::SensorEventConnection::sendEvents(
         sensors_event_t const* buffer, size_t numEvents,
         sensors_event_t* scratch)
@@ -500,10 +482,11 @@
     // filter out events not for this connection
     size_t count = 0;
     if (scratch) {
+        Mutex::Autolock _l(mConnectionLock);
         size_t i=0;
         while (i<numEvents) {
             const int32_t curr = buffer[i].sensor;
-            if (mSensorInfo.indexOfKey(curr) >= 0) {
+            if (mSensorInfo.indexOf(curr) >= 0) {
                 do {
                     scratch[count++] = buffer[i++];
                 } while ((i<numEvents) && (buffer[i].sensor == curr));
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index dfb1c0e..540c7e2 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -34,6 +34,8 @@
 #include <gui/ISensorServer.h>
 #include <gui/ISensorEventConnection.h>
 
+#include "SensorInterface.h"
+
 // ---------------------------------------------------------------------------
 
 struct sensors_poll_device_t;
@@ -50,7 +52,6 @@
    friend class BinderService<SensorService>;
 
    static const nsecs_t MINIMUM_EVENTS_PERIOD =   1000000; // 1000 Hz
-   static const nsecs_t DEFAULT_EVENTS_PERIOD = 200000000; //    5 Hz
 
             SensorService();
     virtual ~SensorService();
@@ -75,13 +76,10 @@
 
         sp<SensorService> const mService;
         sp<SensorChannel> const mChannel;
+        mutable Mutex mConnectionLock;
 
         // protected by SensorService::mLock
-        struct SensorInfo {
-            SensorInfo() : ns(DEFAULT_EVENTS_PERIOD) { }
-            nsecs_t ns;
-        };
-        DefaultKeyedVector<int32_t, SensorInfo> mSensorInfo;
+        SortedVector<int> mSensorInfo;
 
     public:
         SensorEventConnection(const sp<SensorService>& service);
@@ -92,10 +90,6 @@
         bool hasAnySensor() const;
         bool addSensor(int32_t handle);
         bool removeSensor(int32_t handle);
-        status_t setEventRateLocked(int handle, nsecs_t ns);
-        nsecs_t getEventRateForSensor(int32_t handle) const {
-            return mSensorInfo.valueFor(handle).ns;
-        }
     };
 
     class SensorRecord {
@@ -108,19 +102,25 @@
     };
 
     SortedVector< wp<SensorEventConnection> > getActiveConnections() const;
+    DefaultKeyedVector<int, SensorInterface*> getActiveVirtualSensors() const;
+
     String8 getSensorName(int handle) const;
-    status_t recomputeEventsPeriodLocked(int32_t handle);
+    void recordLastValue(sensors_event_t const * buffer, size_t count);
+    static void sortEventBuffer(sensors_event_t* buffer, size_t count);
+    void registerSensor(SensorInterface* sensor);
+    void registerVirtualSensor(SensorInterface* sensor);
 
     // constants
     Vector<Sensor> mSensorList;
-    struct sensors_poll_device_t* mSensorDevice;
-    struct sensors_module_t* mSensorModule;
+    DefaultKeyedVector<int, SensorInterface*> mSensorMap;
+    Vector<SensorInterface *> mVirtualSensorList;
     Permission mDump;
     status_t mInitCheck;
 
     // protected by mLock
     mutable Mutex mLock;
     DefaultKeyedVector<int, SensorRecord*> mActiveSensors;
+    DefaultKeyedVector<int, SensorInterface*> mActiveVirtualSensors;
     SortedVector< wp<SensorEventConnection> > mActiveConnections;
 
     // The size of this vector is constant, only the items are mutable
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 5f9a3e7..d74a547 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -380,7 +380,11 @@
                 break;
             case OFFHOOK:
                 Phone fgPhone = getFgPhone();
-                if (hasActiveFgCall() && !(fgPhone instanceof SipPhone)) {
+                // Enable IN_CALL mode while foreground call is in DIALING,
+                // ALERTING, ACTIVE and DISCONNECTING state and not from sipPhone
+                if (getActiveFgCallState() != Call.State.IDLE
+                        && getActiveFgCallState() != Call.State.DISCONNECTED
+                        && !(fgPhone instanceof SipPhone)) {
                     mode = AudioManager.MODE_IN_CALL;
                 }
                 break;