Merge "Fix a bug that prevented the creation of EGL shared contexts." into gingerbread
diff --git a/api/current.xml b/api/current.xml
index 9145462..5d71cad 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -87294,6 +87294,21 @@
 <parameter name="quality" type="int">
 </parameter>
 </method>
+<method name="get"
+ return="android.media.CamcorderProfile"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cameraId" type="int">
+</parameter>
+<parameter name="quality" type="int">
+</parameter>
+</method>
 <field name="QUALITY_HIGH"
  type="int"
  transient="false"
@@ -87466,6 +87481,21 @@
 <parameter name="quality" type="int">
 </parameter>
 </method>
+<method name="getJpegEncodingQualityParameter"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cameraId" type="int">
+</parameter>
+<parameter name="quality" type="int">
+</parameter>
+</method>
 <field name="QUALITY_HIGH"
  type="int"
  transient="false"
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index 75948a5..10668a4 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -1031,6 +1031,7 @@
             mHardware->getRawHeap());
 
         mSurface->registerBuffers(buffers);
+        IPCThreadState::self()->flushCommands();
     }
 
     mLock.unlock();
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 33696f4..9a97284 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -7,7 +7,7 @@
 	SineSource.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libstagefright libmedia libutils libbinder
+	libstagefright libmedia libutils libbinder libstagefright_foundation
 
 LOCAL_C_INCLUDES:= \
 	$(JNI_H_INCLUDE) \
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 877b908..b7a3f99 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -38,6 +38,9 @@
 #include <media/stagefright/OMXCodec.h>
 #include <media/mediametadataretriever.h>
 
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MPEG4Writer.h>
+
 using namespace android;
 
 static long gNumRepetitions;
@@ -45,6 +48,8 @@
 static long gReproduceBug;  // if not -1.
 static bool gPreferSoftwareCodec;
 static bool gPlaybackAudio;
+static bool gWriteMP4;
+static String8 gWriteMP4Filename;
 
 static int64_t getNowUs() {
     struct timeval tv;
@@ -258,6 +263,21 @@
     }
 }
 
+static void writeSourceToMP4(const sp<MediaSource> &source) {
+    sp<MPEG4Writer> writer =
+        new MPEG4Writer(gWriteMP4Filename.string());
+
+    CHECK_EQ(writer->addSource(source), OK);
+
+    sp<MetaData> params = new MetaData;
+    CHECK_EQ(writer->start(), OK);
+
+    while (!writer->reachedEOS()) {
+        usleep(100000);
+    }
+    writer->stop();
+}
+
 static void usage(const char *me) {
     fprintf(stderr, "usage: %s\n", me);
     fprintf(stderr, "       -h(elp)\n");
@@ -270,6 +290,7 @@
     fprintf(stderr, "       -t(humbnail) extract video thumbnail or album art\n");
     fprintf(stderr, "       -s(oftware) prefer software codec\n");
     fprintf(stderr, "       -o playback audio\n");
+    fprintf(stderr, "       -w(rite) filename (write to .mp4 file)\n");
 }
 
 int main(int argc, char **argv) {
@@ -284,9 +305,10 @@
     gReproduceBug = -1;
     gPreferSoftwareCodec = false;
     gPlaybackAudio = false;
+    gWriteMP4 = false;
 
     int res;
-    while ((res = getopt(argc, argv, "han:lm:b:ptso")) >= 0) {
+    while ((res = getopt(argc, argv, "han:lm:b:ptsow:")) >= 0) {
         switch (res) {
             case 'a':
             {
@@ -322,6 +344,13 @@
                 break;
             }
 
+            case 'w':
+            {
+                gWriteMP4 = true;
+                gWriteMP4Filename.setTo(optarg);
+                break;
+            }
+
             case 'p':
             {
                 dumpProfiles = true;
@@ -554,7 +583,11 @@
             mediaSource = extractor->getTrack(i);
         }
 
-        playSource(&client, mediaSource);
+        if (gWriteMP4) {
+            writeSourceToMP4(mediaSource);
+        } else {
+            playSource(&client, mediaSource);
+        }
     }
 
     client.disconnect();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 9b9ae52..985f591 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -39,7 +39,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index e56fee9..1fe85e6 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1021,16 +1021,6 @@
             return true;
         }
 
-        case REPORT_PSS_TRANSACTION: {
-            data.enforceInterface(IActivityManager.descriptor);
-            IBinder b = data.readStrongBinder();
-            IApplicationThread app = ApplicationThreadNative.asInterface(b);
-            int pss = data.readInt();
-            reportPss(app, pss);
-            reply.writeNoException();
-            return true;
-        }
-
         case START_RUNNING_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             String pkg = data.readString();
@@ -2529,14 +2519,6 @@
         reply.recycle();
         return res;
     }
-    public void reportPss(IApplicationThread caller, int pss) throws RemoteException {
-        Parcel data = Parcel.obtain();
-        data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeStrongBinder(caller.asBinder());
-        data.writeInt(pss);
-        mRemote.transact(REPORT_PSS_TRANSACTION, data, null, 0);
-        data.recycle();
-    }
     public void startRunning(String pkg, String cls, String action,
             String indata) throws RemoteException {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 03bb858..883366b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -616,14 +616,6 @@
             queueOrSendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token);
         }
 
-        public void requestPss() {
-            try {
-                ActivityManagerNative.getDefault().reportPss(this,
-                        (int)Process.getPss(Process.myPid()));
-            } catch (RemoteException e) {
-            }
-        }
-
         public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) {
             ProfilerControlData pcd = new ProfilerControlData();
             pcd.path = path;
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 360959d..1c20062 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -341,13 +341,6 @@
             return true;
         }
         
-        case REQUEST_PSS_TRANSACTION:
-        {
-            data.enforceInterface(IApplicationThread.descriptor);
-            requestPss();
-            return true;
-        }
-        
         case PROFILER_CONTROL_TRANSACTION:
         {
             data.enforceInterface(IApplicationThread.descriptor);
@@ -779,14 +772,6 @@
         data.recycle();
     }
     
-    public final void requestPss() throws RemoteException {
-        Parcel data = Parcel.obtain();
-        data.writeInterfaceToken(IApplicationThread.descriptor);
-        mRemote.transact(REQUEST_PSS_TRANSACTION, data, null,
-                IBinder.FLAG_ONEWAY);
-        data.recycle();
-    }
-    
     public void profilerControl(boolean start, String path,
             ParcelFileDescriptor fd) throws RemoteException {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index bf02d5a..20c9a80 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -247,8 +247,6 @@
     
     public boolean killPids(int[] pids, String reason) throws RemoteException;
     
-    public void reportPss(IApplicationThread caller, int pss) throws RemoteException;
-    
     // Special low-level communication with activity manager.
     public void startRunning(String pkg, String cls, String action,
             String data) throws RemoteException;
@@ -502,7 +500,7 @@
     int FORCE_STOP_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+78;
     int KILL_PIDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79;
     int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80;
-    int REPORT_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81;
+
     int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82;
     int GET_DEVICE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+83;
     int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index ffb8651..c8ef17f 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -95,7 +95,6 @@
             throws RemoteException;
     void scheduleLowMemory() throws RemoteException;
     void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
-    void requestPss() throws RemoteException;
     void profilerControl(boolean start, String path, ParcelFileDescriptor fd)
             throws RemoteException;
     void setSchedulingGroup(int group) throws RemoteException;
@@ -132,7 +131,7 @@
     int SCHEDULE_LOW_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+23;
     int SCHEDULE_ACTIVITY_CONFIGURATION_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+24;
     int SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+25;
-    int REQUEST_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26;
+
     int PROFILER_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
     int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
     int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 429d164..161161c 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -5,10 +5,16 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.graphics.PixelFormat;
 import android.os.Bundle;
+import android.os.Looper;
+import android.os.MessageQueue;
 import android.view.InputChannel;
 import android.view.InputQueue;
+import android.view.KeyEvent;
+import android.view.Surface;
 import android.view.SurfaceHolder;
+import android.view.View;
 
 import java.io.File;
 
@@ -22,7 +28,12 @@
     
     private int mNativeHandle;
     
-    private native int loadNativeCode(String path);
+    private InputQueue mCurInputQueue;
+    private SurfaceHolder mCurSurfaceHolder;
+    
+    private boolean mDestroyed;
+    
+    private native int loadNativeCode(String path, MessageQueue queue);
     private native void unloadNativeCode(int handle);
     
     private native void onStartNative(int handle);
@@ -32,10 +43,10 @@
     private native void onStopNative(int handle);
     private native void onLowMemoryNative(int handle);
     private native void onWindowFocusChangedNative(int handle, boolean focused);
-    private native void onSurfaceCreatedNative(int handle, SurfaceHolder holder);
-    private native void onSurfaceChangedNative(int handle, SurfaceHolder holder,
+    private native void onSurfaceCreatedNative(int handle, Surface surface);
+    private native void onSurfaceChangedNative(int handle, Surface surface,
             int format, int width, int height);
-    private native void onSurfaceDestroyedNative(int handle, SurfaceHolder holder);
+    private native void onSurfaceDestroyedNative(int handle);
     private native void onInputChannelCreatedNative(int handle, InputChannel channel);
     private native void onInputChannelDestroyedNative(int handle, InputChannel channel);
     
@@ -46,6 +57,7 @@
         
         getWindow().takeSurface(this);
         getWindow().takeInputQueue(this);
+        getWindow().setFormat(PixelFormat.RGB_565);
         
         try {
             ai = getPackageManager().getActivityInfo(
@@ -78,7 +90,7 @@
             throw new IllegalArgumentException("Unable to find native library: " + libname);
         }
         
-        mNativeHandle = loadNativeCode(path);
+        mNativeHandle = loadNativeCode(path, Looper.myQueue());
         if (mNativeHandle == 0) {
             throw new IllegalArgumentException("Unable to load native library: " + path);
         }
@@ -87,6 +99,15 @@
 
     @Override
     protected void onDestroy() {
+        mDestroyed = true;
+        if (mCurSurfaceHolder != null) {
+            onSurfaceDestroyedNative(mNativeHandle);
+            mCurSurfaceHolder = null;
+        }
+        if (mCurInputQueue != null) {
+            onInputChannelDestroyedNative(mNativeHandle, mCurInputQueue.getInputChannel());
+            mCurInputQueue = null;
+        }
         unloadNativeCode(mNativeHandle);
         super.onDestroy();
     }
@@ -124,32 +145,66 @@
     @Override
     public void onLowMemory() {
         super.onLowMemory();
-        onLowMemoryNative(mNativeHandle);
+        if (!mDestroyed) {
+            onLowMemoryNative(mNativeHandle);
+        }
     }
 
     @Override
     public void onWindowFocusChanged(boolean hasFocus) {
         super.onWindowFocusChanged(hasFocus);
-        onWindowFocusChangedNative(mNativeHandle, hasFocus);
+        if (!mDestroyed) {
+            onWindowFocusChangedNative(mNativeHandle, hasFocus);
+        }
     }
     
     public void surfaceCreated(SurfaceHolder holder) {
-        onSurfaceCreatedNative(mNativeHandle, holder);
+        if (!mDestroyed) {
+            mCurSurfaceHolder = holder;
+            onSurfaceCreatedNative(mNativeHandle, holder.getSurface());
+        }
     }
     
     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-        onSurfaceChangedNative(mNativeHandle, holder, format, width, height);
+        if (!mDestroyed) {
+            mCurSurfaceHolder = holder;
+            onSurfaceChangedNative(mNativeHandle, holder.getSurface(), format, width, height);
+        }
     }
     
     public void surfaceDestroyed(SurfaceHolder holder) {
-        onSurfaceDestroyedNative(mNativeHandle, holder);
+        mCurSurfaceHolder = null;
+        if (!mDestroyed) {
+            onSurfaceDestroyedNative(mNativeHandle);
+        }
     }
     
     public void onInputQueueCreated(InputQueue queue) {
-        onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel());
+        if (!mDestroyed) {
+            mCurInputQueue = queue;
+            onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel());
+        }
     }
     
     public void onInputQueueDestroyed(InputQueue queue) {
-        onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel());
+        mCurInputQueue = null;
+        if (!mDestroyed) {
+            onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel());
+        }
+    }
+    
+    void dispatchUnhandledKeyEvent(KeyEvent event) {
+        View decor = getWindow().getDecorView();
+        if (decor != null) {
+            decor.dispatchKeyEvent(event);
+        }
+    }
+    
+    void setWindowFlags(int flags, int mask) {
+        getWindow().setFlags(flags, mask);
+    }
+    
+    void setWindowFormat(int format) {
+        getWindow().setFormat(format);
     }
 }
diff --git a/core/java/android/hardware/Usb.java b/core/java/android/hardware/Usb.java
index e9c2cf7..57271d4 100644
--- a/core/java/android/hardware/Usb.java
+++ b/core/java/android/hardware/Usb.java
@@ -39,6 +39,27 @@
     public static final String ACTION_USB_DISCONNECTED =
             "android.hardware.action.USB_DISCONNECTED";
 
+   /**
+     * Broadcast Action:  A sticky broadcast for USB state change events.
+     *
+     * This is a sticky broadcast for clients that are interested in both USB connect and
+     * disconnect events.  If you are only concerned with one or the other, you can use
+     * {@link #ACTION_USB_CONNECTED} or {@link #ACTION_USB_DISCONNECTED} to avoid receiving
+     * unnecessary broadcasts.  The boolean {@link #USB_CONNECTED} extra indicates whether
+     * USB is connected or disconnected.
+     * The extras bundle will also contain name/value pairs with the name of the function
+     * and a value of either {@link #USB_FUNCTION_ENABLED} or {@link #USB_FUNCTION_DISABLED}.
+     * Possible USB function names include {@link #USB_FUNCTION_MASS_STORAGE},
+     * {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS} and {@link #USB_FUNCTION_MTP}.
+     */
+    public static final String ACTION_USB_STATE =
+            "android.hardware.action.USB_STATE";
+
+    /**
+     * Boolean extra indicating whether USB is connected or disconnected.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
+     */
+    public static final String USB_CONNECTED = "connected";
 
     /**
      * Name of the USB mass storage USB function.
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index a8c6f9b..9ad125b 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -35,6 +35,11 @@
 import java.security.cert.X509Certificate;
 
 import javax.net.SocketFactory;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
@@ -48,17 +53,33 @@
 
 /**
  * SSLSocketFactory implementation with several extra features:
+ *
  * <ul>
  * <li>Timeout specification for SSL handshake operations
+ * <li>Hostname verification in most cases (see WARNINGs below)
  * <li>Optional SSL session caching with {@link SSLSessionCache}
  * <li>Optionally bypass all SSL certificate checks
  * </ul>
- * Note that the handshake timeout does not apply to actual connection.
- * If you want a connection timeout as well, use {@link #createSocket()} and
- * {@link Socket#connect(SocketAddress, int)}.
- * <p>
- * On development devices, "setprop socket.relaxsslcheck yes" bypasses all
- * SSL certificate checks, for testing with development servers.
+ *
+ * The handshake timeout does not apply to actual TCP socket connection.
+ * If you want a connection timeout as well, use {@link #createSocket()}
+ * and {@link Socket#connect(SocketAddress, int)}, after which you
+ * must verify the identity of the server you are connected to.
+ *
+ * <p class="caution"><b>Most {@link SSLSocketFactory} implementations do not
+ * verify the server's identity, allowing man-in-the-middle attacks.</b>
+ * This implementation does check the server's certificate hostname, but only
+ * for createSocket variants that specify a hostname.  When using methods that
+ * use {@link InetAddress} or which return an unconnected socket, you MUST
+ * verify the server's identity yourself to ensure a secure connection.</p>
+ *
+ * <p>One way to verify the server's identity is to use
+ * {@link HttpsURLConnection#getDefaultHostnameVerifier()} to get a
+ * {@link HostnameVerifier} to verify the certificate hostname.
+ *
+ * <p>On development devices, "setprop socket.relaxsslcheck yes" bypasses all
+ * SSL certificate and hostname checks for testing purposes.  This setting
+ * requires root access.
  */
 public class SSLCertificateSocketFactory extends SSLSocketFactory {
     private static final String TAG = "SSLCertificateSocketFactory";
@@ -71,6 +92,9 @@
         }
     };
 
+    private static final HostnameVerifier HOSTNAME_VERIFIER =
+        HttpsURLConnection.getDefaultHostnameVerifier();
+
     private SSLSocketFactory mInsecureFactory = null;
     private SSLSocketFactory mSecureFactory = null;
 
@@ -95,7 +119,7 @@
      *
      * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
      *         for none.  The socket timeout is reset to 0 after the handshake.
-     * @return a new SocketFactory with the specified parameters
+     * @return a new SSLSocketFactory with the specified parameters
      */
     public static SocketFactory getDefault(int handshakeTimeoutMillis) {
         return new SSLCertificateSocketFactory(handshakeTimeoutMillis, null, true);
@@ -108,7 +132,7 @@
      * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
      *         for none.  The socket timeout is reset to 0 after the handshake.
      * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
-     * @return a new SocketFactory with the specified parameters
+     * @return a new SSLSocketFactory with the specified parameters
      */
     public static SSLSocketFactory getDefault(int handshakeTimeoutMillis, SSLSessionCache cache) {
         return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true);
@@ -117,13 +141,14 @@
     /**
      * Returns a new instance of a socket factory with all SSL security checks
      * disabled, using an optional handshake timeout and SSL session cache.
-     * Sockets created using this factory are vulnerable to man-in-the-middle
-     * attacks!
+     *
+     * <p class="caution"><b>Warning:</b> Sockets created using this factory
+     * are vulnerable to man-in-the-middle attacks!</p>
      *
      * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
      *         for none.  The socket timeout is reset to 0 after the handshake.
      * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
-     * @return an insecure SocketFactory with the specified parameters
+     * @return an insecure SSLSocketFactory with the specified parameters
      */
     public static SSLSocketFactory getInsecure(int handshakeTimeoutMillis, SSLSessionCache cache) {
         return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, false);
@@ -145,6 +170,44 @@
                 new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true));
     }
 
+    /**
+     * Verify the hostname of the certificate used by the other end of a
+     * connected socket.  You MUST call this if you did not supply a hostname
+     * to {@link #createSocket()}.  It is harmless to call this method
+     * redundantly if the hostname has already been verified.
+     *
+     * <p>Wildcard certificates are allowed to verify any matching hostname,
+     * so "foo.bar.example.com" is verified if the peer has a certificate
+     * for "*.example.com".
+     *
+     * @param socket An SSL socket which has been connected to a server
+     * @param hostname The expected hostname of the remote server
+     * @throws IOException if something goes wrong handshaking with the server
+     * @throws SSLPeerUnverifiedException if the server cannot prove its identity
+     *
+     * @hide
+     */
+    public static void verifyHostname(Socket socket, String hostname) throws IOException {
+        if (!(socket instanceof SSLSocket)) {
+            throw new IllegalArgumentException("Attempt to verify non-SSL socket");
+        }
+
+        if (!isSslCheckRelaxed()) {
+            // The code at the start of OpenSSLSocketImpl.startHandshake()
+            // ensures that the call is idempotent, so we can safely call it.
+            SSLSocket ssl = (SSLSocket) socket;
+            ssl.startHandshake();
+
+            SSLSession session = ssl.getSession();
+            if (session == null) {
+                throw new SSLException("Cannot verify SSL socket without session");
+            }
+            if (!HOSTNAME_VERIFIER.verify(hostname, session)) {
+                throw new SSLPeerUnverifiedException("Cannot verify hostname: " + hostname);
+            }
+        }
+    }
+
     private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) {
         try {
             SSLContextImpl sslContext = new SSLContextImpl();
@@ -156,10 +219,14 @@
         }
     }
 
+    private static boolean isSslCheckRelaxed() {
+        return "1".equals(SystemProperties.get("ro.debuggable")) &&
+            "yes".equals(SystemProperties.get("socket.relaxsslcheck"));
+    }
+
     private synchronized SSLSocketFactory getDelegate() {
         // Relax the SSL check if instructed (for this factory, or systemwide)
-        if (!mSecure || ("1".equals(SystemProperties.get("ro.debuggable")) &&
-            "yes".equals(SystemProperties.get("socket.relaxsslcheck")))) {
+        if (!mSecure || isSslCheckRelaxed()) {
             if (mInsecureFactory == null) {
                 if (mSecure) {
                     Log.w(TAG, "*** BYPASSING SSL SECURITY CHECKS (socket.relaxsslcheck=yes) ***");
@@ -177,13 +244,27 @@
         }
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This method verifies the peer's certificate hostname after connecting.
+     */
     @Override
     public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException {
         OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close);
         s.setHandshakeTimeout(mHandshakeTimeoutMillis);
+        verifyHostname(s, host);
         return s;
     }
 
+    /**
+     * Creates a new socket which is not connected to any remote host.
+     * You must use {@link Socket#connect} to connect the socket.
+     *
+     * <p class="caution"><b>Warning:</b> Hostname verification is not performed
+     * with this method.  You MUST verify the server's identity after connecting
+     * the socket to avoid man-in-the-middle attacks.</p>
+     */
     @Override
     public Socket createSocket() throws IOException {
         OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket();
@@ -191,6 +272,13 @@
         return s;
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * <p class="caution"><b>Warning:</b> Hostname verification is not performed
+     * with this method.  You MUST verify the server's identity after connecting
+     * the socket to avoid man-in-the-middle attacks.</p>
+     */
     @Override
     public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort)
             throws IOException {
@@ -200,6 +288,13 @@
         return s;
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * <p class="caution"><b>Warning:</b> Hostname verification is not performed
+     * with this method.  You MUST verify the server's identity after connecting
+     * the socket to avoid man-in-the-middle attacks.</p>
+     */
     @Override
     public Socket createSocket(InetAddress addr, int port) throws IOException {
         OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port);
@@ -207,19 +302,31 @@
         return s;
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This method verifies the peer's certificate hostname after connecting.
+     */
     @Override
     public Socket createSocket(String host, int port, InetAddress localAddr, int localPort)
             throws IOException {
         OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
                 host, port, localAddr, localPort);
         s.setHandshakeTimeout(mHandshakeTimeoutMillis);
+        verifyHostname(s, host);
         return s;
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This method verifies the peer's certificate hostname after connecting.
+     */
     @Override
     public Socket createSocket(String host, int port) throws IOException {
         OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port);
         s.setHandshakeTimeout(mHandshakeTimeoutMillis);
+        verifyHostname(s, host);
         return s;
     }
 
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 5640a06..f695dbb 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -626,6 +626,15 @@
             throws IllegalArgumentException, SecurityException;
 
     /**
+     * Call with 'false' to cause future calls to {@link #setThreadPriority(int)} to
+     * throw an exception if passed a background-level thread priority.  This is only
+     * effective if the JNI layer is built with GUARD_THREAD_PRIORITY defined to 1.
+     *
+     * @hide
+     */
+    public static final native void setCanSelfBackground(boolean backgroundOk);
+
+    /**
      * Sets the scheduling group for a thread.
      * @hide
      * @param tid The indentifier of the thread/process to change.
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 3f5d6ca..249ad62 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -773,8 +773,6 @@
                     if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
                         if (mInputChannel != null) {
                             InputQueue.unregisterInputChannel(mInputChannel);
-                            mInputChannel.dispose();
-                            mInputChannel = null;
                         }
                     }
                     
@@ -783,6 +781,15 @@
                 }
                 mSurfaceHolder.mSurface.release();
                 mCreated = false;
+                
+                if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+                    // Dispose the input channel after removing the window so the Window Manager
+                    // doesn't interpret the input channel being closed as an abnormal termination.
+                    if (mInputChannel != null) {
+                        mInputChannel.dispose();
+                        mInputChannel = null;
+                    }
+                }
             }
         }
     }
diff --git a/core/java/android/view/InputTarget.java b/core/java/android/view/InputTarget.java
deleted file mode 100644
index 6ff7305..0000000
--- a/core/java/android/view/InputTarget.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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 android.view;
-
-/**
- * An input target specifies how an input event is to be dispatched to a particular window
- * including the window's input channel, control flags, a timeout, and an X / Y offset to
- * be added to input event coordinates to compensate for the absolute position of the
- * window area.
- * 
- * These parameters are used by the native input dispatching code.
- * @hide
- */
-public final class InputTarget {
-    public InputChannel mInputChannel;
-    public int mFlags;
-    public long mTimeoutNanos;
-    public float mXOffset;
-    public float mYOffset;
-    
-    /**
-     * This flag indicates that subsequent event delivery should be held until the
-     * current event is delivered to this target or a timeout occurs.
-     */
-    public static int FLAG_SYNC = 0x01;
-    
-    /**
-     * This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of
-     * this target and so should instead be delivered as an ACTION_OUTSIDE to this target.
-     */
-    public static int FLAG_OUTSIDE = 0x02;
-    
-    /*
-     * This flag indicates that a KeyEvent or MotionEvent is being canceled.
-     * In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set.
-     * In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL.
-     */
-    public static int FLAG_CANCEL = 0x04;
-    
-    public void recycle() {
-        mInputChannel = null;
-    }
-}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index ae9746e..0bfb6d6 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -676,33 +676,12 @@
      * TODO: should the dpad keys be here?  arguably, because they also shouldn't be menu shortcuts
      */
     public final boolean isSystem() {
-        switch (mKeyCode) {
-        case KEYCODE_MENU:
-        case KEYCODE_SOFT_RIGHT:
-        case KEYCODE_HOME:
-        case KEYCODE_BACK:
-        case KEYCODE_CALL:
-        case KEYCODE_ENDCALL:
-        case KEYCODE_VOLUME_UP:
-        case KEYCODE_VOLUME_DOWN:
-        case KEYCODE_MUTE:
-        case KEYCODE_POWER:
-        case KEYCODE_HEADSETHOOK:
-        case KEYCODE_MEDIA_PLAY_PAUSE:
-        case KEYCODE_MEDIA_STOP:
-        case KEYCODE_MEDIA_NEXT:
-        case KEYCODE_MEDIA_PREVIOUS:
-        case KEYCODE_MEDIA_REWIND:
-        case KEYCODE_MEDIA_FAST_FORWARD:
-        case KEYCODE_CAMERA:
-        case KEYCODE_FOCUS:
-        case KEYCODE_SEARCH:
-        case KEYCODE_PICTSYMBOLS:
-        case KEYCODE_SWITCH_CHARSET:
-            return true;
-        default:
-            return false;
-        }
+        return native_isSystemKey(mKeyCode);
+    }
+
+    /** @hide */
+    public final boolean hasDefaultAction() {
+        return native_hasDefaultAction(mKeyCode);
     }
 
 
@@ -1226,4 +1205,7 @@
         mDownTime = in.readLong();
         mEventTime = in.readLong();
     }
+    
+    private native boolean native_isSystemKey(int keyCode);
+    private native boolean native_hasDefaultAction(int keyCode);
 }
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 4854190..1dc82e8 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1762,6 +1762,15 @@
             sWindowSession.remove(mWindow);
         } catch (RemoteException e) {
         }
+        
+        if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+            // Dispose the input channel after removing the window so the Window Manager
+            // doesn't interpret the input channel being closed as an abnormal termination.
+            if (mInputChannel != null) {
+                mInputChannel.dispose();
+                mInputChannel = null;
+            }
+        }
     }
 
     void updateConfiguration(Configuration config, boolean force) {
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 431b786..be1f6d2 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -83,7 +83,7 @@
      * Temporary flag added during the transition to the new native input dispatcher.
      * This will be removed when the old input dispatch code is deleted.
      */
-    public final static boolean ENABLE_NATIVE_INPUT_DISPATCH = false;
+    public final static boolean ENABLE_NATIVE_INPUT_DISPATCH = true;
 
     // flags for interceptKeyTq
     /**
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 2369d25..127ed68 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -126,6 +126,11 @@
 
     public static final String POWER_CPU_SPEEDS = "cpu.speeds";
 
+    /**
+     * Battery capacity in milliAmpHour (mAh).
+     */
+    public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
+
     static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>();
 
     private static final String TAG_DEVICE = "device";
@@ -243,6 +248,19 @@
         }
     }
 
+    /**
+     * Returns the battery capacity, if available, in milli Amp Hours. If not available,
+     * it returns zero.
+     * @return the battery capacity in mAh
+     */
+    public double getBatteryCapacity() {
+        return getAveragePower(POWER_BATTERY_CAPACITY);
+    }
+
+    /**
+     * Returns the number of speeds that the CPU can be run at.
+     * @return
+     */
     public int getNumSpeedSteps() {
         Object value = sPowerMap.get(POWER_CPU_SPEEDS);
         if (value != null && value instanceof Double[]) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index d854e87..a008e96 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -47,7 +47,6 @@
 	android_view_ViewRoot.cpp \
 	android_view_InputChannel.cpp \
 	android_view_InputQueue.cpp \
-	android_view_InputTarget.cpp \
 	android_view_KeyEvent.cpp \
 	android_view_MotionEvent.cpp \
 	android_text_AndroidCharacter.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 3e69c78..6fb1369 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -164,7 +164,6 @@
 extern int register_android_app_NativeActivity(JNIEnv *env);
 extern int register_android_view_InputChannel(JNIEnv* env);
 extern int register_android_view_InputQueue(JNIEnv* env);
-extern int register_android_view_InputTarget(JNIEnv* env);
 extern int register_android_view_KeyEvent(JNIEnv* env);
 extern int register_android_view_MotionEvent(JNIEnv* env);
 
@@ -1297,7 +1296,6 @@
     REG_JNI(register_android_app_NativeActivity),
     REG_JNI(register_android_view_InputChannel),
     REG_JNI(register_android_view_InputQueue),
-    REG_JNI(register_android_view_InputTarget),
     REG_JNI(register_android_view_KeyEvent),
     REG_JNI(register_android_view_MotionEvent),
 };
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 5e5e47e..dab1dba 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -17,47 +17,121 @@
 #define LOG_TAG "NativeActivity"
 #include <utils/Log.h>
 
-#include "JNIHelp.h"
-#include "android_view_InputChannel.h"
+#include <poll.h>
+#include <dlfcn.h>
+
 #include <android_runtime/AndroidRuntime.h>
 #include <android/native_activity.h>
+#include <surfaceflinger/Surface.h>
+#include <ui/egl/android_natives.h>
 #include <ui/InputTransport.h>
+#include <utils/PollLoop.h>
 
-#include <dlfcn.h>
+#include "JNIHelp.h"
+#include "android_os_MessageQueue.h"
+#include "android_view_InputChannel.h"
+#include "android_view_KeyEvent.h"
+#include "android_view_Surface.h"
 
 namespace android
 {
 
+static struct {
+    jclass clazz;
+
+    jmethodID dispatchUnhandledKeyEvent;
+    jmethodID setWindowFlags;
+    jmethodID setWindowFormat;
+} gNativeActivityClassInfo;
+
+// ------------------------------------------------------------------------
+
+/*
+ * Specialized input queue that allows unhandled key events to be dispatched
+ * back to the native activity's Java framework code.
+ */
+struct MyInputQueue : AInputQueue {
+    explicit MyInputQueue(const android::sp<android::InputChannel>& channel, int workWrite)
+        : AInputQueue(channel), mWorkWrite(workWrite) {
+    }
+    
+    virtual void doDefaultKey(android::KeyEvent* keyEvent) {
+        mLock.lock();
+        LOGI("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite);
+        if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) {
+            int8_t cmd = 1;
+            write(mWorkWrite, &cmd, sizeof(cmd));
+        }
+        mPendingKeys.add(keyEvent);
+        mLock.unlock();
+    }
+    
+    KeyEvent* getNextEvent() {
+        KeyEvent* event = NULL;
+        
+        mLock.lock();
+        if (mPendingKeys.size() > 0) {
+            event = mPendingKeys[0];
+            mPendingKeys.removeAt(0);
+        }
+        mLock.unlock();
+        
+        return event;
+    }
+    
+    int mWorkWrite;
+    
+    Mutex mLock;
+    Vector<KeyEvent*> mPendingKeys;
+};
+
+// ------------------------------------------------------------------------
+
+/*
+ * Native state for interacting with the NativeActivity class.
+ */
 struct NativeCode {
-    NativeCode(void* _dlhandle, android_activity_create_t* _createFunc) {
+    NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) {
         memset(&activity, sizeof(activity), 0);
         memset(&callbacks, sizeof(callbacks), 0);
         dlhandle = _dlhandle;
         createActivityFunc = _createFunc;
-        surface = NULL;
+        nativeWindow = NULL;
         inputChannel = NULL;
         nativeInputQueue = NULL;
+        mainWorkRead = mainWorkWrite = -1;
     }
     
     ~NativeCode() {
+        if (activity.env != NULL && activity.clazz != NULL) {
+            activity.env->DeleteGlobalRef(activity.clazz);
+        }
+        if (pollLoop != NULL && mainWorkRead >= 0) {
+            pollLoop->removeCallback(mainWorkRead);
+        }
+        if (nativeInputQueue != NULL) {
+            nativeInputQueue->mWorkWrite = -1;
+        }
         setSurface(NULL);
         setInputChannel(NULL);
         if (callbacks.onDestroy != NULL) {
             callbacks.onDestroy(&activity);
         }
+        if (mainWorkRead >= 0) close(mainWorkRead);
+        if (mainWorkWrite >= 0) close(mainWorkWrite);
         if (dlhandle != NULL) {
-            dlclose(dlhandle);
+            // for now don't unload...  we probably should clean this
+            // up and only keep one open dlhandle per proc, since there
+            // is really no benefit to unloading the code.
+            //dlclose(dlhandle);
         }
     }
     
     void setSurface(jobject _surface) {
-        if (surface != NULL) {
-            activity.env->DeleteGlobalRef(surface);
-        }
         if (_surface != NULL) {
-            surface = activity.env->NewGlobalRef(_surface);
+            nativeWindow = android_Surface_getNativeWindow(activity.env, _surface);
         } else {
-            surface = NULL;
+            nativeWindow = NULL;
         }
     }
     
@@ -73,7 +147,7 @@
             sp<InputChannel> ic =
                     android_view_InputChannel_getInputChannel(activity.env, _channel);
             if (ic != NULL) {
-                nativeInputQueue = new input_queue_t(ic);
+                nativeInputQueue = new MyInputQueue(ic, mainWorkWrite);
                 if (nativeInputQueue->getConsumer().initialize() != android::OK) {
                     delete nativeInputQueue;
                     nativeInputQueue = NULL;
@@ -86,19 +160,51 @@
         return OK;
     }
     
-    android_activity_t activity;
-    android_activity_callbacks_t callbacks;
+    ANativeActivity activity;
+    ANativeActivityCallbacks callbacks;
     
     void* dlhandle;
-    android_activity_create_t* createActivityFunc;
+    ANativeActivity_createFunc* createActivityFunc;
     
-    jobject surface;
+    sp<ANativeWindow> nativeWindow;
     jobject inputChannel;
-    struct input_queue_t* nativeInputQueue;
+    struct MyInputQueue* nativeInputQueue;
+    
+    // These are used to wake up the main thread to process work.
+    int mainWorkRead;
+    int mainWorkWrite;
+    sp<PollLoop> pollLoop;
 };
 
+// ------------------------------------------------------------------------
+
+/*
+ * Callback for handling native events on the application's main thread.
+ */
+static bool mainWorkCallback(int fd, int events, void* data) {
+    NativeCode* code = (NativeCode*)data;
+    if ((events & POLLIN) != 0) {
+        KeyEvent* keyEvent;
+        while ((keyEvent=code->nativeInputQueue->getNextEvent()) != NULL) {
+            jobject inputEventObj = android_view_KeyEvent_fromNative(
+                    code->activity.env, keyEvent);
+            code->activity.env->CallVoidMethod(code->activity.clazz,
+                    gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
+            int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal();
+            if (res != OK) {
+                LOGW("Failed to send finished signal on channel '%s'.  status=%d",
+                        code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res);
+            }
+        }
+    }
+    
+    return true;
+}
+
+// ------------------------------------------------------------------------
+
 static jint
-loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path)
+loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue)
 {
     const char* pathStr = env->GetStringUTFChars(path, NULL);
     NativeCode* code = NULL;
@@ -108,16 +214,39 @@
     env->ReleaseStringUTFChars(path, pathStr);
     
     if (handle != NULL) {
-        code = new NativeCode(handle, (android_activity_create_t*)
-                dlsym(handle, "android_onCreateActivity"));
+        code = new NativeCode(handle, (ANativeActivity_createFunc*)
+                dlsym(handle, "ANativeActivity_onCreate"));
         if (code->createActivityFunc == NULL) {
-            LOGW("android_onCreateActivity not found");
+            LOGW("ANativeActivity_onCreate not found");
             delete code;
             return 0;
         }
+        
+        code->pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueue);
+        if (code->pollLoop == NULL) {
+            LOGW("Unable to retrieve MessageQueue's PollLoop");
+            delete code;
+            return 0;
+        }
+        
+        int msgpipe[2];
+        if (pipe(msgpipe)) {
+            LOGW("could not create pipe: %s", strerror(errno));
+            delete code;
+            return 0;
+        }
+        code->mainWorkRead = msgpipe[0];
+        code->mainWorkWrite = msgpipe[1];
+        code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code);
+        
         code->activity.callbacks = &code->callbacks;
+        if (env->GetJavaVM(&code->activity.vm) < 0) {
+            LOGW("NativeActivity GetJavaVM failed");
+            delete code;
+            return 0;
+        }
         code->activity.env = env;
-        code->activity.clazz = clazz;
+        code->activity.clazz = env->NewGlobalRef(clazz);
         code->createActivityFunc(&code->activity, NULL, 0);
     }
     
@@ -217,9 +346,9 @@
     if (handle != 0) {
         NativeCode* code = (NativeCode*)handle;
         code->setSurface(surface);
-        if (code->callbacks.onSurfaceCreated != NULL) {
-            code->callbacks.onSurfaceCreated(&code->activity,
-                    (android_surface_t*)code->surface);
+        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
+            code->callbacks.onNativeWindowCreated(&code->activity,
+                    code->nativeWindow.get());
         }
     }
 }
@@ -230,9 +359,17 @@
 {
     if (handle != 0) {
         NativeCode* code = (NativeCode*)handle;
-        if (code->surface != NULL && code->callbacks.onSurfaceChanged != NULL) {
-            code->callbacks.onSurfaceChanged(&code->activity,
-                    (android_surface_t*)code->surface, format, width, height);
+        sp<ANativeWindow> oldNativeWindow = code->nativeWindow;
+        code->setSurface(surface);
+        if (oldNativeWindow != code->nativeWindow) {
+            if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
+                code->callbacks.onNativeWindowDestroyed(&code->activity,
+                        oldNativeWindow.get());
+            }
+            if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
+                code->callbacks.onNativeWindowCreated(&code->activity,
+                        code->nativeWindow.get());
+            }
         }
     }
 }
@@ -242,9 +379,9 @@
 {
     if (handle != 0) {
         NativeCode* code = (NativeCode*)handle;
-        if (code->surface != NULL && code->callbacks.onSurfaceDestroyed != NULL) {
-            code->callbacks.onSurfaceDestroyed(&code->activity,
-                    (android_surface_t*)code->surface);
+        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
+            code->callbacks.onNativeWindowDestroyed(&code->activity,
+                    code->nativeWindow.get());
         }
         code->setSurface(NULL);
     }
@@ -283,7 +420,7 @@
 }
 
 static const JNINativeMethod g_methods[] = {
-    { "loadNativeCode", "(Ljava/lang/String;)I", (void*)loadNativeCode_native },
+    { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;)I", (void*)loadNativeCode_native },
     { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
     { "onStartNative", "(I)V", (void*)onStart_native },
     { "onResumeNative", "(I)V", (void*)onResume_native },
@@ -292,23 +429,40 @@
     { "onStopNative", "(I)V", (void*)onStop_native },
     { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
     { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
-    { "onSurfaceCreatedNative", "(ILandroid/view/SurfaceHolder;)V", (void*)onSurfaceCreated_native },
-    { "onSurfaceChangedNative", "(ILandroid/view/SurfaceHolder;III)V", (void*)onSurfaceChanged_native },
-    { "onSurfaceDestroyedNative", "(ILandroid/view/SurfaceHolder;)V", (void*)onSurfaceDestroyed_native },
+    { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
+    { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
+    { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native },
     { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native },
     { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
 };
 
 static const char* const kNativeActivityPathName = "android/app/NativeActivity";
 
+#define FIND_CLASS(var, className) \
+        var = env->FindClass(className); \
+        LOG_FATAL_IF(! var, "Unable to find class " className); \
+        var = jclass(env->NewGlobalRef(var));
+
+#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
+        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find method" methodName);
+        
 int register_android_app_NativeActivity(JNIEnv* env)
 {
     //LOGD("register_android_app_NativeActivity");
 
-    jclass clazz;
+    FIND_CLASS(gNativeActivityClassInfo.clazz, kNativeActivityPathName);
+    
+    GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
+            gNativeActivityClassInfo.clazz,
+            "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V");
 
-    clazz = env->FindClass(kNativeActivityPathName);
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.app.NativeActivity");
+    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
+            gNativeActivityClassInfo.clazz,
+            "setWindowFlags", "(II)V");
+    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat,
+            gNativeActivityClassInfo.clazz,
+            "setWindowFormat", "(I)V");
 
     return AndroidRuntime::registerNativeMethods(
         env, kNativeActivityPathName,
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 68be741..7c99271 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -52,9 +52,15 @@
 #endif
 
 #define POLICY_DEBUG 0
+#define GUARD_THREAD_PRIORITY 0
 
 using namespace android;
 
+#if GUARD_THREAD_PRIORITY
+Mutex gKeyCreateMutex;
+static pthread_key_t gBgKey = -1;
+#endif
+
 static void signalExceptionForPriorityError(JNIEnv* env, jobject obj, int err)
 {
     switch (err) {
@@ -264,9 +270,41 @@
     closedir(d);
 }
 
+static void android_os_Process_setCanSelfBackground(JNIEnv* env, jobject clazz, jboolean bgOk) {
+    // Establishes the calling thread as illegal to put into the background.
+    // Typically used only for the system process's main looper.
+#if GUARD_THREAD_PRIORITY
+    LOGV("Process.setCanSelfBackground(%d) : tid=%d", bgOk, androidGetTid());
+    {
+        Mutex::Autolock _l(gKeyCreateMutex);
+        if (gBgKey == -1) {
+            pthread_key_create(&gBgKey, NULL);
+        }
+    }
+
+    // inverted:  not-okay, we set a sentinel value
+    pthread_setspecific(gBgKey, (void*)(bgOk ? 0 : 0xbaad));
+#endif
+}
+
 void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
                                               jint pid, jint pri)
 {
+#if GUARD_THREAD_PRIORITY
+    // if we're putting the current thread into the background, check the TLS
+    // to make sure this thread isn't guarded.  If it is, raise an exception.
+    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
+        if (pid == androidGetTid()) {
+            void* bgOk = pthread_getspecific(gBgKey);
+            if (bgOk == ((void*)0xbaad)) {
+                LOGE("Thread marked fg-only put self in background!");
+                jniThrowException(env, "java/lang/SecurityException", "May not put this thread into background");
+                return;
+            }
+        }
+    }
+#endif
+
     int rc = androidSetThreadPriority(pid, pri);
     if (rc != 0) {
         if (rc == INVALID_OPERATION) {
@@ -852,6 +890,7 @@
     {"getUidForName",       "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName},
     {"getGidForName",       "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName},
     {"setThreadPriority",   "(II)V", (void*)android_os_Process_setThreadPriority},
+    {"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground},
     {"setThreadPriority",   "(I)V", (void*)android_os_Process_setCallingThreadPriority},
     {"getThreadPriority",   "(I)I", (void*)android_os_Process_getThreadPriority},
     {"setThreadGroup",      "(II)V", (void*)android_os_Process_setThreadGroup},
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index 63e00d7..6fb3cf7 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -19,10 +19,10 @@
 //#define LOG_NDEBUG 0
 
 // Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 1
+#define DEBUG_DISPATCH_CYCLE 0
 
 // Log debug messages about registrations.
-#define DEBUG_REGISTRATION 1
+#define DEBUG_REGISTRATION 0
 
 
 #include "JNIHelp.h"
diff --git a/core/jni/android_view_InputTarget.cpp b/core/jni/android_view_InputTarget.cpp
deleted file mode 100644
index a0a7054..0000000
--- a/core/jni/android_view_InputTarget.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "InputTarget-JNI"
-
-#include "JNIHelp.h"
-
-#include <utils/Log.h>
-#include <ui/InputDispatcher.h>
-#include <ui/InputTransport.h>
-#include "android_view_InputTarget.h"
-#include "android_view_InputChannel.h"
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static struct {
-    jclass clazz;
-
-    jfieldID mInputChannel;
-    jfieldID mFlags;
-    jfieldID mTimeoutNanos;
-    jfieldID mXOffset;
-    jfieldID mYOffset;
-} gInputTargetClassInfo;
-
-// ----------------------------------------------------------------------------
-
-void android_view_InputTarget_toNative(JNIEnv* env, jobject inputTargetObj,
-        InputTarget* outInputTarget) {
-    jobject inputChannelObj = env->GetObjectField(inputTargetObj,
-            gInputTargetClassInfo.mInputChannel);
-    jint flags = env->GetIntField(inputTargetObj,
-            gInputTargetClassInfo.mFlags);
-    jlong timeoutNanos = env->GetLongField(inputTargetObj,
-            gInputTargetClassInfo.mTimeoutNanos);
-    jfloat xOffset = env->GetFloatField(inputTargetObj,
-            gInputTargetClassInfo.mXOffset);
-    jfloat yOffset = env->GetFloatField(inputTargetObj,
-            gInputTargetClassInfo.mYOffset);
-
-    outInputTarget->inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
-    outInputTarget->flags = flags;
-    outInputTarget->timeout = timeoutNanos;
-    outInputTarget->xOffset = xOffset;
-    outInputTarget->yOffset = yOffset;
-
-    env->DeleteLocalRef(inputChannelObj);
-}
-
-// ----------------------------------------------------------------------------
-
-#define FIND_CLASS(var, className) \
-        var = env->FindClass(className); \
-        LOG_FATAL_IF(! var, "Unable to find class " className); \
-        var = jclass(env->NewGlobalRef(var));
-
-#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
-        var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
-        LOG_FATAL_IF(! var, "Unable to find field " fieldName);
-
-int register_android_view_InputTarget(JNIEnv* env) {
-    FIND_CLASS(gInputTargetClassInfo.clazz, "android/view/InputTarget");
-
-    GET_FIELD_ID(gInputTargetClassInfo.mInputChannel, gInputTargetClassInfo.clazz,
-            "mInputChannel", "Landroid/view/InputChannel;");
-
-    GET_FIELD_ID(gInputTargetClassInfo.mFlags, gInputTargetClassInfo.clazz,
-            "mFlags", "I");
-
-    GET_FIELD_ID(gInputTargetClassInfo.mTimeoutNanos, gInputTargetClassInfo.clazz,
-            "mTimeoutNanos", "J");
-
-    GET_FIELD_ID(gInputTargetClassInfo.mXOffset, gInputTargetClassInfo.clazz,
-            "mXOffset", "F");
-
-    GET_FIELD_ID(gInputTargetClassInfo.mYOffset, gInputTargetClassInfo.clazz,
-            "mYOffset", "F");
-    
-    return 0;
-}
-
-} // namespace android
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index df3b952..8f648f4 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -76,8 +76,23 @@
             milliseconds_to_nanoseconds(eventTime));
 }
 
+static jboolean native_isSystemKey(JNIEnv* env, jobject clazz, jint keyCode) {
+    return KeyEvent::isSystemKey(keyCode);
+}
+
+static jboolean native_hasDefaultAction(JNIEnv* env, jobject clazz, jint keyCode) {
+    return KeyEvent::hasDefaultAction(keyCode);
+}
+
 // ----------------------------------------------------------------------------
 
+static const JNINativeMethod g_methods[] = {
+    { "native_isSystemKey", "(I)Z", (void*)native_isSystemKey },
+    { "native_hasDefaultAction", "(I)Z", (void*)native_hasDefaultAction },
+};
+
+static const char* const kKeyEventPathName = "android/view/KeyEvent";
+
 #define FIND_CLASS(var, className) \
         var = env->FindClass(className); \
         LOG_FATAL_IF(! var, "Unable to find class " className); \
@@ -92,8 +107,8 @@
         LOG_FATAL_IF(! var, "Unable to find field " fieldName);
 
 int register_android_view_KeyEvent(JNIEnv* env) {
-    FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
-
+    FIND_CLASS(gKeyEventClassInfo.clazz, kKeyEventPathName);
+        
     GET_METHOD_ID(gKeyEventClassInfo.ctor, gKeyEventClassInfo.clazz,
             "<init>", "(JJIIIIIII)V");
 
@@ -118,7 +133,9 @@
     GET_FIELD_ID(gKeyEventClassInfo.mCharacters, gKeyEventClassInfo.clazz,
             "mCharacters", "Ljava/lang/String;");
 
-    return 0;
+    return AndroidRuntime::registerNativeMethods(
+        env, kKeyEventPathName,
+        g_methods, NELEM(g_methods));
 }
 
 } // namespace android
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index cef5c10..a82abc93 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -33,6 +33,7 @@
 
 #include "jni.h"
 #include <android_runtime/AndroidRuntime.h>
+#include "android_view_Surface.h"
 #include <utils/misc.h>
 
 
@@ -179,7 +180,7 @@
     return result;
 }
 
-EGLNativeWindowType android_Surface_getEGLNativeWindow(
+sp<ANativeWindow> android_Surface_getNativeWindow(
         JNIEnv* env, jobject clazz) {
     return getSurface(env, clazz).get();
 }
diff --git a/core/jni/android_view_InputTarget.h b/core/jni/android_view_Surface.h
similarity index 73%
rename from core/jni/android_view_InputTarget.h
rename to core/jni/android_view_Surface.h
index 9230b1b..c37932e 100644
--- a/core/jni/android_view_InputTarget.h
+++ b/core/jni/android_view_Surface.h
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef _ANDROID_VIEW_INPUTTARGET_H
-#define _ANDROID_VIEW_INPUTTARGET_H
+#ifndef _ANDROID_VIEW_SURFACE_H
+#define _ANDROID_VIEW_SURFACE_H
+
+#include <android/native_window.h>
 
 #include "jni.h"
 
 namespace android {
 
-class InputTarget;
-
-extern void android_view_InputTarget_toNative(JNIEnv* env, jobject inputTargetObj,
-        InputTarget* outInputTarget);
+extern sp<ANativeWindow> android_Surface_getNativeWindow(
+        JNIEnv* env, jobject clazz);
 
 } // namespace android
 
-#endif // _ANDROID_OS_INPUTTARGET_H
+#endif // _ANDROID_VIEW_SURFACE_H
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index d5cde48..866c038 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -25,10 +25,9 @@
 #include <SkBitmap.h>
 #include <SkPixelRef.h>
 
-namespace android {
+#include "android_view_Surface.h"
 
-extern EGLNativeWindowType android_Surface_getEGLNativeWindow(
-        JNIEnv* env, jobject clazz);
+namespace android {
 
 static jclass gDisplay_class;
 static jclass gContext_class;
@@ -325,7 +324,7 @@
     }
     EGLDisplay dpy = getDisplay(_env, display);
     EGLContext cnf = getConfig(_env, config);
-    EGLNativeWindowType window = 0;
+    sp<ANativeWindow> window;
     if (native_window == NULL) {
 not_valid_surface:
         doThrow(_env, "java/lang/IllegalArgumentException",
@@ -333,12 +332,12 @@
         return 0;
     }
 
-    window = android_Surface_getEGLNativeWindow(_env, native_window);
+    window = android_Surface_getNativeWindow(_env, native_window);
     if (window == NULL)
         goto not_valid_surface;
 
     jint* base = beginNativeAttribList(_env, attrib_list);
-    EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window, base);
+    EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window.get(), base);
     endNativeAttributeList(_env, attrib_list, base);
     return (jint)sur;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b1f81df..82f822f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -82,6 +82,7 @@
 
     <protected-broadcast android:name="android.hardware.action.USB_CONNECTED" />
     <protected-broadcast android:name="android.hardware.action.USB_DISCONNECTED" />
+    <protected-broadcast android:name="android.hardware.action.USB_STATE" />
 
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
diff --git a/core/res/res/anim/priority_alert_enter.xml b/core/res/res/anim/priority_alert_enter.xml
new file mode 100644
index 0000000..c8ce23c
--- /dev/null
+++ b/core/res/res/anim/priority_alert_enter.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        >
+    <scale
+        android:interpolator="@android:anim/overshoot_interpolator"
+        android:fromXScale="0.7" android:toXScale="1.0"
+        android:fromYScale="0.7" android:toYScale="1.0"
+        android:pivotX="50%" android:pivotY="50%"
+        android:duration="@android:integer/config_shortAnimTime" />
+    <alpha 
+        android:interpolator="@android:anim/decelerate_interpolator"
+        android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="@android:integer/config_shortAnimTime" />
+</set>
diff --git a/core/res/res/anim/priority_alert_exit.xml b/core/res/res/anim/priority_alert_exit.xml
new file mode 100644
index 0000000..b538cb2
--- /dev/null
+++ b/core/res/res/anim/priority_alert_exit.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        >
+    <scale
+        android:interpolator="@android:anim/accelerate_interpolator"
+        android:fromXScale="1.0" android:toXScale="0.7"
+        android:fromYScale="1.0" android:toYScale="0.7"
+        android:pivotX="50%" android:pivotY="50%"
+        android:duration="@android:integer/config_shortAnimTime" />
+    <alpha 
+        android:interpolator="@android:anim/accelerate_interpolator"
+        android:fromAlpha="1.0" android:toAlpha="0.0"
+        android:duration="@android:integer/config_shortAnimTime" />
+</set>
diff --git a/core/res/res/drawable-hdpi/presence_audio_away.png b/core/res/res/drawable-hdpi/presence_audio_away.png
index f18b6c9..fb5e123 100644
--- a/core/res/res/drawable-hdpi/presence_audio_away.png
+++ b/core/res/res/drawable-hdpi/presence_audio_away.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_audio_busy.png b/core/res/res/drawable-hdpi/presence_audio_busy.png
index ae7b983..81aeb01 100644
--- a/core/res/res/drawable-hdpi/presence_audio_busy.png
+++ b/core/res/res/drawable-hdpi/presence_audio_busy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_audio_online.png b/core/res/res/drawable-hdpi/presence_audio_online.png
index 15e4513..553acbb 100644
--- a/core/res/res/drawable-hdpi/presence_audio_online.png
+++ b/core/res/res/drawable-hdpi/presence_audio_online.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_away.png b/core/res/res/drawable-hdpi/presence_away.png
index d4aa66b..84b00bb 100644
--- a/core/res/res/drawable-hdpi/presence_away.png
+++ b/core/res/res/drawable-hdpi/presence_away.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_busy.png b/core/res/res/drawable-hdpi/presence_busy.png
index 4b27853..d77a463 100644
--- a/core/res/res/drawable-hdpi/presence_busy.png
+++ b/core/res/res/drawable-hdpi/presence_busy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_invisible.png b/core/res/res/drawable-hdpi/presence_invisible.png
index 0e27fba..0d83e70a 100644
--- a/core/res/res/drawable-hdpi/presence_invisible.png
+++ b/core/res/res/drawable-hdpi/presence_invisible.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_offline.png b/core/res/res/drawable-hdpi/presence_offline.png
index e511aa4..2619e9f 100644
--- a/core/res/res/drawable-hdpi/presence_offline.png
+++ b/core/res/res/drawable-hdpi/presence_offline.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_online.png b/core/res/res/drawable-hdpi/presence_online.png
index d787d2f..7e846ef 100644
--- a/core/res/res/drawable-hdpi/presence_online.png
+++ b/core/res/res/drawable-hdpi/presence_online.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_video_away.png b/core/res/res/drawable-hdpi/presence_video_away.png
index f18b6c9..ab5493b 100644
--- a/core/res/res/drawable-hdpi/presence_video_away.png
+++ b/core/res/res/drawable-hdpi/presence_video_away.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_video_busy.png b/core/res/res/drawable-hdpi/presence_video_busy.png
index ae7b983..456cdb0 100644
--- a/core/res/res/drawable-hdpi/presence_video_busy.png
+++ b/core/res/res/drawable-hdpi/presence_video_busy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_video_online.png b/core/res/res/drawable-hdpi/presence_video_online.png
index 15e4513..bd9f8a4 100644
--- a/core/res/res/drawable-hdpi/presence_video_online.png
+++ b/core/res/res/drawable-hdpi/presence_video_online.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_audio_away.png b/core/res/res/drawable-mdpi/presence_audio_away.png
index f67e64e..db96754 100644
--- a/core/res/res/drawable-mdpi/presence_audio_away.png
+++ b/core/res/res/drawable-mdpi/presence_audio_away.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_audio_busy.png b/core/res/res/drawable-mdpi/presence_audio_busy.png
index 0ad991b..0a29948 100644
--- a/core/res/res/drawable-mdpi/presence_audio_busy.png
+++ b/core/res/res/drawable-mdpi/presence_audio_busy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_audio_online.png b/core/res/res/drawable-mdpi/presence_audio_online.png
index 6cc3d1a..df415ba 100644
--- a/core/res/res/drawable-mdpi/presence_audio_online.png
+++ b/core/res/res/drawable-mdpi/presence_audio_online.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_away.png b/core/res/res/drawable-mdpi/presence_away.png
index f8120df..f98e908 100644
--- a/core/res/res/drawable-mdpi/presence_away.png
+++ b/core/res/res/drawable-mdpi/presence_away.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_busy.png b/core/res/res/drawable-mdpi/presence_busy.png
index 9d7620b..933261f4 100644
--- a/core/res/res/drawable-mdpi/presence_busy.png
+++ b/core/res/res/drawable-mdpi/presence_busy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_invisible.png b/core/res/res/drawable-mdpi/presence_invisible.png
index 21399a4..8d72756 100644
--- a/core/res/res/drawable-mdpi/presence_invisible.png
+++ b/core/res/res/drawable-mdpi/presence_invisible.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_offline.png b/core/res/res/drawable-mdpi/presence_offline.png
index 3941b82..fc78396 100644
--- a/core/res/res/drawable-mdpi/presence_offline.png
+++ b/core/res/res/drawable-mdpi/presence_offline.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_online.png b/core/res/res/drawable-mdpi/presence_online.png
index 22d5683..943eee0 100644
--- a/core/res/res/drawable-mdpi/presence_online.png
+++ b/core/res/res/drawable-mdpi/presence_online.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_video_away.png b/core/res/res/drawable-mdpi/presence_video_away.png
index f67e64e..f3ec5d4 100644
--- a/core/res/res/drawable-mdpi/presence_video_away.png
+++ b/core/res/res/drawable-mdpi/presence_video_away.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_video_busy.png b/core/res/res/drawable-mdpi/presence_video_busy.png
index 0ad991b..7379e00 100644
--- a/core/res/res/drawable-mdpi/presence_video_busy.png
+++ b/core/res/res/drawable-mdpi/presence_video_busy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_video_online.png b/core/res/res/drawable-mdpi/presence_video_online.png
index 6cc3d1a..24ceb39 100644
--- a/core/res/res/drawable-mdpi/presence_video_online.png
+++ b/core/res/res/drawable-mdpi/presence_video_online.png
Binary files differ
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cffcd1d..d565c68 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -22,7 +22,7 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Component to be used as the status bar service.  Must implement the IStatusBar
          interface.  This name is in the ComponentName flattened format (package/class)  -->
-    <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.PhoneStatusBarService</string>
+    <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.StatusBarService</string>
 
     <!-- Do not translate. Defines the slots for the right-hand side icons.  That is to say, the
          icons in the status bar that are not notifications. -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 8a92757..99c0b3d 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -28,6 +28,10 @@
     <dimen name="toast_y_offset">64dip</dimen>
     <!-- Height of the status bar -->
     <dimen name="status_bar_height">25dip</dimen>
+    <!-- Height of the status bar -->
+    <dimen name="status_bar_icon_size">25dip</dimen>
+    <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
+    <dimen name="status_bar_edge_ignore">5dp</dimen>
     <!-- Size of the fastscroll hint letter -->
     <dimen name="fastscroll_overlay_size">104dp</dimen>
     <!-- Width of the fastscroll thumb -->
@@ -38,6 +42,4 @@
     <dimen name="password_keyboard_key_height">56dip</dimen>
     <!-- Default correction for the space key in the password keyboard -->
     <dimen name="password_keyboard_spacebar_vertical_correction">4dip</dimen>
-    <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
-    <dimen name="status_bar_edge_ignore">5dp</dimen>
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index af04117..02a601a 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -88,6 +88,13 @@
         <item name="windowExitAnimation">@anim/status_bar_exit</item>
     </style>
 
+    <!-- {@hide} -->
+    <style name="Animation.StatusBar.IntruderAlert"
+        parent="@android:style/Animation.StatusBar">
+        <item name="android:windowEnterAnimation">@anim/priority_alert_enter</item>
+        <item name="android:windowExitAnimation">@anim/priority_alert_exit</item>
+    </style>
+
     <!-- Standard animations for a translucent window or activity.  This
          style is <em>not<em> used by default for the translucent theme
          (since translucent activities are a special case that have no
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index ce623e8..30312b3 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -18,6 +18,7 @@
 -->
 
 <device name="Android">
+  <!-- All values are in mAh except as noted -->
   <item name="none">0</item>
   <item name="screen.on">0.1</item>
   <item name="bluetooth.active">0.1</item>
@@ -48,4 +49,6 @@
   <array name="cpu.active">
       <value>0.2</value>
   </array>
+  <!-- This is the battery capacity in mAh -->
+  <item name="battery.capacity">1000</item>
 </device>
diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
index 5480993..d298d40 100644
--- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml
+++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
@@ -43,6 +43,16 @@
         android:label="Test runner for Connectivity Manager Tests"
     />
 
+    <!--
+     To run the unit tests use the command:
+     "adb shell am instrument -w
+     com.android.connectivitymanagertest/.ConnectivityManagerUnitTestRunner"
+    -->
+    <instrumentation android:name=".ConnectivityManagerUnitTestRunner"
+        android:targetPackage="com.android.connectivitymanagertest"
+        android.label="Test runner for unit tests"
+    />
+
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java
new file mode 100644
index 0000000..6adfc74
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java
@@ -0,0 +1,47 @@
+/*
+ * 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 android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import android.util.Log;
+import com.android.connectivitymanagertest.unit.WifiSoftAPTest;
+
+import junit.framework.TestSuite;
+
+/**
+ * Instrumentation Test Runner for all unit tests
+ *
+ * adb shell am instrument \
+ *     -w com.android.connectivitymanagertest/.ConnectivityManagerUnitTestRunner
+ */
+
+public class ConnectivityManagerUnitTestRunner extends InstrumentationTestRunner {
+    @Override
+    public TestSuite getAllTests() {
+        TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(WifiSoftAPTest.class);
+        return suite;
+    }
+
+
+    @Override
+    public ClassLoader getLoader() {
+        return ConnectivityManagerUnitTestRunner.class.getClassLoader();
+    }
+}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java
new file mode 100644
index 0000000..3f43e48
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java
@@ -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.
+ */
+
+package com.android.connectivitymanagertest.unit;
+
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.Context;
+import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.Message;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.AndroidTestCase;
+
+import java.util.ArrayList;
+
+import android.util.Log;
+
+/**
+ * Test Wifi soft AP configuration
+ */
+public class WifiSoftAPTest extends AndroidTestCase {
+
+    private WifiManager mWifiManager;
+    private WifiConfiguration mWifiConfig = null;
+    private final String TAG = "WifiSoftAPTest";
+    private final int DURATION = 10000;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        assertNotNull(mWifiManager);
+        assertTrue(mWifiManager.setWifiApEnabled(null, true));
+        mWifiConfig = mWifiManager.getWifiApConfiguration();
+        if (mWifiConfig != null) {
+            Log.v(TAG, "mWifiConfig is " + mWifiConfig.toString());
+        } else {
+            Log.v(TAG, "mWifiConfig is null.");
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        Log.v(TAG, "turn off wifi tethering");
+        mWifiManager.setWifiApEnabled(null, false);
+        super.tearDown();
+    }
+
+    // Test case 1: Test the soft AP SSID with letters
+    @LargeTest
+    public void testApSsidWithAlphabet() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = "abcdefghijklmnopqrstuvwxyz";
+        config.allowedKeyManagement.set(KeyMgmt.NONE);
+        mWifiConfig = config;
+        assertTrue(mWifiManager.setWifiApEnabled(mWifiConfig, true));
+        try {
+            Thread.sleep(DURATION);
+        } catch (InterruptedException e) {
+            Log.v(TAG, "exception " + e.getStackTrace());
+            assertFalse(true);
+        }
+        assertNotNull(mWifiManager.getWifiApConfiguration());
+        assertEquals("wifi AP state is not enabled", WifiManager.WIFI_AP_STATE_ENABLED,
+                     mWifiManager.getWifiApState());
+    }
+}
diff --git a/docs/html/guide/developing/eclipse-adt.jd b/docs/html/guide/developing/eclipse-adt.jd
index cf2a457..66379a3 100644
--- a/docs/html/guide/developing/eclipse-adt.jd
+++ b/docs/html/guide/developing/eclipse-adt.jd
@@ -527,7 +527,7 @@
 <p>A library project's manifest file must declare all of the shared components
 that it includes, just as would a standard Android application. For more
 information, see the documentation for <a
-href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
+href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
 
 <p>For example, the <a
 href="{@docRoot}resources/samples/TicTacToeLib/AndroidManifest.html">TicTacToeLib</a>
@@ -613,7 +613,8 @@
   ...
 &lt;/manifest&gt;</pre>
 
-<p>For more information about the manifest file, see the documentation for <a href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
+<p>For more information about the manifest file, see the documentation for <a
+href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
 
 
 <h3 id="considerations">Development considerations</h3>
diff --git a/docs/html/guide/developing/other-ide.jd b/docs/html/guide/developing/other-ide.jd
index e8a6fb6..1d67aa9 100644
--- a/docs/html/guide/developing/other-ide.jd
+++ b/docs/html/guide/developing/other-ide.jd
@@ -687,7 +687,7 @@
 <p>A library project's manifest file must declare all of the shared components
 that it includes, just as would a standard Android application. For more
 information, see the documentation for <a
-href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
+href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
 
 <p>For example, the <a
 href="{@docRoot}resources/samples/TicTacToeLib/AndroidManifest.html">TicTacToeLib</a>
@@ -799,7 +799,8 @@
   ...
 &lt;/manifest&gt;</pre>
 
-<p>For more information about the manifest file, see the documentation for <a href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
+<p>For more information about the manifest file, see the documentation for <a
+href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
 
 <h3 id="depAppBuild">Building a dependent application</h3>
 
diff --git a/docs/html/guide/developing/tools/bmgr.jd b/docs/html/guide/developing/tools/bmgr.jd
index 2f49532..57deb25 100644
--- a/docs/html/guide/developing/tools/bmgr.jd
+++ b/docs/html/guide/developing/tools/bmgr.jd
@@ -15,6 +15,11 @@
 <li><a href="#other">Other Commands</a></li>
   </ol>
 
+  <h2>See also</h2>
+  <ol>
+    <li><a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a></li>
+  </ol>
+
 </div>
 </div>
 
@@ -26,6 +31,9 @@
 intrusive steps in order to test your application's backup agent.  These commands are
 accessed via the <a href="{@docRoot}guide/developing/tools/adb.html">adb</a> shell.
 
+<p>For information about adding support for backup in your application, read <a
+href="{@docRoot}guide/topics/data/backup.html">Data Backup</a>, which includes a guide to testing
+your application using {@code bmgr}.</p>
 
 
 <h2 id="backup">Forcing a Backup Operation</h2>
@@ -90,6 +98,8 @@
 
 <h2 id="other">Other Commands</h2>
 
+<h3>Wiping data</h3>
+
 <p>The data for a single application can be erased from the active data set on demand.  This is
 very useful while you're developing a backup agent, in case bugs lead you to write corrupt data
 or saved state information. You can wipe an application's data with the <code>bmgr wipe</code>
@@ -102,6 +112,9 @@
 erase.  The next backup operation that the application's agent processes will look as
 though the application had never backed anything up before.
 
+
+<h3>Enabling and disabling backup</h3>
+
 <p>You can see whether the Backup Manager is operational at all with the <code>bmgr
 enabled</code> command:
 
diff --git a/docs/html/guide/samples/index.jd b/docs/html/guide/samples/index.jd
index 2f3ac5e..bd9ea52 100644
--- a/docs/html/guide/samples/index.jd
+++ b/docs/html/guide/samples/index.jd
@@ -3,99 +3,13 @@
 @jd:body
 
 
-<p>Sometimes, the best way to learn how things are done is to look at some code.
-Here, you can browse the source of some sample Android applications that are included
-in the Android SDK.</p>
+<script type="text/javascript">
+  window.location = toRoot + "resources/samples/index.html";
+</script>
 
-<p>Each version of the Android platform available for the SDK includes a full set of sample
-applications (which may vary between different versions of the platform).
-You can find the samples in your SDK at:</p>
+<p><strong>This document has moved. Please go to <a
+href="http://developer.android.com/resources/samples/index.html">List of Sample
+Apps</a>.</strong></p>
 
-<p style="margin-left:2em">
-<code><em>&lt;sdk&gt;</em>/platforms/android-<em>&lt;version&gt;</em>/samples/</code>
-</p>
-
-<p>You can easily create new Android projects with these samples, modify them
-if you'd like, then run them on an emulator or device. For example, to create
-a project for the API Demos app from Eclipse,
-start a new Android Project, select "Create project from existing source", then select
-{@code ApiDemos} in the {@code samples/} directory. To create the API Demos project
-using the {@code android} tool, execute:</p>
-<pre>
-android update project -s -n API Demos -t <em>&lt;target_ID></em> -p <em>&lt;path-to-platform></em>/samples/ApiDemos/
-</pre>
-
-<p>The pages below provide an overview of each sample application (available with most
-platforms) and allow you to view the source files in your browser. </p>
-
-<div class="special">
-  <p>Some of the samples in this listing are not yet available in the
-  SDK. While we work to update the SDK, you can
-  <a href="{@docRoot}shareables/latest_samples.zip">download the latest samples</a> as a ZIP
-  archive.</p>
-</div>
-
-<dl>
-
- <dt><a href="{@docRoot}resources/samples/ApiDemos/index.html">API Demos</a></dt>
-  <dd>A variety of small applications that demonstrate an extensive collection of
-  framework topics.</dd>
-
- <dt><a href="{@docRoot}resources/samples/BackupRestore/index.html">Backup and Restore</a></dt>
-  <dd>An simple example that illustrates a few different ways for an application to
-  implement support for the Android data backup and restore mechanism.</dd>
-
- <dt><a href="{@docRoot}resources/samples/BluetoothChat/index.html">Bluetooth Chat</a></dt>
-  <dd>An application for two-way text messaging over Bluetooth.</dd>
-
- <dt><a href="{@docRoot}resources/samples/ContactManager/index.html">Contact Manager</a></dt>
-  <dd>An application that demonstrates how to query the system contacts provider 
-  using the <code>ContactsContract</code> API, as
-  well as insert contacts into a specific account.</dd>
-  
- <dt><a href="{@docRoot}resources/samples/Home/index.html">Home</a></dt>
-  <dd>A home screen replacement application.</dd>
-  
- <dt><a href="{@docRoot}resources/samples/JetBoy/index.html">JetBoy</a></dt>
-  <dd>JetBoy is a game that demonstrates the SONiVOX JET interactive music technology,
-  with {@link android.media.JetPlayer}.</dd>
-    
- <dt><a href="{@docRoot}resources/samples/LunarLander/index.html">Lunar Lander</a></dt>
-  <dd>A classic Lunar Lander game.</dd>
-
- <dt><a href="{@docRoot}resources/samples/MultiResolution/index.html">Multiple Resolutions</a></dt>
-  <dd>A sample application that shows how to use resource directory qualifiers to
-  provide different resources for different screen configurations.</dd>
-  
- <dt><a href="{@docRoot}resources/samples/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>
-  
- <dt><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary</a></dt>
-  <dd>A sample application that demonstrates Android's search framework, 
-  including how to provide search suggestions for Quick Search Box.</dd>
-  
- <dt><a href="{@docRoot}resources/samples/Snake/index.html">Snake</a></dt>
-  <dd>An implementation of the classic game "Snake."</dd>
-  
- <dt><a href="{@docRoot}resources/samples/SoftKeyboard/index.html">Soft Keyboard</a></dt>
-  <dd>An example of writing an input method for a software keyboard.</dd>
-
- <dt><a href=""{@docRoot}resources/samples/Wiktionary/index.html">Wiktionary</a></dt>
-  <dd>An example of creating interactive widgets for display on the Android
-  home screen.</dd>
-
- <dt><a href="{@docRoot}resources/samples/WiktionarySimple/index.html">Wiktionary (Simplified)</a></dt>
-  <dd>A simple Android home screen widgets example.</dd>
-    
-</dl>
-
-
-<div class="special">
-<p>For more sample applications, check out
-<a href="http://code.google.com/p/apps-for-android/">apps-for-android</a>, a
-collection of open source applications that demonstrate various Android APIs.
-</p>
-</div>
 
 
diff --git a/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd
index 4e74a83..6c02031 100644
--- a/docs/html/guide/topics/data/backup.jd
+++ b/docs/html/guide/topics/data/backup.jd
@@ -33,7 +33,7 @@
     <li><a href="#RestoreVersion">Checking the Restore Data Version</a></li>
     <li><a href="#RequestingBackup">Requesting Backup</a></li>
     <li><a href="#RequestingRestore">Requesting Restore</a></li>
-    <li><a href="#DevelopingTesting">Developing and Testing Your Backup Agent</a></li>
+    <li><a href="#Testing">Testing Your Backup Agent</a></li>
   </ol>
 
   <h2>Key classes</h2>
@@ -43,6 +43,11 @@
     <li>{@link android.app.backup.BackupAgentHelper}</li>
   </ol>
 
+  <h2>See also</h2>
+  <ol>
+    <li><a href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr} tool</a></li>
+  </ol>
+
 </div>
 </div>
 
@@ -235,7 +240,7 @@
 <h2 id="BackupAgent">Extending BackupAgent</h2>
 
 <p>Most applications shouldn't need to extend the {@link android.app.backup.BackupAgent} class
-directly, but should instead <a href="BackupAgentHelper">extend BackupAgentHelper</a> to take
+directly, but should instead <a href="#BackupAgentHelper">extend BackupAgentHelper</a> to take
 advantage of the built-in helper classes that automatically backup and restore your files. However,
 you might want to extend {@link android.app.backup.BackupAgent} directly if you need to:</p>
 <ul>
@@ -257,7 +262,7 @@
 <p>If you don't need to perform any of the tasks above and want to back up complete files from
 {@link android.content.SharedPreferences} or <a
 href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>, you
-should skip to <a href="BackupAgentHelper">Extending BackupAgentHelper</a>.</p>
+should skip to <a href="#BackupAgentHelper">Extending BackupAgentHelper</a>.</p>
 
 
 
@@ -308,7 +313,7 @@
 
 <p class="note"><strong>Tip:</strong> While developing your application, you can initiate an
 immediate backup operation from the Backup Manager with the <a
-href="{@docRoot}guide/developing/tools/bmgr.html">bmgr tool</a>.</p>
+href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr} tool</a>.</p>
 
 <p>When the Backup Manager calls your {@link
 android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
@@ -452,7 +457,7 @@
 href="#RequestingRestore">Requesting restore</a> for more information).</p>
 
 <p class="note"><strong>Note:</strong> While developing your application, you can also request a
-restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">bmgr
+restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}
 tool</a>.</p>
 
 <p>When the Backup Manager calls your {@link
@@ -571,8 +576,8 @@
 
 <h3 id="SharedPreferences">Backing up SharedPreferences</h3>
 
-<p>When you instantiate a {@link android.app.backup.SharedPreferencesBackupHelper}, you must the
-name of one or more {@link android.content.SharedPreferences} files.</p>
+<p>When you instantiate a {@link android.app.backup.SharedPreferencesBackupHelper}, you must
+include the name of one or more {@link android.content.SharedPreferences} files.</p>
 
 <p>For example, to back up a {@link android.content.SharedPreferences} file named
 "user_preferences", a complete backup agent using {@link android.app.backup.BackupAgentHelper} looks
@@ -813,7 +818,7 @@
 
 <p class="note"><strong>Note:</strong> While developing your application, you can request a
 backup and initiate an immediate backup operation with the <a
-href="{@docRoot}guide/developing/tools/bmgr.html">bmgr
+href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}
 tool</a>.</p>
 
 
@@ -828,25 +833,52 @@
 implementation, passing the data from the current set of backup data.</p>
 
 <p class="note"><strong>Note:</strong> While developing your application, you can request a
-restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">bmgr
+restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}
 tool</a>.</p>
 
 
-<h2 id="DevelopingTesting">Developing and Testing Your Backup Agent</h2>
+<h2 id="Testing">Testing Your Backup Agent</h2>
 
-<p>To develop and test your backup agent:</p>
-<ul>
-  <li>Set your build target to a platform using API Level 8 or higher</li>
-  <li>Run your application on a suitable Android system image:
+<p>Once you've implemented your backup agent, you can test the backup and restore functionality
+with the following procedure, using <a
+href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}</a>.</p>
+
+<ol>
+  <li>Install your application on a suitable Android system image
     <ul>
-      <li>If using the emulator, create and use an AVD with the Google APIs add-on (API Level
-8) &mdash; the Google APIs add-on is available as an SDK component through the SDK and AVD
-Manager</li>
+      <li>If using the emulator, create and use an AVD with Android 2.2 (API Level 8).</li>
       <li>If using a device, the device must be running Android 2.2 or greater and have Android
-Market built in</li>
+Market built in.</li>
     </ul>
   </li>
-  <li>Test your backup agent using the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code
-bmgr}</a> tool to initiate backup and restore operations</li>
-</ul>
+  <li>Ensure that backup is enabled
+    <ul>
+      <li>If using the emulator, you can enable backup with the following command from your SDK
+{@code tools/} path:
+<pre class="no-pretty-print">adb shell bmgr enable true</pre>
+      </li>
+      <li>If using a device, open the system <b>Settings</b>, select <b>Privacy</b>, then enable
+<b>Back up my data</b> and <b>Automatic restore</b>.
+    </ul>
+  </li>
+  <li>Open your application and initialize some data
+    <p>If you've properly implemented backup in your application, then it should request a
+backup each time the data changes. For example, each time the user changes some data, your app
+should call {@link android.app.backup.BackupManager#dataChanged()}, which adds a backup request to
+the Backup Manager queue. For testing purposes, you can also make a request with the following
+{@code bmgr} command:</p>
+<pre class="no-pretty-print">adb shell bmgr backup <em>your.package.name</em></pre>
+  </li>
+  <li>Initiate a backup operation:
+<pre class="no-pretty-print">adb shell bmgr run</pre>
+    <p>This forces the Backup Manager to perform all backup requests that are in its
+queue.</p>
+  <li>Uninstall your application:
+<pre class="no-pretty-print">adb uninstall <em>your.package.name</em></pre>
+  </li>
+  <li>Re-install your application.</li>
+</ol>
+
+<p>If your backup agent is successful, all the data you initialized in step 4 is restored.</p>
+
 
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index cac85e8..7e2f8a0 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -761,7 +761,7 @@
 cannot use the resources named with the new qualifier. For example, if your <a
 href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
 minSdkVersion}</a> is set to 4, and you qualify all of your drawable resources using <a
-href="NightQualifier">night mode</a> ({@code night} or {@code notnight}, which were added in API
+href="#NightQualifier">night mode</a> ({@code night} or {@code notnight}, which were added in API
 Level 8), then an API Level 4 device cannot access your drawable resources and will crash. In this
 case, you probably want {@code notnight} to be your default resources, so you should exclude that
 qualifier so your drawable resources are in either {@code drawable/} or {@code drawable-night/}.</p>
diff --git a/docs/html/guide/topics/ui/menus.jd b/docs/html/guide/topics/ui/menus.jd
index cf3c7de..b4e467c 100644
--- a/docs/html/guide/topics/ui/menus.jd
+++ b/docs/html/guide/topics/ui/menus.jd
@@ -5,198 +5,324 @@
 
 <div id="qv-wrapper">
 <div id="qv">
-  <h2>Key classes</h2>
-  <ol>
-    <li>{@link android.view.Menu}</li>
-    <li>{@link android.view.ContextMenu}</li>
-    <li>{@link android.view.SubMenu}</li>
-  </ol>
   <h2>In this document</h2>
   <ol>
-    <li><a href="#options-menu">Options Menu</a></li>
-    <li><a href="#context-menu">Context Menu</a></li>
-    <li><a href="#submenu">Submenu</a></li>
-    <li><a href="#xml">Define Menus in XML</a></li>
-    <li><a href="#features">Menu Features</a>
+    <li><a href="#xml">Defining Menus</a></li>
+    <li><a href="#Inflating">Inflating a Menu Resource</a>
+    <li><a href="#options-menu">Creating an Options Menu</a>
+      <ol>
+        <li><a href="#ChangingTheMenu">Changing the menu when it opens</a></li>
+      </ol>
+    </li>
+    <li><a href="#context-menu">Creating a Context Menu</a></li>
+    <li><a href="#submenu">Creating a Submenu</a></li>
+    <li><a href="#features">Other Menu Features</a>
       <ol>
         <li><a href="#groups">Menu groups</a></li>
         <li><a href="#checkable">Checkable menu items</a></li>
         <li><a href="#shortcuts">Shortcut keys</a></li>
-        <li><a href="#intents">Menu item intents</a></li>
+        <li><a href="#intents">Intents for menu items</a></li>
       </ol>
     </li>
   </ol>
+
+  <h2>Key classes</h2>
+  <ol>
+    <li>{@link android.view.Menu}</li>
+    <li>{@link android.view.MenuItem}</li>
+    <li>{@link android.view.ContextMenu}</li>
+    <li>{@link android.view.SubMenu}</li>
+  </ol>
+
+  <h2>See also</h2>
+  <ol>
+    <li><a href="{@docRoot}guide/topics/resources/menu-resource.html">Menu Resource</a></li>
+  </ol>
 </div>
 </div>
 
-<p>Menus are an important part of any application. They provide familiar interfaces
-that reveal application functions and settings. Android offers an easy programming interface
-for developers to provide standardized application menus for various situations.</p>
+<p>Menus are an important part of an application that provide a familiar interface for the user
+to access application functions and settings. Android offers an easy programming interface
+for you to provide application menus in your application.</p>
 
-<p>Android offers three fundamental types of application menus:</p>
+<p>Android provides three types of application menus:</p>
 <dl>
   <dt><strong>Options Menu</strong></dt>
-    <dd>This is the primary set of menu items for an Activity. It is revealed by pressing 
-    the device MENU key. Within the Options Menu are two groups of menu items:
+    <dd>The primary menu for an Activity, which appears when the user presses
+    the device MENU key. Within the Options Menu are two groups:
       <dl style="margin-top:1em">
         <dt><em>Icon Menu</em></dt>
-          <dd>This is the collection of items initially visible at the bottom of the screen 
+          <dd>The menu items visible at the bottom of the screen
           at the press of the MENU key. It supports a maximum of six menu items.
           These are the only menu items that support icons and the only menu items that <em>do not</em> support
           checkboxes or radio buttons.</dd>
         <dt><em>Expanded Menu</em></dt>
-          <dd>This is a vertical list of items exposed by the "More" menu item from the Icon Menu.
-          It exists only when the Icon Menu becomes over-loaded and is comprised of the sixth 
-          Option Menu item and the rest.</dd>
+          <dd>The vertical list of menu items exposed by the "More" menu item in the Icon Menu.
+          When the Icon Menu is full, the expanded menu is comprised of the sixth
+          menu item and the rest.</dd>
       </dl>
     </dd>
   <dt><strong>Context Menu</strong></dt>
-    <dd>This is a floating list of menu items that may appear when you perform a long-press on a View 
-    (such as a list item). </dd>
+    <dd>A floating list of menu items that appears when the user performs a long-press on a View.
+</dd>
   <dt><strong>Submenu</strong></dt>
-    <dd>This is a floating list of menu items that is revealed by an item in the Options Menu
-    or a Context Menu. A Submenu item cannot support nested Submenus. </dd>
+    <dd>A floating list of menu items that the user opens by pressing a menu item in the Options
+Menu or a context menu. A submenu item cannot support a nested submenu. </dd>
 </dl>
 
 
-<h2 id="options-menu">Options Menu</h2>
-<img align="right" src="{@docRoot}images/options_menu.png" />
-<p>The Options Menu is opened by pressing the device MENU key.
-When opened, the Icon Menu is displayed, which holds the first six menu items.
-If more than six items are added to the Options Menu, then those that can't fit
-in the Icon Menu are revealed in the Expanded Menu, via the "More" menu item. The Expanded Menu
-is automatically added when there are more than six items.</p>
 
-<p>The Options Menu is where you should include basic application functions
-and any necessary navigation items (e.g., to a home screen or application settings).
-You can also add <a href="#submenu">Submenus</a> for organizing topics 
-and including extra menu functionality.</p>
+<h2 id="xml">Defining Menus</h2>
 
-<p>When this menu is opened for the first time, 
-the Android system will call the Activity <code>{@link android.app.Activity#onCreateOptionsMenu(Menu)
-onCreateOptionsMenu()}</code> callback method. Override this method in your Activity
-and populate the {@link android.view.Menu} object given to you. You can populate the menu by
-inflating a menu resource that was <a href="#xml">defined in XML</a>, or by calling 
-<code>{@link android.view.Menu#add(CharSequence) add()}</code>
-for each item you'd like in the menu. This method adds a {@link android.view.MenuItem}, and returns the
-newly created object to you. You can use the returned MenuItem to set additional properties like 
-an icon, a keyboard shortcut, an intent, and other settings for the item.</p>
+<p>Instead of instantiating {@link android.view.Menu} objects in your application code, you should
+define a menu and all its items in an XML <a
+href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>, then inflate the menu
+resource (load it as a programmable object) in your application code. Defining your menus in XML is
+a good practice because it separates your interface design from your application code (the same as
+when you <a href="{@docRoot}guide/topics/ui/declaring-layout.html">define your Activity
+layout</a>).</p>
 
-<p>There are multiple <code>{@link android.view.Menu#add(CharSequence) add()}</code> methods.
-Usually, you'll want to use one that accepts an <var>itemId</var> argument. 
-This is a unique integer that allows you to identify the item during a callback.</p>
+<p>To define a menu, create an XML file inside your project's <code>res/menu/</code>
+directory and build the menu with the following elements:</p>
+<dl>
+  <dt><code>&lt;menu></code></dt>
+    <dd>Creates a {@link android.view.Menu}, which is a container for menu items. It must be
+the root node and holds one or more of the following elements. You can also nest this element
+in an {@code &lt;item&gt;} to create a submenu.</dd>
+  <dt><code>&lt;item></code></dt>
+    <dd>Creates a {@link android.view.MenuItem}, which represents a single item in a menu.</dd>
+  <dt><code>&lt;group></code></dt>
+    <dd>An optional, invisible container for {@code &lt;item&gt;} elements. It allows you to
+categorize menu items so they share properties such as active state and visibility. See <a
+href="#groups">Menu groups</a>.</dd>
+</dl>
 
-<p>When a menu item is selected from the Options Menu, you will receive a callback to the
-<code>{@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}</code> 
-method of your Activity. This callback passes you the 
-<code>MenuItem</code> that has been selected. You can identify the item by requesting the 
-<var>itemId</var>, with <code>{@link android.view.MenuItem#getItemId() getItemId()}</code>,
-which returns the integer that was assigned with the <code>add()</code> method. Once you identify
-the menu item, you can take the appropriate action.</p>
+<p>For example, here is a file in <code>res/menu/</code> named <code>game_menu.xml</code>:</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+    &lt;item android:id="@+id/new_game"
+          android:icon="@drawable/ic_new_game"
+          android:title="@string/new_game" /&gt;
+    &lt;item android:id="@+id/quit"
+          android:icon="@drawable/ic_quit"
+          android:title="@string/quit" /&gt;
+&lt;/menu&gt;
+</pre>
 
-<p>Here's an example of this procedure, inside an Activity, wherein we create an 
-Options Menu and handle item selections:</p>
+<p>This example defines a menu with two menu items. Each item includes the attributes:</p>
+<dl>
+  <dt>{@code android:id}</dt>
+    <dd>A resource ID that's unique to the item so that the application can recognize the item when
+the user selects it.</dd>
+  <dt>{@code android:icon}</dt>
+    <dd>A drawable resource that is the icon visible to the user.</dd>
+  <dt>{@code android:title}</dt>
+    <dd>A string resource that is the title visible to the user.</dd>
+</dl>
+
+<p>For more about the XML syntax and attributes for a menu resource, see the <a
+href="{@docRoot}guide/topics/resources/menu-resource.html">Menu Resource</a> reference.</p>
+
+
+<h2 id="Inflating">Inflating a Menu Resource</h2>
+
+<p>You can inflate your menu resource (convert the XML resource into a programmable object) using
+{@link android.view.MenuInflater#inflate(int,Menu) MenuInflater.inflate()}. For
+example, the following code inflates the <code>game_menu.xml</code> file defined above during the
+{@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} callback method, to be
+used for the Options Menu:</p>
 
 <pre>
-/* Creates the menu items */
+&#64;Override
 public boolean onCreateOptionsMenu(Menu menu) {
-    menu.add(0, MENU_NEW_GAME, 0, "New Game");
-    menu.add(0, MENU_QUIT, 0, "Quit");
+    MenuInflater inflater = getMenuInflater();
+    inflater.inflate(R.menu.game_menu, menu);
     return true;
 }
-
-/* Handles item selections */
-public boolean onOptionsItemSelected(MenuItem item) {
-    switch (item.getItemId()) {
-    case MENU_NEW_GAME:
-        newGame();
-        return true;
-    case MENU_QUIT:
-        quit();
-        return true;
-    }
-    return false;
-}
 </pre>
 
-<p>The <code>add()</code> method used in this sample takes four arguments: 
-<var>groupId</var>, <var>itemId</var>, <var>order</var>, and <var>title</var>.
-The <var>groupId</var> allows you to associate this menu item with a group of other items
-(more about <a href="#groups">Menu groups</a>, below) &mdash; in 
-this example, we ignore it. <var>itemId</var> is a unique integer that we give the 
-MenuItem so that can identify it in the next callback. <var>order</var> allows us to 
-define the display order of the item &mdash; by default, they are displayed by the
-order in which we add them. <var>title</var> is, of course, the name that goes on the 
-menu item (this can also be a 
-<a href="{@docRoot}guide/topics/resources/available-resources.html#stringresources">string resource</a>, 
-and we recommend you do it that way for easier localization).</p>
+<p>The {@link android.app.Activity#getMenuInflater()} method returns a {@link
+android.view.MenuInflater} for the Activity. With this object, you can call {@link
+android.view.MenuInflater#inflate(int,Menu) inflate()}, which inflates a menu resource into a
+{@link android.view.Menu} object. In this example, the menu resource defined by
+<code>game_menu.xml</code>
+is inflated into the {@link android.view.Menu} that was passed into {@link
+android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()}. (This callback method for
+creating an option menu is discussed more in the next section.)</p>
 
-<p class="note"><strong>Tip:</strong>
-If you have several menu items that can be grouped together with a title, 
-consider organizing them into a <a href="#submenu">Submenu</a>.</p>
 
-<h3>Adding icons</h3>
-<p>Icons can also be added to items that appears in the Icon Menu with
-<code>{@link android.view.MenuItem#setIcon(Drawable) setIcon()}</code>. For example:</p>
+
+<h2 id="options-menu">Creating an Options Menu</h2>
+
+<div class="figure" style="width:200px">
+  <img src="{@docRoot}images/options_menu.png" height="300" alt="" />
+  <p class="img-caption"><strong>Figure 1.</strong> Screenshot of an Options Menu.</p>
+</div>
+
+
+<p>The Options Menu is where you should include basic application functions
+and necessary navigation items (for example, a button
+to open application settings). The user
+can open the Options Menu with the device MENU key.
+Figure 1 shows a screenshot of an Options Menu.</p>
+
+<p>When opened, the first visible portion of the Options Menu is called the Icon Menu. It
+holds the first six menu items.
+If you add more than six items to the Options Menu, Android places the sixth item and those after it
+into the Expanded Menu, which the user can open with the "More" menu item.</p>
+
+<p>When the user opens the Options Menu for the first time, Android calls your Activity's
+{@link android.app.Activity#onCreateOptionsMenu(Menu)
+onCreateOptionsMenu()} method. Override this method in your Activity
+and populate the {@link android.view.Menu} that is passed into the method. Populate the
+{@link android.view.Menu} by inflating a menu resource as described in <a
+href="#Inflating">Inflating a Menu Resource</a>. (You can
+also populate the menu in code, using {@link android.view.Menu#add(int,int,int,int)
+add()} to add menu items.)</p>
+
+<p>When the user selects a menu item from the Options Menu, the system calls your Activity's
+{@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}
+method. This method passes the
+{@link android.view.MenuItem} that the user selected. You can identify the menu item by calling
+{@link android.view.MenuItem#getItemId()}, which returns the unique ID for the menu
+item (defined by the {@code android:id} attribute in the menu resource or with an integer passed
+to the {@link android.view.Menu#add(int,int,int,int) add()} method). You can match this ID
+against known menu items and perform the appropriate action.</p>
+
+<p>For example:</p>
+
 <pre>
-menu.add(0, MENU_QUIT, 0, "Quit")
-    .setIcon(R.drawable.menu_quit_icon);</pre>
+&#64;Override
+public boolean onOptionsItemSelected(MenuItem item) {
+    // Handle item selection
+    switch (item.getItemId()) {
+    case R.id.new_game:
+        newGame();
+        return true;
+    case R.id.quit:
+        quit();
+        return true;
+    default:
+        return super.onOptionsItemSelected(item);
+    }
+}
+</pre>
 
-<h3>Modifying the menu</h3>
-<p>If you want to sometimes re-write the Options Menu as it is opened, override the 
-<code>{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()}</code> method, which is
-called each time the menu is opened. This will pass you the Menu object, just like the 
-<code>onCreateOptionsMenu()</code> callback. This is useful if you'd like to add or remove
-menu options depending on the current state of an application or game.</p>
+<p>In this example, {@link android.view.MenuItem#getItemId()} queries the ID for the selected menu
+item and the switch statement compares the ID against the resource IDs that were assigned to menu
+items in the XML resource. When a switch case successfully handles the item, it
+returns "true" to indicate that the item selection was handled. Otherwise, the default statement
+passes the menu item to the super class in
+case it can handle the item selected. (If you've directly extended the {@link android.app.Activity}
+class, then the super class returns "false", but it's a good practice to
+pass unhandled menu items to the super class instead of directly returning "false".)</p>
+
+<p class="note"><strong>Tip:</strong> If your application contains multiple activities and
+some of them provide the same Options Menu, consider creating
+an Activity that implements nothing except the {@link android.app.Activity#onCreateOptionsMenu(Menu)
+onCreateOptionsMenu()} and {@link android.app.Activity#onOptionsItemSelected(MenuItem)
+onOptionsItemSelected()} methods. Then extend this class for each Activity that should share the
+same Options Menu. This way, you have to manage only one set of code for handling menu
+actions and each decendent class inherits the menu behaviors.<br/><br/>
+If you want to add menu items to one of your decendent activities,
+override {@link android.app.Activity#onCreateOptionsMenu(Menu)
+onCreateOptionsMenu()} in that Activity. Call {@code super.onCreateOptionsMenu(menu)} so the
+original menu items are created, then add new menu items with {@link
+android.view.Menu#add(int,int,int,int) menu.add()}. You can also override the super class's
+behavior for individual menu items.</p>
+
+
+<h3 id="ChangingTheMenu">Changing the menu when it opens</h3>
+
+<p>The {@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} method is
+called only the first time the Options Menu is opened. The system keeps and re-uses the {@link
+android.view.Menu} you define in this method until your Activity is destroyed. If you want to change
+the Options Menu each time it opens, you must override the
+{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()} method. This passes
+you the {@link android.view.Menu} object as it currently exists. This is useful if you'd like to
+remove, add, disable, or enable menu items depending on the current state of your application.</p>
 
 <p class="note"><strong>Note:</strong> 
-When changing items in the menu, it's bad practice to do so based on the currently selected item.
-Keep in mind that, when in touch mode, there will not be a selected (or focused) item. Instead, you
-should use a <a href="#context-menu">Context Menu</a> for such behaviors, when you want to provide 
-functionality based on a particular item in the UI.</p>
+You should never change items in the Options Menu based on the {@link android.view.View} currently
+in focus. When in touch mode (when the user is not using a trackball or d-pad), Views
+cannot take focus, so you should never use focus as the basis for modifying
+items in the Options Menu. If you want to provide menu items that are context-sensitive to a {@link
+android.view.View}, use a <a href="#context-menu">Context Menu</a>.</p>
 
 
-<h2 id="context-menu">Context Menu</h2>
-<p>The Android context menu is similar, in concept, to the menu revealed with a "right-click" on a PC. 
-When a view is registered to a context menu, 
-performing a "long-press" (press and hold for about two seconds) on the object
-will reveal a floating menu that provides functions relating to that item. 
-Context menus can be registered to any View object,
-however, they are most often used for items in a 
-{@link android.widget.ListView}, which helpfully indicates the presence of the context menu
-by transforming the background color of the ListView item when pressed.
-(The items in the phone's contact list offer an example of this feature.)
-</p>
 
-<p class="note"><strong>Note:</strong> Context menu items do not support icons or shortcut keys.</p>
+<h2 id="context-menu">Creating a Context Menu</h2>
 
-<p>To create a context menu, you must override the Activity's context menu callback methods:
-<code>{@link android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo) onCreateContextMenu()}</code> and 
-<code>{@link android.app.Activity#onContextItemSelected(MenuItem) onContextItemSelected()}</code>.
-Inside the <code>onCreateContextMenu()</code> callback method, you can add menu items using one of the 
-<code>{@link android.view.Menu#add(CharSequence) add()}</code> methods, or by 
-inflating a menu resource that was <a href="#xml">defined in XML</a>.
-Then, register a {@link android.view.ContextMenu} for the View, with 
-<code>{@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()}</code>.</p>
+<p>A context menu is conceptually similar to the menu displayed when the user performs a
+"right-click" on a PC. You should use a context menu to provide the user access to
+actions that pertain to a specific item in the user interface. On Android, a context menu is
+displayed when the user performs a "long press" (press and hold) on an item.</p>
 
-<p>For example, here is some code that can be used with the 
-<a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad application</a>
-to add a context menu for each note in the list:</p>
+<p>You can create a context menu for any View, though context menus are most often used for items in
+a {@link android.widget.ListView}. When the user performs a long-press on an item in a ListView and
+the list is registered to provide a context menu, the list item signals to the user that a context
+menu is available by animating its background color&mdash;it transitions from
+orange to white before opening the context menu. (The Contacts application demonstrates this
+feature.)</p>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h3>Register a ListView</h3>
+<p>If your Activity uses a {@link android.widget.ListView} and
+you want all list items to provide a context menu, register all items for a context
+menu by passing the {@link android.widget.ListView} to {@link
+android.app.Activity#registerForContextMenu(View) registerForContextMenu()}. For
+example, if you're using a {@link android.app.ListActivity}, register all list items like this:</p>
+<p><code>registerForContextMenu({@link android.app.ListActivity#getListView()});</code></p>
+</div>
+</div>
+
+<p>In order for a View to provide a context menu, you must "register" the view for a context
+menu. Call {@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()} and
+pass it the {@link android.view.View} you want to give a context menu. When this View then
+receives a long-press, it displays a context menu.</p>
+
+<p>To define the context menu's appearance and behavior, override your Activity's context menu
+callback methods, {@link android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo)
+onCreateContextMenu()} and
+{@link android.app.Activity#onContextItemSelected(MenuItem) onContextItemSelected()}.</p>
+
+<p>For example, here's an {@link
+android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo)
+onCreateContextMenu()} that uses the {@code context_menu.xml} menu resource:</p>
 <pre>
+&#64;Override
 public void onCreateContextMenu(ContextMenu menu, View v,
                                 ContextMenuInfo menuInfo) {
   super.onCreateContextMenu(menu, v, menuInfo);
-  menu.add(0, EDIT_ID, 0, "Edit");
-  menu.add(0, DELETE_ID, 0,  "Delete");
+  MenuInflater inflater = getMenuInflater();
+  inflater.inflate(R.menu.context_menu, menu);
 }
+</pre>
 
+<p>{@link android.view.MenuInflater} is used to inflate the context menu from a <a
+href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>. (You can also use
+{@link android.view.Menu#add(int,int,int,int) add()} to add menu items.) The callback method
+parameters include the {@link android.view.View}
+that the user selected and a {@link android.view.ContextMenu.ContextMenuInfo} object that provides
+additional information about the item selected. You might use these parameters to determine
+which context menu should be created, but in this example, all context menus for the Activity are
+the same.</p>
+
+<p>Then when the user selects an item from the context menu, the system calls {@link
+android.app.Activity#onContextItemSelected(MenuItem) onContextItemSelected()}. Here is an example
+of how you can handle selected items:</p>
+
+<pre>
+&#64;Override
 public boolean onContextItemSelected(MenuItem item) {
   AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
   switch (item.getItemId()) {
-  case EDIT_ID:
+  case R.id.edit:
     editNote(info.id);
     return true;
-  case DELETE_ID:
+  case R.id.delete:
     deleteNote(info.id);
     return true;
   default:
@@ -205,285 +331,276 @@
 }
 </pre>
 
-<p>In <code>onCreateContextMenu()</code>, we are given not only the ContextMenu to
-which we will add {@link android.view.MenuItem}s, but also the {@link android.view.View}
-that was selected and a {@link android.view.ContextMenu.ContextMenuInfo ContextMenuInfo} object,
-which provides additional information about the object that was selected. 
-In this example, nothing special is done in <code>onCreateContextMenu()</code> &mdash; just
-a couple items are added as usual. In the <code>onContextItemSelected()</code>
-callback, we request the {@link android.widget.AdapterView.AdapterContextMenuInfo AdapterContextMenuInfo} 
-from the {@code MenuItem}, which provides information about the currently selected item. 
-All we need from
-this is the list ID for the selected item, so whether editing a note or deleting it, 
-we find the ID with the {@code AdapterContextMenuInfo.info} field of the object. This ID
-is passed to the <code>editNote()</code> and <code>deleteNote()</code> methods to perform
-the respective action.</p>
+<p>The structure of this code is similar to the example for <a href="#options-menu">Creating an
+Options Menu</a>, in which {@link android.view.MenuItem#getItemId()} queries the ID for the selected
+menu item and a switch statement matches the item to the IDs that are defined in the menu resource.
+And like the options menu example, the default statement calls the super class in case it
+can handle menu items not handled here, if necessary.</p>
 
-<p>Now, to register this context menu for all the items in a {@link android.widget.ListView},
-we pass the entire {@code ListView} to the 
-<code>{@link android.app.Activity#registerForContextMenu(View)}</code> method:</p>
+<p>In this example, the selected item is an item from a {@link android.widget.ListView}. To
+perform an action on the selected item, the application needs to know the list
+ID for the selected item (it's position in the ListView). To get the ID, the application calls
+{@link android.view.MenuItem#getMenuInfo()}, which returns a {@link
+android.widget.AdapterView.AdapterContextMenuInfo} object that includes the list ID for the
+selected item in the {@link android.widget.AdapterView.AdapterContextMenuInfo#id id} field. The
+local methods <code>editNote()</code> and <code>deleteNote()</code> methods accept this list ID to
+perform an action on the data specified by the list ID.</p>
 
-<pre>registerForContextMenu(getListView());</pre>
-<p>Remember, you can pass any View object to register a context menu. Here,
-<code>{@link android.app.ListActivity#getListView()}</code> returns the ListView
-object used in the Notepad application's {@link android.app.ListActivity}. As such, each item 
-in the list is registered to this context menu.</p>
+<p class="note"><strong>Note:</strong> Items in a context menu do not support icons or shortcut
+keys.</p>
 
 
 
-<h2 id="submenu">Submenus</h2>
-<p>A sub menu can be added within any menu, except another sub menu.
-These are very useful when your application has a lot of functions that may be
-organized in topics, like the items in a PC application's menu bar (File, Edit, View, etc.).</p>
+<h2 id="submenu">Creating Submenus</h2>
 
-<p>A sub menu is created by adding it to an existing {@link android.view.Menu}
-with <code>{@link android.view.Menu#addSubMenu(CharSequence) addSubMenu()}</code>. 
-This returns a {@link android.view.SubMenu} object (an extension of {@link android.view.Menu}). 
-You can then add additional items to this menu, with the normal routine, using
-the <code>{@link android.view.Menu#add(CharSequence) add()}</code> methods. For example:</p>
+<p>A submenu is a menu that the user can open by selecting an item in another menu. You can add a
+submenu to any menu (except a submenu). Submenus are useful when your application has a lot of
+functions that can be organized into topics, like items in a PC application's menu bar (File, Edit,
+View, etc.).</p>
+
+<p>When creating your <a href="{@docRoot}guide/topics/resources/menu-resource.html">menu
+resource</a>, you can create a submenu by adding a {@code &lt;menu&gt;} element as the child of an
+{@code &lt;item&gt;}. For example:</p>
 
 <pre>
-public boolean onCreateOptionsMenu(Menu menu) {
-  boolean result = super.onCreateOptionsMenu(menu);
-
-  SubMenu fileMenu = menu.addSubMenu("File");
-  SubMenu editMenu = menu.addSubMenu("Edit");
-  fileMenu.add("new");
-  fileMenu.add("open");
-  fileMenu.add("save");
-  editMenu.add("undo");
-  editMenu.add("redo");
-
-  return result;
-}
-</pre>
-<p>Callbacks for items selected in a sub menu are made to the parent menu's callback method. 
-For the example above, selections in the sub menu will be handled by the 
-<code>onOptionsItemSelected()</code> callback.</p>
-<p>You can also add Submenus when you <a href="#xml">define the parent menu in XML</a>.</p>
-
-
-<h2 id="xml">Define Menus in XML</h2>
-<p>Just like Android UI layouts, you can define application menus in XML, then inflate them 
-in your menu's <code>onCreate...()</code> callback method. This makes your application code cleaner and
-separates more interface design into XML, which is easier to visualize.</p>
-
-<p>To start, create a new folder in your project <code>res/</code> directory called <code>menu</code>.
-This is where you should keep all XML files that define your application menus.</p>
-
-<p>In a menu XML layout, there are
-three valid elements: <code>&lt;menu></code>, <code>&lt;group></code> and <code>&lt;item></code>. The
-<code>item</code> and <code>group</code> elements must be children of a <code>menu</code>, but <code>item</code>
-elements may also be the children of a <code>group</code>, and another <code>menu</code> element may be the child
-of an <code>item</code> (to create a Submenu). Of course, the root node of any file
-must be a <code>menu</code> element.</p>
-
-<p>As an example, we'll define the same menu created in the <a href="#options-menu">Options Menu</a> section, 
-above. We start with an XML file named <code>options_menu.xml</code> inside the <code>res/menu/</code> folder:</p>
-<pre>
-&lt;menu xmlns:android="http://schemas.android.com/apk/res/android">
-    &lt;item android:id="@+id/new_game"
-          android:title="New Game" />
-    &lt;item android:id="@+id/quit"
-          android:title="Quit" />
-&lt;/menu>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+    &lt;item android:id="@+id/file"
+          android:icon="@drawable/file"
+          android:title="@string/file" &gt;
+        &lt;!-- "file" submenu --&gt;
+        &lt;menu"&gt;
+            &lt;item android:id="@+id/new"
+                  android:title="@string/new" /&gt;
+            &lt;item android:id="@+id/open"
+                  android:title="@string/open" /&gt;
+        &lt;/menu&gt;
+    &lt;/item&gt;
+&lt;/menu&gt;
 </pre>
 
-<p>Then, in the <code>onCreateOptionsMenu()</code> method, we inflate this resource using
-<code>{@link android.view.MenuInflater#inflate(int,Menu) MenuInflater.inflate()}</code>:</p>
-<pre>
-public boolean onCreateOptionsMenu(Menu menu) {
-    MenuInflater inflater = getMenuInflater();
-    inflater.inflate(R.menu.options_menu, menu);
-    return true;
-}
-</pre>
+<p>When the user selects an item from a submenu, the parent menu's respective on-item-selected
+callback method receives the event. For instance, if the above menu is applied as an Options Menu,
+then the {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} method
+is called when a submenu item is selected.</p>
 
-<p>The <code>{@link android.app.Activity#getMenuInflater()}</code> method returns the {@link android.view.MenuInflater}
-for our activity's context. We then call <code>{@link android.view.MenuInflater#inflate(int,Menu) inflate()}</code>,
-passing it a pointer to our menu resource and the Menu object given by the callback.</code></p>
+<p>You can also use {@link android.view.Menu#addSubMenu(int,int,int,int) addSubMenu()} to
+dynamically add a {@link android.view.SubMenu} to an existing {@link android.view.Menu}. This
+returns the new {@link android.view.SubMenu} object, to which you can add
+submenu items, using {@link android.view.Menu#add(int,int,int,int) add()}</p>
 
-<p>While this small sample may seem like more effort, compared to creating the menu items in the 
-<code>onCreateOptionsMenu()</code> method, this will save a lot of trouble when dealing with more items
-and it keeps your application code clean.</p>
 
-<p>You can define <a href="#groups">menu groups</a> by wrapping <code>item</code> elements in a <code>group</code>
-element, and create Submenus by nesting another <code>menu</code> inside an <code>item</code>.
-Each element also supports all the necessary attributes to control features like shortcut keys,
-checkboxes, icons, and more. To learn about these attributes and more about the XML syntax, see the Menus
-topic in the <a href="{@docRoot}guide/topics/resources/available-resources.html#menus">Available 
-Resource Types</a> document.</p>
 
-<h2 id="features">Menu Features</h2>
-<p>Here are some other features that can be applied to most menu items.</p>
+<h2 id="features">Other Menu Features</h2>
+
+<p>Here are some other features that you can apply to most menu items.</p>
 
 <h3 id="groups">Menu groups</h3>
-<p>When adding new items to a menu, you can optionally include each item in a group.
-A menu group is a collection of menu items that can share certain traits, like
-whether they are visible, enabled, or checkable.</p>
 
-<p>A group is defined by an integer (or a resource id, in XML). A menu item is added to the group when it is
-added to the menu, using one of the <code>add()</code> methods that accepts a <var>groupId</var>
-as an argument, such as <code>{@link android.view.Menu#add(int,int,int,int)}</code>.</p>
+<p>A menu group is a collection of menu items that share certain traits. With a group, you
+can:</p>
+<ul>
+  <li>Show or hide all items with {@link android.view.Menu#setGroupVisible(int,boolean)
+setGroupVisible()}</li>
+  <li>Enable or disable all items with {@link android.view.Menu#setGroupEnabled(int,boolean)
+setGroupEnabled()}</li>
+  <li>Specify whether all items are checkable with {@link
+android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()}</li>
+</ul>
 
-<p>You can show or hide the entire group with 
-<code>{@link android.view.Menu#setGroupVisible(int,boolean) setGroupVisible()}</code>; 
-enable or disable the group with 
-<code>{@link android.view.Menu#setGroupEnabled(int,boolean) setGroupEnabled()}</code>;
-and set whether the items can be checkable with
-<code>{@link android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()}</code>.
-</p>
+<p>You can create a group by nesting {@code &lt;item&gt;} elements inside a {@code &lt;group&gt;}
+element in your menu resource or by specifying a group ID with the the {@link
+android.view.Menu#add(int,int,int,int) add()} method.</p>
+
+<p>Here's an example menu resource that includes a group:</p>
+
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+    &lt;item android:id="@+id/item1"
+          android:icon="@drawable/item1"
+          android:title="@string/item1" /&gt;
+    &lt;!-- menu group --&gt;
+    &lt;group android:id="@+id/group1"&gt;
+        &lt;item android:id="@+id/groupItem1"
+              android:title="@string/groupItem1" /&gt;
+        &lt;item android:id="@+id/groupItem2"
+              android:title="@string/groupItem2" /&gt;
+    &lt;/group&gt;
+&lt;/menu&gt;
+</pre>
+
+<p>The items that are in the group appear the same as the first item that is not in a
+group&mdash;all three items in the menu are siblings. However, you can modify the traits of the two
+items in the group by referencing the group ID and using the methods listed above.</p>
+
 
 <h3 id="checkable">Checkable menu items</h3>
-<img align="right" src="{@docRoot}images/radio_buttons.png" alt="" />
-<p>Any menu item can be used as an interface for turning options on and off. This can
-be indicated with a checkbox for stand-alone options, or radio buttons for groups of
-mutually exclusive options (see the screenshot, to the right).</p>
 
-<p class="note"><strong>Note:</strong> Menu items in the Icon Menu cannot
+<div class="figure" style="width:200px">
+  <img src="{@docRoot}images/radio_buttons.png" height="300" alt="" />
+  <p class="img-caption"><strong>Figure 2.</strong> Screenshot of checkable menu items</p>
+</div>
+
+<p>A menu can be useful as an interface for turning options on and off, using a checkbox for
+stand-alone options, or radio buttons for groups of
+mutually exclusive options. Figure 2 shows a submenu with items that are checkable with radio
+buttons.</p>
+
+<p class="note"><strong>Note:</strong> Menu items in the Icon Menu (from the Options Menu) cannot
 display a checkbox or radio button. If you choose to make items in the Icon Menu checkable,
-then you must personally indicate the state by swapping the icon and/or text 
-each time the state changes between on and off.</p>
+you must manually indicate the checked state by swapping the icon and/or text
+each time the state changes.</p>
 
-<p>To make a single item checkable, use the <code>{@link android.view.MenuItem#setCheckable(boolean)
-setCheckable()}</code> method, like so:</p>
+<p>You can define the checkable behavior for individual menu items using the {@code
+android:checkable} attribute in the {@code &lt;item&gt;} element, or for an entire group with
+the {@code android:checkableBehavior} attribute in the {@code &lt;group&gt;} element. For
+example, all items in this menu group are checkable with a radio button:</p>
+
 <pre>
-menu.add(0, VIBRATE_SETTING_ID, 0, "Vibrate")
-    .setCheckable(true);
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+    &lt;group android:checkableBehavior="single"&gt;
+        &lt;item android:id="@+id/red"
+              android:title="@string/red" /&gt;
+        &lt;item android:id="@+id/blue"
+              android:title="@string/blue" /&gt;
+    &lt;/group&gt;
+&lt;/menu&gt;
 </pre>
-<p>This will display a checkbox with the menu item (unless it's in the Icon Menu). When the item
-is selected, the <code>onOptionsItemSelected()</code> callback is called as usual. It is here that
-you must set the state of the checkbox. You can query the current state of the item with
-<code>{@link android.view.MenuItem#isChecked()}</code> and set the checked state with
-<code>{@link android.view.MenuItem#setChecked(boolean) setChecked()}</code>. 
-Here's what this looks like inside the 
-<code>onOptionsItemSelected()</code> callback:</p>
+
+<p>The {@code android:checkableBehavior} attribute accepts either:
+<dl>
+  <dt>{@code single}</dt>
+    <dd>Only one item from the group can be checked (radio buttons)</dd>
+  <dt>{@code all}</dt>
+    <dd>All items can be checked (checkboxes)</dd>
+  <dt>{@code none}</dt>
+    <dd>No items are checkable</dd>
+</dl>
+
+<p>You can apply a default checked state to an item using the {@code android:checked} attribute in
+the {@code &lt;item&gt;} element and change it in code with the {@link
+android.view.MenuItem#setChecked(boolean) setChecked()} method.</p>
+
+<p>When a checkable item is selected, the system calls your respective item-selected callback method
+(such as {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}). It
+is here that you must set the state of the checkbox, because a checkbox or radio button does not
+change its state automatically. You can query the current state of the item (as it was before the
+user selected it) with {@link android.view.MenuItem#isChecked()} and then set the checked state with
+{@link android.view.MenuItem#setChecked(boolean) setChecked()}. For example:</p>
+
 <pre>
-switch (item.getItemId()) {
-case VIBRATE_SETTING_ID:
-  if (item.isChecked()) item.setChecked(false);
-  else item.setChecked(true);
-  return true;
-...
+&#64;Override
+public boolean onOptionsItemSelected(MenuItem item) {
+  switch (item.getItemId()) {
+  case R.id.vibrate:
+  case R.id.dont_vibrate:
+    if (item.isChecked()) item.setChecked(false);
+    else item.setChecked(true);
+    return true;
+  default:
+    return super.onOptionsItemSelected(item);
+  }
 }
 </pre>
 
-<p>To make a group of mutually exclusive radio button items, simply 
-assign the same group ID to each menu item 
-and call <code>{@link android.view.Menu#setGroupCheckable(int,boolean,boolean) 
-setGroupCheckable()}</code>. In this case, you don't need to call <code>setCheckable()</code>
-on each menu items, because the group as a whole is set checkable. Here's an example of
-two mutually exclusive options in a Submenu:</p>
-<pre>
-SubMenu subMenu = menu.addSubMenu("Color");
-subMenu.add(COLOR_MENU_GROUP, COLOR_RED_ID, 0, "Red");
-subMenu.add(COLOR_MENU_GROUP, COLOR_BLUE_ID, 0, "Blue");
-subMenu.setGroupCheckable(COLOR_MENU_GROUP, true, true);
-</pre>
-<p>In the <code>setGroupCheckable()</code> method, the first argument is the group ID
-that we want to set checkable. The second argument is whether we want the group items
-to be checkable. The last one is whether we want each item to be exclusively checkable 
-(if we set this <em>false</em>, then all the items will be checkboxes instead of radio buttons).
-When the group is set to be exclusive (radio buttons), each time a new item is selected, 
-all other are automatically de-selected.</p>
-<p>
+<p>If you don't set the checked state this way, then the visible state of the item (the checkbox or
+radio button) will not
+change when the user selects it. When you do set the state, the Activity preserves the checked state
+of the item so that when the user opens the menu later, the checked state that you
+set is visible.</p>
 
 <p class="note"><strong>Note:</strong>
-Checkable menu items are intended to be used only on a per-session basis and not saved to the device 
-(e.g., the <em>Map mode</em> setting in the Maps application is not saved &mdash; screenshot above).
-If there are application settings that you would like to save for the user,
-then you should store the data using <a href="#{@docRoot}guide/topics/data/data-storage.html#pref">Preferences</a>,
-and manage them with a {@link android.preference.PreferenceActivity}.</p>
+Checkable menu items are intended to be used only on a per-session basis and not saved after the
+application is destroyed. If you have application settings that you would like to save for the user,
+you should store the data using <a
+href="#{@docRoot}guide/topics/data/data-storage.html#pref">Shared Preferences</a>.</p>
 
 
 <h3 id="shortcuts">Shortcut keys</h3>
-<p>Quick access shortcut keys using letters and/or numbers can be added to menu items with
-<code>setAlphabeticShortcut(char)</code> (to set char shortcut), <code>setNumericShortcut(int)</code>
-(to set numeric shortcut),
-or <code>setShortcut(char,int)</code> (to set both)</code>. Case is <em>not</em> sensitive.
 
-For example:</p>
-<pre>
-menu.add(0, MENU_QUIT, 0, "Quit")
-    .setAlphabeticShortcut('q');
-</pre>
-<p>Now, when the menu is open (or while holding the MENU key), pressing the "q" key will 
-select this item.</p>
-<p>This shortcut key will be displayed as a tip in the menu item, below the menu item name
-(except for items in the Icon Menu).</p>
-<p class="note"><strong>Note:</strong> Shortcuts cannot be added to items in a Context Menu.</p>
+<p>You can add quick-access shortcut keys using letters and/or numbers to menu items with the
+{@code android:alphabeticShortcut} and {@code android:numericShortcut} attributes in the {@code
+&lt;item&gt;} element. You can also use the methods {@link
+android.view.MenuItem#setAlphabeticShortcut(char)} and {@link
+android.view.MenuItem#setNumericShortcut(char)}. Shortcut keys are <em>not</em>
+case sensitive.</p>
+
+<p>For example, if you apply the "s" character as an alphabetic shortcut to a "save" menu item, then
+when the menu is open (or while the user holds the MENU key) and the user presses the "s" key,
+the "save" menu item is selected.</p>
+
+<p>This shortcut key is displayed as a tip in the menu item, below the menu item name
+(except for items in the Icon Menu, which are displayed only if the user holds the MENU
+key).</p>
+
+<p class="note"><strong>Note:</strong> Shortcut keys for menu items only work on devices with a
+hardware keyboard. Shortcuts cannot be added to items in a Context Menu.</p>
 
 
-<h3 id="intents">Menu item intents</h3>
-<p>If you've read the <a href="{@docRoot}guide/topics/fundamentals.html">Application
-Fundamentals</a>, then you're at least a little familiar
-with Android Intents. These allow applications to bind with each other, share information,
-and perform user tasks cooperatively. Just like your application might fire an Intent to launch a web browser,
-an email client, or another Activity in your application,
-you can perform such actions from within a menu.
-There are two ways to do this: define an Intent and assign it to a single menu item, or
-define an Intent and allow Android to search the device for activities and dynamically add a 
-menu item for each one that meets the Intent criteria.</p>
+<h3 id="intents">Intents for menu items</h3>
 
-<p>For more information on creating Intents and providing your application's services to other applications, 
-read the <a href="/guide/topics/intents/intents-filters.html">Intents 
-and Intent Filters</a> document.</p>
+<p>Sometimes you'll want a menu item to launch an Activity using an Intent (whether it's an
+Actvitity in your application or another application). When you know the Intent you want to use and
+have a specific menu item that should initiate the Intent, you can execute the Intent with {@link
+android.app.Activity#startActivity(Intent) startActivity()} during the appropriate on-item-selected
+callback method (such as the {@link android.app.Activity#onOptionsItemSelected(MenuItem)
+onOptionsItemSelected()} callback).</p>
 
-<h4>Set an intent for a single menu item</h4>
-<p>If you want to offer a specific menu item that launches a new Activity, then you 
-can specifically define an Intent for the menu item with the
-<code>{@link android.view.MenuItem#setIntent(Intent)
-setIntent()}</code> method.</p>
+<p>However, if you are not certain that the user's device
+contains an application that handles the Intent, then adding a menu item that executes the
+Intent can result in a non-functioning menu item, because the Intent might not resolve to an
+Activity that accepts it. To solve this, Android lets you dynamically add menu items to your menu
+when Android finds activities on the device that handle your Intent.</p>
 
-<p>For example, inside the <code>{@link android.app.Activity#onCreateOptionsMenu(Menu)
-onCreateOptionsMenu()}</code> method, you can define a new menu item with an Intent like this:</p>
-<pre>
-MenuItem menuItem = menu.add(0, PHOTO_PICKER_ID, 0, "Select Photo");
-menuItem.setIntent(new Intent(this, PhotoPicker.class));
-</pre>
-<p>Android will automatically launch the Activity when the item is selected.</p>
+<p>If you're not familiar with creating Intents, read the <a
+href="/guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>.</p>
 
-<p class="note"><strong>Note:</strong> This will not return a result to your Activity.
-If you wish to be returned a result, then do not use <code>setIntent()</code>.
-Instead, handle the selection as usual in the <code>onOptionsMenuItemSelected()</code>
-or <code>onContextMenuItemSelected()</code> callback and call
-<code>{@link android.app.Activity#startActivityForResult(Intent,int) startActivityForResult()}</code>.
-</p>
 
-<h4>Dynamically add intents</h4>
+<h4>Dynamically adding Intents</h4>
 
-<p>If there are potentially multiple activities that are relevant to your current
-Activity or selected item, then the application can dynamically add menu items that execute other
-services.</p>
-<p>During menu creation, define an Intent with the category <var>Intent.ALTERNATIVE_CATEGORY</var> and/or
-<var>Intent.SELECTED_ALTERNATIVE</var>, the MIME type currently selected (if any), and any other
-requirements, the same way as you would satisfy an intent filter to open a new
-Activity. Then call  
-<code>{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) 
-addIntentOptions()}</code> to have Android search for any services meeting those requirements
-and add them to the menu for you. If there are no applications installed
-that satisfy the Intent, then no additional menu items are added.</p>
+<p>When you don't know if the user's device has an application that handles a specific Intent,
+you can define the Intent and let Android search the device for activities that accept the Intent.
+When it finds activies that handle the Intent, it adds a menu item for
+each one to your menu and attaches the appropriate Intent to open the Activity when the user
+selects it.</p>
+
+<p>To add menu items based on available activities that accept an Intent:</p>
+<ol>
+  <li>Define an
+Intent with the category {@link android.content.Intent#CATEGORY_ALTERNATIVE} and/or
+{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE}, plus any other requirements.</li>
+  <li>Call {@link
+android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[])
+Menu.addIntentOptions()}. Android then searches for any applications that can perform the Intent
+and adds them to your menu.</li>
+</ol>
+
+<p>If there are no applications installed
+that satisfy the Intent, then no menu items are added.</p>
 
 <p class="note"><strong>Note:</strong>
-<var>SELECTED_ALTERNATIVE</var> is used to handle the currently selected element on the 
-screen. So, it should only be used when creating a Menu in <code>onCreateContextMenu()</code> or
-<code>onPrepareOptionsMenu()</code>, which is called every time the Options Menu is opened.</p>
+{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} is used to handle the currently
+selected element on the screen. So, it should only be used when creating a Menu in {@link
+android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo)
+onCreateContextMenu()}.</p>
 
-<p>Here's an example demonstrating how an application would search for
-additional services to display on its menu.</p>
+<p>For example:</p>
 
 <pre>
+&#64;Override
 public boolean onCreateOptionsMenu(Menu menu){
     super.onCreateOptionsMenu(menu);
 
     // Create an Intent that describes the requirements to fulfill, to be included
-    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE. 
-    Intent intent = new Intent(null, getIntent().getData());
+    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
+    Intent intent = new Intent(null, dataUri);
     intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
-        
-    // Search for, and populate the menu with, acceptable offering applications.
+
+    // Search and populate the menu with acceptable offering applications.
     menu.addIntentOptions(
-         thisClass.INTENT_OPTIONS,  // Menu group 
+         R.id.intent_group,  // Menu group to which new items will be added
          0,      // Unique item ID (none)
          0,      // Order for the items (none)
          this.getComponentName(),   // The current Activity name
@@ -495,17 +612,27 @@
     return true;
 }</pre>
 
-<p>For each Activity found that provides an Intent Filter matching the Intent defined, a menu
-item will be added, using the <var>android:label</var> value of the intent filter as the text
-for the menu item. 
-The <code>{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) addIntentOptions()}</code> method will also return the number of menu items added.</p>
-<p>Also be aware that, when <code>addIntentOptions()</code> is called, it will override any and all
-menu items in the menu group specified in the first argument.</p>
+<p>For each Activity found that provides an Intent filter matching the Intent defined, a menu
+item is added, using the value in the Intent filter's <code>android:label</code> as the
+menu item title and the application icon as the menu item icon. The
+{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[])
+addIntentOptions()} method returns the number of menu items added.</p>
 
-<p>If you wish to offer the services of your Activity to other application menus, then you 
-only need to define an intent filter as usual. Just be sure to include the <var>ALTERNATIVE</var> and/or
-<var>SELECTED_ALTERNATIVE</var> values in the <var>name</var> attribute of 
-a <code>&lt;category></code> element in the intent filter. For example:</p>
+<p class="note"><strong>Note:</strong> When you call {@link
+android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[])
+addIntentOptions()}, it overrides any and all menu items by the menu group specified in the first
+argument.</p>
+
+
+<h4>Allowing your Activity to be added to menus</h4>
+
+<p>You can also offer the services of your Activity to other applications, so your
+application can be included in the menu of others (reverse the roles described above).</p>
+
+<p>To be included in other application menus, you need to define an Intent
+filter as usual, but be sure to include the {@link android.content.Intent#CATEGORY_ALTERNATIVE}
+and/or {@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} values for the Intent filter
+category. For example:</p>
 <pre>
 &lt;intent-filter label="Resize Image">
     ...
@@ -514,9 +641,10 @@
     ...
 &lt;/intent-filter>
 </pre>
-<p>read more about writing intent filters in the 
+
+<p>Read more about writing Intent filters in the
 <a href="/guide/topics/intents/intents-filters.html">Intents and Intent Filters</a> document.</p>
 
 <p>For a sample application using this technique, see the 
-<a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a>
-sample code.</p>
+<a href="{@docRoot}resources/samples/NotePad/src/com/example/android/notepad/NoteEditor.html">Note
+Pad</a> sample code.</p>
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 5e75105..6cb7228 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -43,29 +43,73 @@
 the hands of users. For information about how to target your application to devices based on
 platform version, see <a href="{@docRoot}guide/appendix/api-levels.html">API Levels</a>.</p>
 
-<p class="note"><strong>Note:</strong> This data is based on the number
-of Android devices that have accessed Android Market within a 14-day period
-ending on the data collection date noted below.</p>
+
+<h3 id="Current">Current Distribution</h3>
+
+<p>The following pie chart and table is based on the number of Android devices that have accessed
+Android Market within a 14-day period ending on the data collection date noted below.</p>
 
 <div class="dashboard-panel">
 
-<img alt="" width="460" height="250"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.1,24.6,25.0,0.1,0.3,50.0&chl=
-Android%201.1|Android%201.5|Android%201.6|Android%202.0|Android%202.0.1|Android%202.1&chco=c4df9b,
-6fad0c" />
+<img alt="" height="250" width="460"
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.3,21.3,23.5,53.1,1.8&chl=Other*|
+Android%201.5|Android%201.6|Android%202.1|Android%202.2&chco=c4df9b,6fad0c" />
 
 <table>
 <tr>
-  <th>Android Platform</th>
-  <th>Percent of Devices</th>
+  <th>Platform</th>
+  <th>API Level</th>
+  <th>Distribution</th>
 </tr>
-<tr><td>Android 1.1</td><td>0.1%</td></tr> 
-<tr><td>Android 1.5</td><td>24.6%</td></tr> 
-<tr><td>Android 1.6</td><td>25.0%</td></tr> 
-<tr><td>Android 2.0</td><td>0.1%</td></tr> 
-<tr><td>Android 2.0.1</td><td>0.3%</td></tr> 
-<tr><td>Android 2.1</td><td>50.0%</td></tr> 
+<tr><td>Android 1.5</td><td>3</td><td>21.3%</td></tr>
+<tr><td>Android 1.6</td><td>4</td><td>23.5%</td></tr>
+<tr><td>Android 2.1</td><td>7</td><td>53.1%</td></tr>
+<tr><td>Android 2.2</td><td>8</td><td>1.8%</td></tr>
 </table>
-<p><em>Data collected during two weeks ending on June 16, 2010</em></p>
-</div>
+
+<p><em>Data collected during two weeks ending on July 1, 2010</em></p>
+<p style="font-size:.9em">* <em>Other: 0.3% of devices running obsolete versions</em></p>
+
+</div><!-- end dashboard-panel -->
+
+
+<h3 id="Historical">Historical Distribution</h3>
+
+<p>The following stacked line graph provides a history of the relative number of
+active Android devices running different versions of the Android platform. It also provides a
+valuable perspective of how many devices your application is compatible with, based on the
+platform version.</p>
+
+<p>Notice that the platform versions are stacked on top of each other with the oldest active
+version at the top. This format indicates the total percent of active devices that are compatible
+with a given version of Android. For example, if you develop your application for
+the version that is at the very top of the chart, then your application is
+compatible with 100% of active devices (and all future versions), because all Android APIs are
+forward compatible. Or, if you develop your application for a version lower on the chart,
+then it is currently compatible with the percentage of devices indicated on the y-axis, where the
+line for that version meets the y-axis on the right.</p>
+
+<p>Each dataset in the timeline is based on the number of Android devices that accessed
+Android Market within a 14-day period ending on the date indicated on the x-axis.</p>
+
+<div class="dashboard-panel">
+
+<img alt="" height="265" width="700" style="padding:5px;background:#fff"
+src="http://chart.apis.google.com/chart?&cht=lc&chs=700x265&chxt=x,y,r&chxr=0,0,10%7C1,0,100%7C2,0,
+100&chxl=0%3A%7C2010/02/01%7C02/15%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%
+7C2010/07/01%7C1%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%
+7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10&chxtc=0,5&chd=t:99.0,99.2,99.4,99.5,99.6,99.6,99.6,99.7,100.6
+,101.1,99.9%7C63.4,62.5,61.6,60.6,61.5,61.7,62.3,63.5,73.0,76.4,78.6%7C22.6,23.2,24.3,25.4,29.4,30.2
+,32.7,35.3,46.2,51.3,55.1%7C0.0,0.0,0.0,0.0,4.0,28.3,32.0,34.9,45.9,51.0,54.9%7C0.0,0.0,0.0,0.0,0.0,
+0.0,0.0,0.0,0.8,1.2,1.8&chm=tAndroid%201.5,7caa36,0,0,15,,t::-5%7Cb,c3df9b,0,1,0%7CtAndroid%201.6,
+638d23,1,0,15,,t::-5%7Cb,b0db6e,1,2,0%7CtAndroid%202.0.1,496c13,2,0,15,,t::-5%7Cb,9ddb3d,2,3,0%
+7CtAndroid%202.1,2f4708,3,5,15,,t::-5%7Cb,89cf19,3,4,0%7CB,6fad0c,4,5,0&chg=9,25&chdl=Android%201.5%
+20(API%20Level%203)%7CAndroid%201.6%20(API%20Level%204)%7CAndroid%202.0.1%20(API%20Level%206)%
+7CAndroid%202.1%20(API%20Level%207)%7CAndroid%202.2%20(API%20Level %208)&chco=add274,
+9ad145,84c323,6ba213,507d08" />
+
+<p><em>Last historical dataset collected during two weeks ending on July 1, 2010</em></p>
+
+
+</div><!-- end dashboard-panel -->
 
diff --git a/docs/html/resources/dashboard/screens.jd b/docs/html/resources/dashboard/screens.jd
index f8130ea..89fdd2d 100644
--- a/docs/html/resources/dashboard/screens.jd
+++ b/docs/html/resources/dashboard/screens.jd
@@ -49,7 +49,7 @@
 <div class="dashboard-panel">
 
 <img alt="" width="460" height="250"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.1,57.8,41.0&chl=Small%20/%20ldpi|
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.4,54.5,44.1&chl=Small%20/%20ldpi|
 Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" />
 
 <table>
@@ -60,14 +60,14 @@
 <th scope="col">High Density</th>
 </tr>
 <tr><th scope="row">Small</th> 
-<td class='cent hi'>1.1%</td> 
+<td class='cent hi'>1.4%</td> 
 <td></td> 
 <td></td> 
 </tr> 
 <tr><th scope="row">Normal</th> 
 <td></td> 
-<td class='cent hi'>57.8%</td> 
-<td class='cent hi'>41.0%</td> 
+<td class='cent hi'>54.5%</td> 
+<td class='cent hi'>44.1%</td> 
 </tr> 
 <tr><th scope="row">Large</th> 
 <td></td> 
@@ -76,6 +76,6 @@
 </tr> 
 </table>
 
-<p><em>Data collected during two weeks ending on June 16, 2010</em></p>
+<p><em>Data collected during two weeks ending on July 1, 2010</em></p>
 </div>
 
diff --git a/docs/html/sdk/older_releases.jd b/docs/html/sdk/older_releases.jd
index c3ba495..77f7e43 100644
--- a/docs/html/sdk/older_releases.jd
+++ b/docs/html/sdk/older_releases.jd
@@ -47,7 +47,7 @@
     <td>Windows</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.6_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.6_r1.zip">android-sdk-
 windows-1 .6_r1.zip</a>
     </td>
     <td>260529085 bytes</td>
@@ -57,7 +57,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.6_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.6_r1.zip">android-sdk-
 mac_x86-1 .6_r1.zip</a>
     </td>
     <td>247412515 bytes</td>
@@ -67,7 +67,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.6_r1.tgz">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.6_r1.tgz">android-
 sdk- linux_x86-1.6_r1.tgz</a>
     </td>
     <td>238224860 bytes</td>
@@ -92,7 +92,7 @@
     <td>Windows</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.5_r3.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r3.zip">android-sdk-
 windows-1 .5_r3.zip</a>
     </td>
     <td>191477853 bytes</td>
@@ -102,7 +102,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r3.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r3.zip">android-sdk-
 mac_x86-1 .5_r3.zip</a>
     </td>
     <td>183024673 bytes</td>
@@ -112,7 +112,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r3.zip">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r3.zip">android-
 sdk- linux_x86-1.5_r3.zip</a>
     </td>
     <td>178117561 bytes</td>
@@ -137,7 +137,7 @@
     <td>Windows</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.1_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.1_r1.zip">android-sdk-
 windows-1
 .1_r1.zip</a>
     </td>
@@ -148,7 +148,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.1_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.1_r1.zip">android-sdk-
 mac_x86-1
 .1_r1.zip</a>
     </td>
@@ -159,7 +159,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.1_r1.zip">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.1_r1.zip">android-
 sdk-
 linux_x86-1.1_r1.zip</a>
     </td>
@@ -185,7 +185,7 @@
     <td>Windows</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.0_r2.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.0_r2.zip">android-sdk-
 windows-1
 .0_r2.zip</a>
     </td>
@@ -196,7 +196,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.0_r2.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.0_r2.zip">android-sdk-
 mac_x86-1
 .0_r2.zip</a>
     </td>
@@ -207,7 +207,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.0_r2.zip">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.0_r2.zip">android-
 sdk-
 linux_x86-1.0_r2.zip</a>
     </td>
@@ -241,7 +241,7 @@
     <td>Windows</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.5_r2.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r2.zip">android-sdk-
 windows-1 .5_r2.zip</a>
     </td>
     <td>178346828 bytes</td>
@@ -251,7 +251,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r2.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r2.zip">android-sdk-
 mac_x86-1 .5_r2.zip</a>
     </td>
     <td>169945128 bytes</td>
@@ -261,7 +261,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r2.zip">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r2.zip">android-
 sdk- linux_x86-1.5_r2.zip</a>
     </td>
     <td>165035130 bytes</td>
@@ -286,7 +286,7 @@
     <td>Windows</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.5_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r1.zip">android-sdk-
 windows-1 .5_r1.zip</a>
     </td>
     <td>176263368 bytes</td>
@@ -296,7 +296,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r1.zip">android-sdk-
 mac_x86-1 .5_r1.zip</a>
     </td>
     <td>167848675 bytes</td>
@@ -306,7 +306,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r1.zip">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r1.zip">android-
 sdk- linux_x86-1.5_r1.zip</a>
     </td>
     <td>162938845 bytes</td>
@@ -331,7 +331,7 @@
     <td>Windows</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.0_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.0_r1.zip">android-sdk-
 windows-1 .0_r1.zip</a>
     </td>
     <td>89.7 MB bytes</td>
@@ -341,7 +341,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.0_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.0_r1.zip">android-sdk-
 mac_x86-1 .0_r1.zip</a>
     </td>
     <td>87.5 MB bytes</td>
@@ -351,7 +351,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.0_r1.zip">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.0_r1.zip">android-
 sdk- linux_x86-1.0_r1.zip</a>
     </td>
     <td>87.8 MB bytes</td>
diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h
index 24ac82b..cfc17a5 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -24,7 +24,7 @@
 
 class ISurface;
 class ICamera;
-class IMediaPlayerClient;
+class IMediaRecorderClient;
 
 class IMediaRecorder: public IInterface
 {
@@ -43,7 +43,7 @@
     virtual	status_t		setVideoSize(int width, int height) = 0;
     virtual	status_t		setVideoFrameRate(int frames_per_second) = 0;
     virtual     status_t                setParameters(const String8& params) = 0;
-    virtual     status_t                setListener(const sp<IMediaPlayerClient>& listener) = 0;
+    virtual     status_t                setListener(const sp<IMediaRecorderClient>& listener) = 0;
     virtual	status_t		prepare() = 0;
     virtual	status_t		getMaxAmplitude(int* max) = 0;
     virtual	status_t		start() = 0;
diff --git a/include/media/IMediaRecorderClient.h b/include/media/IMediaRecorderClient.h
new file mode 100644
index 0000000..0058ef2
--- /dev/null
+++ b/include/media/IMediaRecorderClient.h
@@ -0,0 +1,48 @@
+/*
+ * 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_IMEDIARECORDERCLIENT_H
+#define ANDROID_IMEDIARECORDERCLIENT_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IMediaRecorderClient: public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(MediaRecorderClient);
+
+    virtual void notify(int msg, int ext1, int ext2) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnMediaRecorderClient: public BnInterface<IMediaRecorderClient>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IMEDIARECORDERCLIENT_H
+
diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h
index a4eea2a..c3cd361 100644
--- a/include/media/MediaProfiles.h
+++ b/include/media/MediaProfiles.h
@@ -48,8 +48,8 @@
     static MediaProfiles* getInstance();
 
     /**
-     * Returns the value for the given param name at the given quality level,
-     * or -1 if error.
+     * Returns the value for the given param name for the given camera at
+     * the given quality level, or -1 if error.
      *
      * Supported param name are:
      * duration - the recording duration.
@@ -64,7 +64,8 @@
      * aud.hz - audio sample rate
      * aud.ch - number of audio channels
      */
-    int getCamcorderProfileParamByName(const char *name, camcorder_quality quality) const;
+    int getCamcorderProfileParamByName(const char *name, int cameraId,
+                                       camcorder_quality quality) const;
 
     /**
      * Returns the output file formats supported.
@@ -124,12 +125,7 @@
     /**
      * Returns the number of image encoding quality levels supported.
      */
-    Vector<int> getImageEncodingQualityLevels() const;
-
-    /**
-     * Returns the maximum amount of memory in bytes we can use for decoding a JPEG file.
-     */
-    int getImageDecodingMaxMemory() const;
+    Vector<int> getImageEncodingQualityLevels(int cameraId) const;
 
 private:
     MediaProfiles& operator=(const MediaProfiles&);  // Don't call me
@@ -171,7 +167,8 @@
 
     struct CamcorderProfile {
         CamcorderProfile()
-            : mFileFormat(OUTPUT_FORMAT_THREE_GPP),
+            : mCameraId(0),
+              mFileFormat(OUTPUT_FORMAT_THREE_GPP),
               mQuality(CAMCORDER_QUALITY_HIGH),
               mDuration(0),
               mVideoCodec(0),
@@ -182,6 +179,7 @@
             delete mAudioCodec;
         }
 
+        int mCameraId;
         output_format mFileFormat;
         camcorder_quality mQuality;
         int mDuration;
@@ -249,6 +247,11 @@
         int tag;
     };
 
+    struct ImageEncodingQualityLevels {
+        int mCameraId;
+        Vector<int> mLevels;
+    };
+
     // Debug
     static void logVideoCodec(const VideoCodec& codec);
     static void logAudioCodec(const AudioCodec& codec);
@@ -267,9 +270,11 @@
     static VideoDecoderCap* createVideoDecoderCap(const char **atts);
     static VideoEncoderCap* createVideoEncoderCap(const char **atts);
     static AudioEncoderCap* createAudioEncoderCap(const char **atts);
-    static CamcorderProfile* createCamcorderProfile(const char **atts);
-    static int getImageEncodingQualityLevel(const char **atts);
-    static int getImageDecodingMaxMemory(const char **atts);
+    static CamcorderProfile* createCamcorderProfile(int cameraId, const char **atts);
+    static int getCameraId(const char **atts);
+
+    ImageEncodingQualityLevels* findImageEncodingQualityLevels(int cameraId) const;
+    void addImageEncodingQualityLevel(int cameraId, const char** atts);
 
     // Customized element tag handler for parsing the xml configuration file.
     static void startElementHandler(void *userData, const char *name, const char **atts);
@@ -303,6 +308,7 @@
     static bool sIsInitialized;
     static MediaProfiles *sInstance;
     static Mutex sLock;
+    int mCurrentCameraId;
 
     Vector<CamcorderProfile*> mCamcorderProfiles;
     Vector<AudioEncoderCap*>  mAudioEncoders;
@@ -310,8 +316,7 @@
     Vector<AudioDecoderCap*>  mAudioDecoders;
     Vector<VideoDecoderCap*>  mVideoDecoders;
     Vector<output_format>     mEncoderOutputFileFormats;
-    Vector<int>               mImageEncodingQualityLevels;
-    int                       mImageDecodingMaxMemory;
+    Vector<ImageEncodingQualityLevels *>  mImageEncodingQualityLevels;
 };
 
 }; // namespace android
diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index 5b787a7..497965c 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -41,7 +41,7 @@
     virtual status_t setOutputFile(const char *path) = 0;
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0;
     virtual status_t setParameters(const String8& params) = 0;
-    virtual status_t setListener(const sp<IMediaPlayerClient>& listener) = 0;
+    virtual status_t setListener(const sp<IMediaRecorderClient>& listener) = 0;
     virtual status_t prepare() = 0;
     virtual status_t start() = 0;
     virtual status_t stop() = 0;
diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h
index 4f17c1a..c04105e 100644
--- a/include/media/PVMediaRecorder.h
+++ b/include/media/PVMediaRecorder.h
@@ -18,7 +18,7 @@
 #ifndef ANDROID_PVMEDIARECORDER_H
 #define ANDROID_PVMEDIARECORDER_H
 
-#include <media/IMediaPlayerClient.h>
+#include <media/IMediaRecorderClient.h>
 #include <media/MediaRecorderBase.h>
 
 namespace android {
@@ -45,7 +45,7 @@
     virtual status_t setOutputFile(const char *path);
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
     virtual status_t setParameters(const String8& params);
-    virtual status_t setListener(const sp<IMediaPlayerClient>& listener);
+    virtual status_t setListener(const sp<IMediaRecorderClient>& listener);
     virtual status_t prepare();
     virtual status_t start();
     virtual status_t stop();
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 4f4ec43..b21bc4d 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -22,7 +22,7 @@
 #include <utils/threads.h>
 #include <utils/List.h>
 #include <utils/Errors.h>
-#include <media/IMediaPlayerClient.h>
+#include <media/IMediaRecorderClient.h>
 #include <media/IMediaDeathNotifier.h>
 
 namespace android {
@@ -149,7 +149,7 @@
     virtual void notify(int msg, int ext1, int ext2) = 0;
 };
 
-class MediaRecorder : public BnMediaPlayerClient,
+class MediaRecorder : public BnMediaRecorderClient,
                       public virtual IMediaDeathNotifier
 {
 public:
diff --git a/include/media/stagefright/ColorConverter.h b/include/media/stagefright/ColorConverter.h
index 1e341b9..bc3f464 100644
--- a/include/media/stagefright/ColorConverter.h
+++ b/include/media/stagefright/ColorConverter.h
@@ -58,6 +58,11 @@
             const void *srcBits, size_t srcSkip,
             void *dstBits, size_t dstSkip);
 
+    void convertYUV420SemiPlanar(
+            size_t width, size_t height,
+            const void *srcBits, size_t srcSkip,
+            void *dstBits, size_t dstSkip);
+
     ColorConverter(const ColorConverter &);
     ColorConverter &operator=(const ColorConverter &);
 };
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index 46aaf7c..8d3a9df 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -19,7 +19,7 @@
 #define MEDIA_WRITER_H_
 
 #include <utils/RefBase.h>
-#include <media/IMediaPlayerClient.h>
+#include <media/IMediaRecorderClient.h>
 
 namespace android {
 
@@ -27,7 +27,10 @@
 struct MetaData;
 
 struct MediaWriter : public RefBase {
-    MediaWriter() {}
+    MediaWriter()
+        : mMaxFileSizeLimitBytes(0),
+          mMaxFileDurationLimitUs(0) {
+    }
 
     virtual status_t addSource(const sp<MediaSource> &source) = 0;
     virtual bool reachedEOS() = 0;
@@ -36,7 +39,7 @@
     virtual void pause() = 0;
     virtual void setMaxFileSize(int64_t bytes) { mMaxFileSizeLimitBytes = bytes; }
     virtual void setMaxFileDuration(int64_t durationUs) { mMaxFileDurationLimitUs = durationUs; }
-    virtual void setListener(const sp<IMediaPlayerClient>& listener) {
+    virtual void setListener(const sp<IMediaRecorderClient>& listener) {
         mListener = listener;
     }
 
@@ -44,7 +47,7 @@
     virtual ~MediaWriter() {}
     int64_t mMaxFileSizeLimitBytes;
     int64_t mMaxFileDurationLimitUs;
-    sp<IMediaPlayerClient> mListener;
+    sp<IMediaRecorderClient> mListener;
 
     void notify(int msg, int ext1, int ext2) {
         if (mListener != NULL) {
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index c95fc02..99ec8e6 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -172,6 +172,9 @@
     void setVideoInputFormat(
             const char *mime, const sp<MetaData>& meta);
 
+    status_t setupBitRate(int32_t bitRate);
+    status_t setupErrorCorrectionParameters();
+    status_t setupH263EncoderParameters(const sp<MetaData>& meta);
     status_t setupMPEG4EncoderParameters(const sp<MetaData>& meta);
     status_t setupAVCEncoderParameters(const sp<MetaData>& meta);
 
diff --git a/include/media/stagefright/foundation/ALooper.h b/include/media/stagefright/foundation/ALooper.h
index 69ad837..194f1fc 100644
--- a/include/media/stagefright/foundation/ALooper.h
+++ b/include/media/stagefright/foundation/ALooper.h
@@ -39,7 +39,10 @@
     handler_id registerHandler(const sp<AHandler> &handler);
     void unregisterHandler(handler_id handlerID);
 
-    status_t start(bool runOnCallingThread = false);
+    status_t start(
+            bool runOnCallingThread = false,
+            bool canCallJava = false);
+
     status_t stop();
 
     static int64_t GetNowUs();
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 139c620..c674cba 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -60,6 +60,8 @@
 
     sp<AMessage> dup() const;
 
+    AString debugString(int32_t indent = 0) const;
+
 protected:
     virtual ~AMessage();
 
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index f333911..4fd0681 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -131,7 +131,7 @@
 // ---------------------------------------------------------------------------
 
 class Surface 
-    : public EGLNativeBase<android_native_window_t, Surface, RefBase>
+    : public EGLNativeBase<ANativeWindow, Surface, RefBase>
 {
 public:
     struct SurfaceInfo {
@@ -195,14 +195,14 @@
 
 
     /*
-     *  android_native_window_t hooks
+     *  ANativeWindow hooks
      */
-    static int setSwapInterval(android_native_window_t* window, int interval);
-    static int dequeueBuffer(android_native_window_t* window, android_native_buffer_t** buffer);
-    static int lockBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
-    static int queueBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
-    static int query(android_native_window_t* window, int what, int* value);
-    static int perform(android_native_window_t* window, int operation, ...);
+    static int setSwapInterval(ANativeWindow* window, int interval);
+    static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer);
+    static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
+    static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
+    static int query(ANativeWindow* window, int what, int* value);
+    static int perform(ANativeWindow* window, int operation, ...);
 
     int dequeueBuffer(android_native_buffer_t** buffer);
     int lockBuffer(android_native_buffer_t* buffer);
diff --git a/include/ui/FramebufferNativeWindow.h b/include/ui/FramebufferNativeWindow.h
index 8ea3ab9..0f4594f 100644
--- a/include/ui/FramebufferNativeWindow.h
+++ b/include/ui/FramebufferNativeWindow.h
@@ -43,7 +43,7 @@
 
 class FramebufferNativeWindow 
     : public EGLNativeBase<
-        android_native_window_t, 
+        ANativeWindow, 
         FramebufferNativeWindow, 
         LightRefBase<FramebufferNativeWindow> >
 {
@@ -59,12 +59,12 @@
 private:
     friend class LightRefBase<FramebufferNativeWindow>;    
     ~FramebufferNativeWindow(); // this class cannot be overloaded
-    static int setSwapInterval(android_native_window_t* window, int interval);
-    static int dequeueBuffer(android_native_window_t* window, android_native_buffer_t** buffer);
-    static int lockBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
-    static int queueBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
-    static int query(android_native_window_t* window, int what, int* value);
-    static int perform(android_native_window_t* window, int operation, ...);
+    static int setSwapInterval(ANativeWindow* window, int interval);
+    static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer);
+    static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
+    static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
+    static int query(ANativeWindow* window, int what, int* value);
+    static int perform(ANativeWindow* window, int operation, ...);
     
     framebuffer_device_t* fbDev;
     alloc_device_t* grDev;
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 32f85b3..a2e0ba06 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -43,7 +43,7 @@
 /*
  * Declare a concrete type for the NDK's input event forward declaration.
  */
-struct input_event_t { };
+struct AInputEvent { };
 
 namespace android {
 
@@ -133,7 +133,7 @@
 /*
  * Input events.
  */
-class InputEvent : public input_event_t {
+class InputEvent : public AInputEvent {
 public:
     virtual ~InputEvent() { }
 
@@ -142,7 +142,7 @@
     inline int32_t getDeviceId() const { return mDeviceId; }
 
     inline int32_t getNature() const { return mNature; }
-
+    
 protected:
     void initialize(int32_t deviceId, int32_t nature);
 
@@ -176,6 +176,14 @@
 
     inline nsecs_t getEventTime() const { return mEventTime; }
 
+    // Return true if this event may have a default action implementation.
+    static bool hasDefaultAction(int32_t keyCode);
+    bool hasDefaultAction() const;
+
+    // Return true if this event represents a system key.
+    static bool isSystemKey(int32_t keyCode);
+    bool isSystemKey() const;
+    
     void initialize(
             int32_t deviceId,
             int32_t nature,
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 511ad20..eb8f820 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -126,21 +126,21 @@
     /* Gets the key repeat timeout or -1 if automatic key repeating is disabled. */
     virtual nsecs_t getKeyRepeatTimeout() = 0;
 
-    /* Gets the input targets for a key event.
+    /* Waits for key event input targets to become available.
      * If the event is being injected, injectorPid and injectorUid should specify the
      * process id and used id of the injecting application, otherwise they should both
      * be -1.
      * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
-    virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+    virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid,
             Vector<InputTarget>& outTargets) = 0;
 
-    /* Gets the input targets for a motion event.
+    /* Waits for motion event targets to become available.
      * If the event is being injected, injectorPid and injectorUid should specify the
      * process id and used id of the injecting application, otherwise they should both
      * be -1.
      * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
-    virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+    virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid,
             Vector<InputTarget>& outTargets) = 0;
 };
@@ -186,6 +186,16 @@
     virtual int32_t injectInputEvent(const InputEvent* event,
             int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
 
+    /* Preempts input dispatch in progress by making pending synchronous
+     * dispatches asynchronous instead.  This method is generally called during a focus
+     * transition from one application to the next so as to enable the new application
+     * to start receiving input as soon as possible without having to wait for the
+     * old application to finish up.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void preemptInputDispatch() = 0;
+
     /* Registers or unregister input channels that may be used as targets for input events.
      *
      * These methods may be called on any thread (usually by the input manager).
@@ -233,6 +243,8 @@
     virtual int32_t injectInputEvent(const InputEvent* event,
             int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
 
+    virtual void preemptInputDispatch();
+
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h
index 7509dd2..e755238 100644
--- a/include/ui/InputManager.h
+++ b/include/ui/InputManager.h
@@ -87,6 +87,14 @@
     virtual int32_t injectInputEvent(const InputEvent* event,
             int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
 
+    /* Preempts input dispatch in progress by making pending synchronous
+     * dispatches asynchronous instead.  This method is generally called during a focus
+     * transition from one application to the next so as to enable the new application
+     * to start receiving input as soon as possible without having to wait for the
+     * old application to finish up.
+     */
+    virtual void preemptInputDispatch() = 0;
+
     /* Gets input device configuration. */
     virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0;
 
@@ -130,6 +138,8 @@
     virtual int32_t injectInputEvent(const InputEvent* event,
             int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
 
+    virtual void preemptInputDispatch();
+
     virtual void getInputConfiguration(InputConfiguration* outConfiguration) const;
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
             int32_t scanCode) const;
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index d76b8fe..781da35 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -53,6 +53,8 @@
  */
 struct InputDevice {
     struct AbsoluteAxisInfo {
+        bool valid;        // set to true if axis parameters are known, false otherwise
+
         int32_t minValue;  // minimum value
         int32_t maxValue;  // maximum value
         int32_t range;     // range of values, equal to maxValue - minValue
@@ -272,9 +274,16 @@
         } jumpyTouchFilter;
 
         struct Precalculated {
+            int32_t xOrigin;
             float xScale;
+
+            int32_t yOrigin;
             float yScale;
+
+            int32_t pressureOrigin;
             float pressureScale;
+
+            int32_t sizeOrigin;
             float sizeScale;
         } precalculated;
 
@@ -361,7 +370,7 @@
 
         // The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it
         // passes through the dispatch pipeline.
-        ACTION_BRIGHT_HERE = 0x00000008
+        ACTION_BRIGHT_HERE = 0x00000008,
     };
 
     /* Describes a virtual key. */
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index d6bded6..2dfe2a8 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -333,18 +333,20 @@
 /*
  * NDK input queue API.
  */
-struct input_queue_t {
+struct AInputQueue {
 public:
     /* Creates a consumer associated with an input channel. */
-    explicit input_queue_t(const android::sp<android::InputChannel>& channel);
+    explicit AInputQueue(const android::sp<android::InputChannel>& channel);
 
     /* Destroys the consumer and releases its input channel. */
-    ~input_queue_t();
+    virtual ~AInputQueue();
 
     inline android::InputConsumer& getConsumer() { return mConsumer; }
     
     android::status_t consume(android::InputEvent** event);
     
+    virtual void doDefaultKey(android::KeyEvent* keyEvent) = 0;
+    
 private:
     android::InputConsumer mConsumer;
     android::PreallocatedInputEventFactory mInputEventFactory;
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index 171f3df..ca89b06 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -22,6 +22,8 @@
 
 #include <hardware/gralloc.h>
 
+#include <android/native_window.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -90,19 +92,19 @@
     NATIVE_WINDOW_API_EGL = 1
 };
 
-typedef struct android_native_window_t 
+struct ANativeWindow 
 {
 #ifdef __cplusplus
-    android_native_window_t()
+    ANativeWindow()
         : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)
     {
         common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
-        common.version = sizeof(android_native_window_t);
+        common.version = sizeof(ANativeWindow);
         memset(common.reserved, 0, sizeof(common.reserved));
     }
 
-    // Implement the methods that sp<android_native_window_t> expects so that it
-    // can be used to automatically refcount android_native_window_t's.
+    // Implement the methods that sp<ANativeWindow> expects so that it
+    // can be used to automatically refcount ANativeWindow's.
     void incStrong(const void* id) const {
         common.incRef(const_cast<android_native_base_t*>(&common));
     }
@@ -135,7 +137,7 @@
      * 
      * Returns 0 on success or -errno on error.
      */
-    int     (*setSwapInterval)(struct android_native_window_t* window,
+    int     (*setSwapInterval)(struct ANativeWindow* window,
                 int interval);
     
     /*
@@ -145,7 +147,7 @@
      * 
      * Returns 0 on success or -errno on error.
      */
-    int     (*dequeueBuffer)(struct android_native_window_t* window,
+    int     (*dequeueBuffer)(struct ANativeWindow* window,
                 struct android_native_buffer_t** buffer);
 
     /*
@@ -155,7 +157,7 @@
      * 
      * Returns 0 on success or -errno on error.
      */
-    int     (*lockBuffer)(struct android_native_window_t* window,
+    int     (*lockBuffer)(struct ANativeWindow* window,
                 struct android_native_buffer_t* buffer);
    /*
     * hook called by EGL when modifications to the render buffer are done. 
@@ -165,7 +167,7 @@
     * 
     * Returns 0 on success or -errno on error.
     */
-    int     (*queueBuffer)(struct android_native_window_t* window,
+    int     (*queueBuffer)(struct ANativeWindow* window,
                 struct android_native_buffer_t* buffer);
 
     /*
@@ -173,13 +175,13 @@
      * 
      * Returns 0 on success or -errno on error.
      */
-    int     (*query)(struct android_native_window_t* window,
+    int     (*query)(struct ANativeWindow* window,
                 int what, int* value);
     
     /*
      * hook used to perform various operations on the surface.
      * (*perform)() is a generic mechanism to add functionality to
-     * android_native_window_t while keeping backward binary compatibility.
+     * ANativeWindow while keeping backward binary compatibility.
      * 
      * This hook should not be called directly, instead use the helper functions
      * defined below.
@@ -197,12 +199,14 @@
      *  
      */
     
-    int     (*perform)(struct android_native_window_t* window,
+    int     (*perform)(struct ANativeWindow* window,
                 int operation, ... );
     
     void* reserved_proc[3];
-} android_native_window_t;
+};
 
+// Backwards compatibility...  please switch to ANativeWindow.
+typedef struct ANativeWindow android_native_window_t;
 
 /*
  *  native_window_set_usage(..., usage)
@@ -216,7 +220,7 @@
  */
 
 static inline int native_window_set_usage(
-        android_native_window_t* window, int usage)
+        ANativeWindow* window, int usage)
 {
     return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage);
 }
@@ -228,7 +232,7 @@
  * can happen if it's connected to some other API.
  */
 static inline int native_window_connect(
-        android_native_window_t* window, int api)
+        ANativeWindow* window, int api)
 {
     return window->perform(window, NATIVE_WINDOW_CONNECT, api);
 }
@@ -240,7 +244,7 @@
  * first place.
  */
 static inline int native_window_disconnect(
-        android_native_window_t* window, int api)
+        ANativeWindow* window, int api)
 {
     return window->perform(window, NATIVE_WINDOW_DISCONNECT, api);
 }
@@ -258,7 +262,7 @@
  * out of the buffer's bound or if the window is invalid.
  */
 static inline int native_window_set_crop(
-        android_native_window_t* window,
+        ANativeWindow* window,
         android_native_rect_t const * crop)
 {
     return window->perform(window, NATIVE_WINDOW_SET_CROP, crop);
@@ -269,7 +273,7 @@
  * Sets the number of buffers associated with this native window.
  */
 static inline int native_window_set_buffer_count(
-        android_native_window_t* window,
+        ANativeWindow* window,
         size_t bufferCount)
 {
     return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount);
@@ -287,7 +291,7 @@
  *
  */
 static inline int native_window_set_buffers_geometry(
-        android_native_window_t* window,
+        ANativeWindow* window,
         int w, int h, int format)
 {
     return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index cb9937c..5ae8d01 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -29,7 +29,7 @@
 ContextSetSurface {
 	param uint32_t width
 	param uint32_t height
-	param android_native_window_t *sur
+	param ANativeWindow *sur
 	}
 
 ContextDump {
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index d8a9a99..596f533 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -473,7 +473,7 @@
     objDestroyOOBDestroy();
 }
 
-void Context::setSurface(uint32_t w, uint32_t h, android_native_window_t *sur)
+void Context::setSurface(uint32_t w, uint32_t h, ANativeWindow *sur)
 {
     rsAssert(mIsGraphicsContext);
 
@@ -888,7 +888,7 @@
     rsc->resume();
 }
 
-void rsi_ContextSetSurface(Context *rsc, uint32_t w, uint32_t h, android_native_window_t *sur)
+void rsi_ContextSetSurface(Context *rsc, uint32_t w, uint32_t h, ANativeWindow *sur)
 {
     rsc->setSurface(w, h, sur);
 }
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 82c3687..709730e 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -98,7 +98,7 @@
 
     void pause();
     void resume();
-    void setSurface(uint32_t w, uint32_t h, android_native_window_t *sur);
+    void setSurface(uint32_t w, uint32_t h, ANativeWindow *sur);
     void setPriority(int32_t p);
 
     void assignName(ObjectBase *obj, const char *name, uint32_t len);
@@ -246,7 +246,7 @@
 
     static void * threadProc(void *);
 
-    android_native_window_t *mWndSurface;
+    ANativeWindow *mWndSurface;
 
     Vector<ObjectBase *> mNames;
 
diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk
index 0879a66..a14bfb5 100644
--- a/libs/surfaceflinger/Android.mk
+++ b/libs/surfaceflinger/Android.mk
@@ -6,6 +6,7 @@
     DisplayHardware/DisplayHardware.cpp \
     DisplayHardware/DisplayHardwareBase.cpp \
     BlurFilter.cpp.arm \
+    GLExtensions.cpp \
     Layer.cpp \
     LayerBase.cpp \
     LayerBuffer.cpp \
@@ -19,8 +20,8 @@
 LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\"
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 
-ifeq ($(TARGET_BOARD_PLATFORM), msm7k)
-	LOCAL_CFLAGS += -DDIM_WITH_TEXTURE
+ifeq ($(TARGET_BOARD_PLATFORM), omap3)
+	LOCAL_CFLAGS += -DNO_RGBX_8888
 endif
 
 # need "-lrt" on Linux simulator to pick up clock_gettime
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 51de1da..2eac0a8 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -40,6 +40,8 @@
 #include <hardware/overlay.h>
 #include <hardware/gralloc.h>
 
+#include "GLExtensions.h"
+
 using namespace android;
 
 
@@ -73,7 +75,8 @@
 DisplayHardware::DisplayHardware(
         const sp<SurfaceFlinger>& flinger,
         uint32_t dpy)
-    : DisplayHardwareBase(flinger, dpy), mFlags(0)
+    : DisplayHardwareBase(flinger, dpy),
+      mFlags(0)
 {
     init(dpy);
 }
@@ -97,6 +100,9 @@
 {
     mNativeWindow = new FramebufferNativeWindow();
     framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
+    mDpiX = mNativeWindow->xdpi;
+    mDpiY = mNativeWindow->ydpi;
+    mRefreshRate = fbDev->fps;
 
     mOverlayEngine = NULL;
     hw_module_t const* module;
@@ -104,6 +110,11 @@
         overlay_control_open(module, &mOverlayEngine);
     }
 
+    EGLint w, h, dummy;
+    EGLint numConfigs=0;
+    EGLSurface surface;
+    EGLContext context;
+
     // initialize EGL
     EGLint attribs[] = {
             EGL_SURFACE_TYPE,   EGL_WINDOW_BIT,
@@ -121,11 +132,6 @@
         }
     }
 
-    EGLint w, h, dummy;
-    EGLint numConfigs=0;
-    EGLSurface surface;
-    EGLContext context;
-
     // TODO: all the extensions below should be queried through
     // eglGetProcAddress().
 
@@ -144,22 +150,6 @@
     eglGetConfigAttrib(display, config, EGL_BLUE_SIZE,  &b);
     eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
 
-    /*
-     * Gather EGL extensions
-     */
-
-    const char* const egl_extensions = eglQueryString(
-            display, EGL_EXTENSIONS);
-    
-    LOGI("EGL informations:");
-    LOGI("# of configs : %d", numConfigs);
-    LOGI("vendor    : %s", eglQueryString(display, EGL_VENDOR));
-    LOGI("version   : %s", eglQueryString(display, EGL_VERSION));
-    LOGI("extensions: %s", egl_extensions);
-    LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported");
-    LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
-    
-
     if (mNativeWindow->isUpdateOnDemand()) {
         mFlags |= PARTIAL_UPDATES;
     }
@@ -174,6 +164,8 @@
      */
 
     surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
+    eglQuerySurface(display, surface, EGL_WIDTH,  &mWidth);
+    eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight);
 
     if (mFlags & PARTIAL_UPDATES) {
         // if we have partial updates, we definitely don't need to
@@ -187,31 +179,6 @@
             mFlags |= BUFFER_PRESERVED;
         }
     }
-
-    eglQuerySurface(display, surface, EGL_WIDTH,  &mWidth);
-    eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight);
-
-#ifdef EGL_ANDROID_swap_rectangle    
-    if (strstr(egl_extensions, "EGL_ANDROID_swap_rectangle")) {
-        if (eglSetSwapRectangleANDROID(display, surface,
-                0, 0, mWidth, mHeight) == EGL_TRUE) {
-            // This could fail if this extension is not supported by this
-            // specific surface (of config)
-            mFlags |= SWAP_RECTANGLE;
-        }
-    }
-    // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE
-    // choose PARTIAL_UPDATES, which should be more efficient
-    if (mFlags & PARTIAL_UPDATES)
-        mFlags &= ~SWAP_RECTANGLE;
-#endif
-    
-
-    LOGI("flags     : %08x", mFlags);
-    
-    mDpiX = mNativeWindow->xdpi;
-    mDpiY = mNativeWindow->ydpi;
-    mRefreshRate = fbDev->fps; 
     
     /* Read density from build-specific ro.sf.lcd_density property
      * except if it is overridden by qemu.sf.lcd_density.
@@ -234,49 +201,67 @@
     
     context = eglCreateContext(display, config, NULL, NULL);
     
-    /*
-     * Gather OpenGL ES extensions
-     */
-
-    eglMakeCurrent(display, surface, surface, context);
-    const char* const  gl_extensions = (const char*)glGetString(GL_EXTENSIONS);
-    const char* const  gl_renderer = (const char*)glGetString(GL_RENDERER);
-    LOGI("OpenGL informations:");
-    LOGI("vendor    : %s", glGetString(GL_VENDOR));
-    LOGI("renderer  : %s", gl_renderer);
-    LOGI("version   : %s", glGetString(GL_VERSION));
-    LOGI("extensions: %s", gl_extensions);
-
-    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
-    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims);
-    LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize);
-    LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims);
-
-
-    if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) {
-        mFlags |= NPOT_EXTENSION;
-    }
-#ifdef EGL_ANDROID_image_native_buffer
-    if (strstr( gl_extensions, "GL_OES_EGL_image") &&
-        (strstr(egl_extensions, "EGL_KHR_image_base") || 
-                strstr(egl_extensions, "EGL_KHR_image")) &&
-        strstr(egl_extensions, "EGL_ANDROID_image_native_buffer")) {
-        mFlags |= DIRECT_TEXTURE;
-    }
-#else
-#warning "EGL_ANDROID_image_native_buffer not supported"
-#endif
-
-
-    // Unbind the context from this thread
-    eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
     mDisplay = display;
     mConfig  = config;
     mSurface = surface;
     mContext = context;
     mFormat  = fbDev->format;
     mPageFlipCount = 0;
+
+    /*
+     * Gather OpenGL ES extensions
+     */
+
+    eglMakeCurrent(display, surface, surface, context);
+
+    GLExtensions& extensions(GLExtensions::getInstance());
+    extensions.initWithGLStrings(
+            glGetString(GL_VENDOR),
+            glGetString(GL_RENDERER),
+            glGetString(GL_VERSION),
+            glGetString(GL_EXTENSIONS),
+            eglQueryString(display, EGL_VENDOR),
+            eglQueryString(display, EGL_VERSION),
+            eglQueryString(display, EGL_EXTENSIONS));
+
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims);
+
+
+#ifdef EGL_ANDROID_swap_rectangle
+    if (extensions.hasExtension("EGL_ANDROID_swap_rectangle")) {
+        if (eglSetSwapRectangleANDROID(display, surface,
+                0, 0, mWidth, mHeight) == EGL_TRUE) {
+            // This could fail if this extension is not supported by this
+            // specific surface (of config)
+            mFlags |= SWAP_RECTANGLE;
+        }
+    }
+    // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE
+    // choose PARTIAL_UPDATES, which should be more efficient
+    if (mFlags & PARTIAL_UPDATES)
+        mFlags &= ~SWAP_RECTANGLE;
+#endif
+
+    LOGI("EGL informations:");
+    LOGI("# of configs : %d", numConfigs);
+    LOGI("vendor    : %s", extensions.getEglVendor());
+    LOGI("version   : %s", extensions.getEglVersion());
+    LOGI("extensions: %s", extensions.getEglExtension());
+    LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported");
+    LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
+
+    LOGI("OpenGL informations:");
+    LOGI("vendor    : %s", extensions.getVendor());
+    LOGI("renderer  : %s", extensions.getRenderer());
+    LOGI("version   : %s", extensions.getVersion());
+    LOGI("extensions: %s", extensions.getExtension());
+    LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize);
+    LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims);
+    LOGI("flags = %08x", mFlags);
+
+    // Unbind the context from this thread
+    eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
 }
 
 /*
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
index ebd7c42..66bf521 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -29,6 +29,8 @@
 
 #include <pixelflinger/pixelflinger.h>
 
+#include "GLExtensions.h"
+
 #include "DisplayHardware/DisplayHardwareBase.h"
 
 struct overlay_control_device_t;
@@ -43,13 +45,11 @@
 {
 public:
     enum {
-        DIRECT_TEXTURE          = 0x00000002,
-        COPY_BITS_EXTENSION     = 0x00000008,
-        NPOT_EXTENSION          = 0x00000100,
-        BUFFER_PRESERVED        = 0x00010000,
-        PARTIAL_UPDATES         = 0x00020000,   // video driver feature
-        SLOW_CONFIG             = 0x00040000,   // software
-        SWAP_RECTANGLE          = 0x00080000,
+        COPY_BITS_EXTENSION         = 0x00000008,
+        BUFFER_PRESERVED            = 0x00010000,
+        PARTIAL_UPDATES             = 0x00020000,   // video driver feature
+        SLOW_CONFIG                 = 0x00040000,   // software
+        SWAP_RECTANGLE              = 0x00080000,
     };
 
     DisplayHardware(
diff --git a/libs/surfaceflinger/GLExtensions.cpp b/libs/surfaceflinger/GLExtensions.cpp
new file mode 100644
index 0000000..7f4f9fc
--- /dev/null
+++ b/libs/surfaceflinger/GLExtensions.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "GLExtensions.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE( GLExtensions )
+
+GLExtensions::GLExtensions()
+    : mHaveTextureExternal(false),
+      mHaveNpot(false),
+      mHaveDirectTexture(false)
+{
+}
+
+void GLExtensions::initWithGLStrings(
+        GLubyte const* vendor,
+        GLubyte const* renderer,
+        GLubyte const* version,
+        GLubyte const* extensions,
+        char const* egl_vendor,
+        char const* egl_version,
+        char const* egl_extensions)
+{
+    mVendor     = (char const*)vendor;
+    mRenderer   = (char const*)renderer;
+    mVersion    = (char const*)version;
+    mExtensions = (char const*)extensions;
+    mEglVendor     = egl_vendor;
+    mEglVersion    = egl_version;
+    mEglExtensions = egl_extensions;
+
+    char const* curr = (char const*)extensions;
+    char const* head = curr;
+    do {
+        head = strchr(curr, ' ');
+        String8 s(curr, head ? head-curr : strlen(curr));
+        if (s.length()) {
+            mExtensionList.add(s);
+        }
+        curr = head+1;
+    } while (head);
+
+    curr = egl_extensions;
+    head = curr;
+    do {
+        head = strchr(curr, ' ');
+        String8 s(curr, head ? head-curr : strlen(curr));
+        if (s.length()) {
+            mExtensionList.add(s);
+        }
+        curr = head+1;
+    } while (head);
+
+#ifdef EGL_ANDROID_image_native_buffer
+    if (hasExtension("GL_OES_EGL_image") &&
+        (hasExtension("EGL_KHR_image_base") || hasExtension("EGL_KHR_image")) &&
+        hasExtension("EGL_ANDROID_image_native_buffer"))
+    {
+        mHaveDirectTexture = true;
+    }
+#else
+#warning "EGL_ANDROID_image_native_buffer not supported"
+#endif
+
+    if (hasExtension("GL_ARB_texture_non_power_of_two")) {
+        mHaveNpot = true;
+    }
+
+    if (hasExtension("GL_OES_texture_external")) {
+        mHaveTextureExternal = true;
+    } else if (strstr(mRenderer.string(), "Adreno")) {
+        // hack for Adreno 200
+        mHaveTextureExternal = true;
+    }
+}
+
+bool GLExtensions::hasExtension(char const* extension) const
+{
+    const String8 s(extension);
+    return mExtensionList.indexOf(s) >= 0;
+}
+
+char const* GLExtensions::getVendor() const {
+    return mVendor.string();
+}
+
+char const* GLExtensions::getRenderer() const {
+    return mRenderer.string();
+}
+
+char const* GLExtensions::getVersion() const {
+    return mVersion.string();
+}
+
+char const* GLExtensions::getExtension() const {
+    return mExtensions.string();
+}
+
+char const* GLExtensions::getEglVendor() const {
+    return mEglVendor.string();
+}
+
+char const* GLExtensions::getEglVersion() const {
+    return mEglVersion.string();
+}
+
+char const* GLExtensions::getEglExtension() const {
+    return mEglExtensions.string();
+}
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/surfaceflinger/GLExtensions.h b/libs/surfaceflinger/GLExtensions.h
new file mode 100644
index 0000000..bbb284e
--- /dev/null
+++ b/libs/surfaceflinger/GLExtensions.h
@@ -0,0 +1,94 @@
+/*
+ * 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_SF_GLEXTENSION_H
+#define ANDROID_SF_GLEXTENSION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/String8.h>
+#include <utils/SortedVector.h>
+#include <utils/Singleton.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class GLExtensions : public Singleton<GLExtensions>
+{
+    friend class Singleton<GLExtensions>;
+
+    bool mHaveTextureExternal   : 1;
+    bool mHaveNpot              : 1;
+    bool mHaveDirectTexture     : 1;
+
+    String8 mVendor;
+    String8 mRenderer;
+    String8 mVersion;
+    String8 mExtensions;
+    String8 mEglVendor;
+    String8 mEglVersion;
+    String8 mEglExtensions;
+    SortedVector<String8> mExtensionList;
+
+    GLExtensions(const GLExtensions&);
+    GLExtensions& operator = (const GLExtensions&);
+
+protected:
+    GLExtensions();
+
+public:
+    inline bool haveTextureExternal() const {
+        return mHaveTextureExternal;
+    }
+    inline bool haveNpot() const {
+        return mHaveNpot;
+    }
+    inline bool haveDirectTexture() const {
+        return mHaveDirectTexture;
+    }
+
+    void initWithGLStrings(
+            GLubyte const* vendor,
+            GLubyte const* renderer,
+            GLubyte const* version,
+            GLubyte const* extensions,
+            char const* egl_vendor,
+            char const* egl_version,
+            char const* egl_extensions);
+
+    char const* getVendor() const;
+    char const* getRenderer() const;
+    char const* getVersion() const;
+    char const* getExtension() const;
+
+    char const* getEglVendor() const;
+    char const* getEglVersion() const;
+    char const* getEglExtension() const;
+
+    bool hasExtension(char const* extension) const;
+};
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SF_GLEXTENSION_H
diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp
index e606f71..758da4e 100644
--- a/libs/surfaceflinger/Layer.cpp
+++ b/libs/surfaceflinger/Layer.cpp
@@ -31,6 +31,7 @@
 #include <surfaceflinger/Surface.h>
 
 #include "clz.h"
+#include "GLExtensions.h"
 #include "Layer.h"
 #include "SurfaceFlinger.h"
 #include "DisplayHardware/DisplayHardware.h"
@@ -50,10 +51,11 @@
 Layer::Layer(SurfaceFlinger* flinger,
         DisplayID display, const sp<Client>& client)
     :   LayerBaseClient(flinger, display, client),
+        mGLExtensions(GLExtensions::getInstance()),
         mNeedsBlending(true),
         mNeedsDithering(false),
         mSecure(false),
-        mTextureManager(mFlags),
+        mTextureManager(),
         mBufferManager(mTextureManager),
         mWidth(0), mHeight(0), mFixedSize(false)
 {
@@ -185,17 +187,13 @@
         return;
     }
 
-#ifdef EGL_ANDROID_image_native_buffer
-    if (mFlags & DisplayHardware::DIRECT_TEXTURE) {
+    if (mGLExtensions.haveDirectTexture()) {
         EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
         if (mBufferManager.initEglImage(dpy, buffer) != NO_ERROR) {
             // not sure what we can do here...
-            mFlags &= ~DisplayHardware::DIRECT_TEXTURE;
             goto slowpath;
         }
-    } else
-#endif
-    {
+    } else {
 slowpath:
         GGLSurface t;
         status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
@@ -786,19 +784,24 @@
     status_t err = NO_INIT;
     ssize_t index = mActiveBuffer;
     if (index >= 0) {
-        Image& texture(mBufferData[index].texture);
-        err = mTextureManager.initEglImage(&texture, dpy, buffer);
-        // if EGLImage fails, we switch to regular texture mode, and we
-        // free all resources associated with using EGLImages.
-        if (err == NO_ERROR) {
-            mFailover = false;
-            destroyTexture(&mFailoverTexture, dpy);
-        } else {
-            mFailover = true;
-            const size_t num = mNumBuffers;
-            for (size_t i=0 ; i<num ; i++) {
-                destroyTexture(&mBufferData[i].texture, dpy);
+        if (!mFailover) {
+            Image& texture(mBufferData[index].texture);
+            err = mTextureManager.initEglImage(&texture, dpy, buffer);
+            // if EGLImage fails, we switch to regular texture mode, and we
+            // free all resources associated with using EGLImages.
+            if (err == NO_ERROR) {
+                mFailover = false;
+                destroyTexture(&mFailoverTexture, dpy);
+            } else {
+                mFailover = true;
+                const size_t num = mNumBuffers;
+                for (size_t i=0 ; i<num ; i++) {
+                    destroyTexture(&mBufferData[i].texture, dpy);
+                }
             }
+        } else {
+            // we failed once, don't try again
+            err = BAD_VALUE;
         }
     }
     return err;
diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h
index dcb27a0..e1d283b 100644
--- a/libs/surfaceflinger/Layer.h
+++ b/libs/surfaceflinger/Layer.h
@@ -37,9 +37,10 @@
 
 // ---------------------------------------------------------------------------
 
-class Client;
-class UserClient;
 class FreezeLock;
+class Client;
+class GLExtensions;
+class UserClient;
 
 // ---------------------------------------------------------------------------
 
@@ -206,6 +207,7 @@
     // constants
     sp<Surface> mSurface;
     PixelFormat mFormat;
+    const GLExtensions& mGLExtensions;
     bool mNeedsBlending;
     bool mNeedsDithering;
 
diff --git a/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp
index c1c440b..64a43c7 100644
--- a/libs/surfaceflinger/LayerBlur.cpp
+++ b/libs/surfaceflinger/LayerBlur.cpp
@@ -147,7 +147,9 @@
     Region::const_iterator const end = clip.end();
     if (it != end) {
 #if defined(GL_OES_texture_external)
-        glDisable(GL_TEXTURE_EXTERNAL_OES);
+        if (GLExtensions::getInstance().haveTextureExternal()) {
+            glDisable(GL_TEXTURE_EXTERNAL_OES);
+        }
 #endif
         glEnable(GL_TEXTURE_2D);
         glBindTexture(GL_TEXTURE_2D, mTextureName);
@@ -181,7 +183,7 @@
             bl.data = (GGLubyte*)pixels;            
             blurFilter(&bl, 8, 2);
 
-            if (mFlags & (DisplayHardware::NPOT_EXTENSION)) {
+            if (GLExtensions::getInstance().haveNpot()) {
                 glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, w, h, 0,
                         mReadFormat, mReadType, pixels);
                 mWidthScale  = 1.0f / w;
diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp
index 732a4ec..5f836366 100644
--- a/libs/surfaceflinger/LayerBuffer.cpp
+++ b/libs/surfaceflinger/LayerBuffer.cpp
@@ -315,8 +315,7 @@
 
 LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer,
         const ISurface::BufferHeap& buffers)
-    : Source(layer), mStatus(NO_ERROR), mBufferSize(0),
-      mTextureManager(layer.mFlags)
+    : Source(layer), mStatus(NO_ERROR), mBufferSize(0)
 {
     if (buffers.heap == NULL) {
         // this is allowed, but in this case, it is illegal to receive
@@ -374,11 +373,11 @@
 
     if (mTexture.name != -1U) {
         // GL textures can only be destroyed from the GL thread
-        mLayer.mFlinger->mEventQueue.postMessage(
-                new MessageDestroyTexture(mLayer.mFlinger.get(), mTexture.name) );
+        getFlinger()->mEventQueue.postMessage(
+                new MessageDestroyTexture(getFlinger(), mTexture.name) );
     }
     if (mTexture.image != EGL_NO_IMAGE_KHR) {
-        EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
+        EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay());
         eglDestroyImageKHR(dpy, mTexture.image);
     }
 }
@@ -444,7 +443,7 @@
     const Rect transformedBounds(mLayer.getTransformedBounds());
 
 #if defined(EGL_ANDROID_image_native_buffer)
-    if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) {
+    if (GLExtensions::getInstance().haveDirectTexture()) {
         err = INVALID_OPERATION;
         if (ourBuffer->supportsCopybit()) {
             copybit_device_t* copybit = mLayer.mBlitEngine;
@@ -549,7 +548,7 @@
         dst.crop.r = w;
         dst.crop.b = h;
 
-        EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
+        EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay());
         err = mTextureManager.initEglImage(&mTexture, dpy, buffer);
     }
 
@@ -559,7 +558,7 @@
 void LayerBuffer::BufferSource::clearTempBufferImage() const
 {
     // delete the image
-    EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
+    EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay());
     eglDestroyImageKHR(dpy, mTexture.image);
 
     // and the associated texture (recreate a name)
@@ -576,7 +575,7 @@
     : Source(layer), mVisibilityChanged(false),
     mOverlay(0), mOverlayHandle(0), mOverlayDevice(0), mOrientation(orientation)
 {
-    overlay_control_device_t* overlay_dev = mLayer.mFlinger->getOverlayEngine();
+    overlay_control_device_t* overlay_dev = getFlinger()->getOverlayEngine();
     if (overlay_dev == NULL) {
         // overlays not supported
         return;
@@ -607,7 +606,7 @@
 
     *overlayRef = new OverlayRef(mOverlayHandle, channel,
             mWidth, mHeight, mFormat, mWidthStride, mHeightStride);
-    mLayer.mFlinger->signalEvent();
+    getFlinger()->signalEvent();
 }
 
 LayerBuffer::OverlaySource::~OverlaySource()
diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h
index 413b8a4..1c0bf83 100644
--- a/libs/surfaceflinger/LayerBuffer.h
+++ b/libs/surfaceflinger/LayerBuffer.h
@@ -47,6 +47,7 @@
         virtual void postBuffer(ssize_t offset);
         virtual void unregisterBuffers();
         virtual void destroy() { }
+        SurfaceFlinger* getFlinger() const { return mLayer.mFlinger.get(); }
     protected:
         LayerBuffer& mLayer;
     };
diff --git a/libs/surfaceflinger/LayerDim.cpp b/libs/surfaceflinger/LayerDim.cpp
index 906a583..a1f339e 100644
--- a/libs/surfaceflinger/LayerDim.cpp
+++ b/libs/surfaceflinger/LayerDim.cpp
@@ -51,54 +51,6 @@
     sWidth = w;
     sHeight = h;
     sUseTexture = false;
-    
-#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer)
-
-#warning "using a texture to implement LayerDim"
-    
-    /* On some h/w like msm7K, it is faster to use a texture because the
-     * software renderer will defer to copybit, for this to work we need to
-     * use an EGLImage texture so copybit can actually make use of it.
-     * This burns a full-screen worth of graphic memory.
-     */
-
-    const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
-    uint32_t flags = hw.getFlags();
-
-    if (LIKELY(flags & DisplayHardware::DIRECT_TEXTURE)) {
-        sp<GraphicBuffer> buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGB_565,
-                 GraphicBuffer::USAGE_SW_WRITE_OFTEN |
-                 GraphicBuffer::USAGE_HW_TEXTURE);
-        
-        android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
-
-        glGenTextures(1, &sTexId);
-        glBindTexture(GL_TEXTURE_2D, sTexId);
-
-        EGLDisplay dpy = eglGetCurrentDisplay();
-        sImage = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, 
-                EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)clientBuf, 0);
-        if (sImage == EGL_NO_IMAGE_KHR) {
-            LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
-            return;
-        }
-
-        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)sImage);
-        GLint error = glGetError();
-        if (error != GL_NO_ERROR) {
-            eglDestroyImageKHR(dpy, sImage);
-            LOGE("glEGLImageTargetTexture2DOES() failed. err=0x%4x", error);
-            return;
-        }
-
-        // initialize the texture with zeros
-        GGLSurface t;
-        buffer->lock(&t, GRALLOC_USAGE_SW_WRITE_OFTEN);
-        memset(t.data, 0, t.stride * t.height * 2);
-        buffer->unlock();
-        sUseTexture = true;
-    }
-#endif
 }
 
 LayerDim::~LayerDim()
@@ -112,36 +64,19 @@
     Region::const_iterator const end = clip.end();
     if (s.alpha>0 && (it != end)) {
         const DisplayHardware& hw(graphicPlane(0).displayHardware());
-        const GGLfixed alpha = (s.alpha << 16)/255;
+        const GLfloat alpha = s.alpha/255.0f;
         const uint32_t fbHeight = hw.getHeight();
         glDisable(GL_DITHER);
         glEnable(GL_BLEND);
         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-        glColor4x(0, 0, 0, alpha);
-        
+        glColor4f(0, 0, 0, alpha);
+
 #if defined(GL_OES_texture_external)
-        glDisable(GL_TEXTURE_EXTERNAL_OES);
-#endif
-#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer)
-        if (sUseTexture) {
-            glBindTexture(GL_TEXTURE_2D, sTexId);
-            glEnable(GL_TEXTURE_2D);
-            glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-            const GLshort texCoords[4][2] = {
-                    { 0,  0 },
-                    { 0,  1 },
-                    { 1,  1 },
-                    { 1,  0 }
-            };
-            glMatrixMode(GL_TEXTURE);
-            glLoadIdentity();
-            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-            glTexCoordPointer(2, GL_SHORT, 0, texCoords);
-        } else
-#endif
-        {
-            glDisable(GL_TEXTURE_2D);
+        if (GLExtensions::getInstance().haveTextureExternal()) {
+            glDisable(GL_TEXTURE_EXTERNAL_OES);
         }
+#endif
+        glDisable(GL_TEXTURE_2D);
 
         GLshort w = sWidth;
         GLshort h = sHeight;
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 96a5411..68e8f19 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -44,6 +44,7 @@
 #include <GLES/gl.h>
 
 #include "clz.h"
+#include "GLExtensions.h"
 #include "Layer.h"
 #include "LayerBlur.h"
 #include "LayerBuffer.h"
@@ -993,7 +994,9 @@
         glTexCoordPointer(2, GL_SHORT, 0, tcoords);
         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 #if defined(GL_OES_texture_external)
-        glDisable(GL_TEXTURE_EXTERNAL_OES);
+        if (GLExtensions::getInstance().haveTextureExternal()) {
+            glDisable(GL_TEXTURE_EXTERNAL_OES);
+        }
 #endif
         glEnable(GL_TEXTURE_2D);
         glBindTexture(GL_TEXTURE_2D, mWormholeTexName);
@@ -1260,10 +1263,19 @@
         format = PIXEL_FORMAT_RGBA_8888;
         break;
     case PIXEL_FORMAT_OPAQUE:
+#ifdef NO_RGBX_8888
+        format = PIXEL_FORMAT_RGB_565;
+#else
         format = PIXEL_FORMAT_RGBX_8888;
+#endif
         break;
     }
 
+#ifdef NO_RGBX_8888
+    if (format == PIXEL_FORMAT_RGBX_8888)
+        format = PIXEL_FORMAT_RGBA_8888;
+#endif
+
     sp<Layer> layer = new Layer(this, display, client);
     status_t err = layer->setBuffers(w, h, format, flags);
     if (LIKELY(err != NO_ERROR)) {
diff --git a/libs/surfaceflinger/TextureManager.cpp b/libs/surfaceflinger/TextureManager.cpp
index d9bdc6a..6526032 100644
--- a/libs/surfaceflinger/TextureManager.cpp
+++ b/libs/surfaceflinger/TextureManager.cpp
@@ -30,14 +30,15 @@
 
 #include "clz.h"
 #include "DisplayHardware/DisplayHardware.h"
+#include "GLExtensions.h"
 #include "TextureManager.h"
 
 namespace android {
 
 // ---------------------------------------------------------------------------
 
-TextureManager::TextureManager(uint32_t flags)
-    : mFlags(flags)
+TextureManager::TextureManager()
+    : mGLExtensions(GLExtensions::getInstance())
 {
 }
 
@@ -85,9 +86,11 @@
 
     GLenum target = GL_TEXTURE_2D;
 #if defined(GL_OES_texture_external)
-    if (format && isSupportedYuvFormat(format)) {
-        target = GL_TEXTURE_EXTERNAL_OES;
-        pImage->target = Texture::TEXTURE_EXTERNAL;
+    if (GLExtensions::getInstance().haveTextureExternal()) {
+        if (format && isYuvFormat(format)) {
+            target = GL_TEXTURE_EXTERNAL_OES;
+            pImage->target = Texture::TEXTURE_EXTERNAL;
+        }
     }
 #endif
 
@@ -102,23 +105,32 @@
 
 bool TextureManager::isSupportedYuvFormat(int format)
 {
-    return isYuvFormat(format);
+    switch (format) {
+    case HAL_PIXEL_FORMAT_YV12:
+    case HAL_PIXEL_FORMAT_YV16:
+        return true;
+    }
+    return false;
 }
 
 bool TextureManager::isYuvFormat(int format)
 {
     switch (format) {
-    case HAL_PIXEL_FORMAT_NV16:
-    case HAL_PIXEL_FORMAT_NV21:
-    case HAL_PIXEL_FORMAT_IYUV:
-    case HAL_PIXEL_FORMAT_YUV9:
-    case HAL_PIXEL_FORMAT_YUY2:
-    case HAL_PIXEL_FORMAT_UYVY:
-    case HAL_PIXEL_FORMAT_NV12:
-    case HAL_PIXEL_FORMAT_NV61:
-    case HAL_PIXEL_FORMAT_NV12_ADRENO_TILED:
+    // supported YUV formats
+    case HAL_PIXEL_FORMAT_YV12:
+    case HAL_PIXEL_FORMAT_YV16:
+    // Legacy/deprecated YUV formats
+    case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+    case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+    case HAL_PIXEL_FORMAT_YCbCr_422_I:
+    case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
         return true;
     }
+
+    // Any OEM format needs to be considered
+    if (format>=0x100 && format<=0x1FF)
+        return true;
+
     return false;
 }
 
@@ -208,7 +220,7 @@
     /*
      * round to POT if needed
      */
-    if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) {
+    if (!mGLExtensions.haveNpot()) {
         texture->NPOTAdjust = true;
     }
 
@@ -252,7 +264,7 @@
             glTexImage2D(GL_TEXTURE_2D, 0,
                     GL_RGBA, texture->potWidth, texture->potHeight, 0,
                     GL_RGBA, GL_UNSIGNED_BYTE, data);
-        } else if (isYuvFormat(t.format)) {
+        } else if (isSupportedYuvFormat(t.format)) {
             // just show the Y plane of YUV buffers
             glTexImage2D(GL_TEXTURE_2D, 0,
                     GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
@@ -280,7 +292,7 @@
                     0, bounds.top, t.width, bounds.height(),
                     GL_RGBA, GL_UNSIGNED_BYTE,
                     t.data + bounds.top*t.stride*4);
-        } else if (isYuvFormat(t.format)) {
+        } else if (isSupportedYuvFormat(t.format)) {
             // just show the Y plane of YUV buffers
             glTexSubImage2D(GL_TEXTURE_2D, 0,
                     0, bounds.top, t.width, bounds.height(),
@@ -294,17 +306,19 @@
 void TextureManager::activateTexture(const Texture& texture, bool filter)
 {
     const GLenum target = getTextureTarget(&texture);
-
-    glBindTexture(target, texture.name);
-    glEnable(target);
-
+    if (target == GL_TEXTURE_2D) {
+        glBindTexture(GL_TEXTURE_2D, texture.name);
+        glEnable(GL_TEXTURE_2D);
 #if defined(GL_OES_texture_external)
-    if (texture.target == Texture::TEXTURE_2D) {
-        glDisable(GL_TEXTURE_EXTERNAL_OES);
+        if (GLExtensions::getInstance().haveTextureExternal()) {
+            glDisable(GL_TEXTURE_EXTERNAL_OES);
+        }
     } else {
+        glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name);
+        glEnable(GL_TEXTURE_EXTERNAL_OES);
         glDisable(GL_TEXTURE_2D);
-    }
 #endif
+    }
 
     if (filter) {
         glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -319,7 +333,9 @@
 {
     glDisable(GL_TEXTURE_2D);
 #if defined(GL_OES_texture_external)
-    glDisable(GL_TEXTURE_EXTERNAL_OES);
+    if (GLExtensions::getInstance().haveTextureExternal()) {
+        glDisable(GL_TEXTURE_EXTERNAL_OES);
+    }
 #endif
 }
 
diff --git a/libs/surfaceflinger/TextureManager.h b/libs/surfaceflinger/TextureManager.h
index 1f7fe3f..c7c14e7 100644
--- a/libs/surfaceflinger/TextureManager.h
+++ b/libs/surfaceflinger/TextureManager.h
@@ -32,6 +32,7 @@
 
 // ---------------------------------------------------------------------------
 
+class GLExtensions;
 class GraphicBuffer;
 
 // ---------------------------------------------------------------------------
@@ -61,7 +62,7 @@
 // ---------------------------------------------------------------------------
 
 class TextureManager {
-    uint32_t mFlags;
+    const GLExtensions& mGLExtensions;
     static status_t initTexture(Image* texture, int32_t format);
     static status_t initTexture(Texture* texture);
     static bool isSupportedYuvFormat(int format);
@@ -69,7 +70,7 @@
     static GLenum getTextureTarget(const Image* pImage);
 public:
 
-    TextureManager(uint32_t flags);
+    TextureManager();
 
     // load bitmap data into the active buffer
     status_t loadTexture(Texture* texture,
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index 8617d94a..dc6332c 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -387,21 +387,21 @@
 
 void Surface::init()
 {
-    android_native_window_t::setSwapInterval  = setSwapInterval;
-    android_native_window_t::dequeueBuffer    = dequeueBuffer;
-    android_native_window_t::lockBuffer       = lockBuffer;
-    android_native_window_t::queueBuffer      = queueBuffer;
-    android_native_window_t::query            = query;
-    android_native_window_t::perform          = perform;
+    ANativeWindow::setSwapInterval  = setSwapInterval;
+    ANativeWindow::dequeueBuffer    = dequeueBuffer;
+    ANativeWindow::lockBuffer       = lockBuffer;
+    ANativeWindow::queueBuffer      = queueBuffer;
+    ANativeWindow::query            = query;
+    ANativeWindow::perform          = perform;
 
     DisplayInfo dinfo;
     SurfaceComposerClient::getDisplayInfo(0, &dinfo);
-    const_cast<float&>(android_native_window_t::xdpi) = dinfo.xdpi;
-    const_cast<float&>(android_native_window_t::ydpi) = dinfo.ydpi;
+    const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi;
+    const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi;
     // FIXME: set real values here
-    const_cast<int&>(android_native_window_t::minSwapInterval) = 1;
-    const_cast<int&>(android_native_window_t::maxSwapInterval) = 1;
-    const_cast<uint32_t&>(android_native_window_t::flags) = 0;
+    const_cast<int&>(ANativeWindow::minSwapInterval) = 1;
+    const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
+    const_cast<uint32_t&>(ANativeWindow::flags) = 0;
 
     mConnected = 0;
     mSwapRectangle.makeInvalid();
@@ -485,35 +485,35 @@
 
 // ----------------------------------------------------------------------------
 
-int Surface::setSwapInterval(android_native_window_t* window, int interval) {
+int Surface::setSwapInterval(ANativeWindow* window, int interval) {
     return 0;
 }
 
-int Surface::dequeueBuffer(android_native_window_t* window, 
+int Surface::dequeueBuffer(ANativeWindow* window, 
         android_native_buffer_t** buffer) {
     Surface* self = getSelf(window);
     return self->dequeueBuffer(buffer);
 }
 
-int Surface::lockBuffer(android_native_window_t* window, 
+int Surface::lockBuffer(ANativeWindow* window, 
         android_native_buffer_t* buffer) {
     Surface* self = getSelf(window);
     return self->lockBuffer(buffer);
 }
 
-int Surface::queueBuffer(android_native_window_t* window, 
+int Surface::queueBuffer(ANativeWindow* window, 
         android_native_buffer_t* buffer) {
     Surface* self = getSelf(window);
     return self->queueBuffer(buffer);
 }
 
-int Surface::query(android_native_window_t* window, 
+int Surface::query(ANativeWindow* window, 
         int what, int* value) {
     Surface* self = getSelf(window);
     return self->query(what, value);
 }
 
-int Surface::perform(android_native_window_t* window, 
+int Surface::perform(ANativeWindow* window, 
         int operation, ...) {
     va_list args;
     va_start(args, operation);
@@ -803,7 +803,7 @@
 {
     if (getConnectedApi()) {
         LOGE("Surface::lock(%p) failed. Already connected to another API",
-                (android_native_window_t*)this);
+                (ANativeWindow*)this);
         CallStack stack;
         stack.update();
         stack.dump("");
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index 52380a0..6f8948d 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -67,7 +67,7 @@
  * This implements the (main) framebuffer management. This class is used
  * mostly by SurfaceFlinger, but also by command line GL application.
  * 
- * In fact this is an implementation of android_native_window_t on top of
+ * In fact this is an implementation of ANativeWindow on top of
  * the framebuffer.
  * 
  * Currently it is pretty simple, it manages only two buffers (the front and 
@@ -117,23 +117,23 @@
         LOGE_IF(err, "fb buffer 1 allocation failed w=%d, h=%d, err=%s",
                 fbDev->width, fbDev->height, strerror(-err));
 
-        const_cast<uint32_t&>(android_native_window_t::flags) = fbDev->flags; 
-        const_cast<float&>(android_native_window_t::xdpi) = fbDev->xdpi;
-        const_cast<float&>(android_native_window_t::ydpi) = fbDev->ydpi;
-        const_cast<int&>(android_native_window_t::minSwapInterval) = 
+        const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags; 
+        const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;
+        const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi;
+        const_cast<int&>(ANativeWindow::minSwapInterval) = 
             fbDev->minSwapInterval;
-        const_cast<int&>(android_native_window_t::maxSwapInterval) = 
+        const_cast<int&>(ANativeWindow::maxSwapInterval) = 
             fbDev->maxSwapInterval;
     } else {
         LOGE("Couldn't get gralloc module");
     }
 
-    android_native_window_t::setSwapInterval = setSwapInterval;
-    android_native_window_t::dequeueBuffer = dequeueBuffer;
-    android_native_window_t::lockBuffer = lockBuffer;
-    android_native_window_t::queueBuffer = queueBuffer;
-    android_native_window_t::query = query;
-    android_native_window_t::perform = perform;
+    ANativeWindow::setSwapInterval = setSwapInterval;
+    ANativeWindow::dequeueBuffer = dequeueBuffer;
+    ANativeWindow::lockBuffer = lockBuffer;
+    ANativeWindow::queueBuffer = queueBuffer;
+    ANativeWindow::query = query;
+    ANativeWindow::perform = perform;
 }
 
 FramebufferNativeWindow::~FramebufferNativeWindow() 
@@ -168,13 +168,13 @@
 }
 
 int FramebufferNativeWindow::setSwapInterval(
-        android_native_window_t* window, int interval) 
+        ANativeWindow* window, int interval) 
 {
     framebuffer_device_t* fb = getSelf(window)->fbDev;
     return fb->setSwapInterval(fb, interval);
 }
 
-int FramebufferNativeWindow::dequeueBuffer(android_native_window_t* window, 
+int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, 
         android_native_buffer_t** buffer)
 {
     FramebufferNativeWindow* self = getSelf(window);
@@ -196,7 +196,7 @@
     return 0;
 }
 
-int FramebufferNativeWindow::lockBuffer(android_native_window_t* window, 
+int FramebufferNativeWindow::lockBuffer(ANativeWindow* window, 
         android_native_buffer_t* buffer)
 {
     FramebufferNativeWindow* self = getSelf(window);
@@ -210,7 +210,7 @@
     return NO_ERROR;
 }
 
-int FramebufferNativeWindow::queueBuffer(android_native_window_t* window, 
+int FramebufferNativeWindow::queueBuffer(ANativeWindow* window, 
         android_native_buffer_t* buffer)
 {
     FramebufferNativeWindow* self = getSelf(window);
@@ -224,7 +224,7 @@
     return res;
 }
 
-int FramebufferNativeWindow::query(android_native_window_t* window,
+int FramebufferNativeWindow::query(ANativeWindow* window,
         int what, int* value) 
 {
     FramebufferNativeWindow* self = getSelf(window);
@@ -245,7 +245,7 @@
     return BAD_VALUE;
 }
 
-int FramebufferNativeWindow::perform(android_native_window_t* window,
+int FramebufferNativeWindow::perform(ANativeWindow* window,
         int operation, ...)
 {
     switch (operation) {
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 4b5f025..519c277 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -127,18 +127,6 @@
 {
     GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
     status_t err = allocator.alloc(w, h, format, reqUsage, &handle, &stride);
-
-    if (err<0 && format == PIXEL_FORMAT_RGBX_8888) {
-        /*
-         * There is currently a bug with some gralloc implementations
-         * not supporting RGBX_8888. In this case, we revert to using RGBA_8888
-         * which is not exactly the same, as GL_REPLACE will yield a different
-         * result.
-         */
-        format = PIXEL_FORMAT_RGBA_8888;
-        err = allocator.alloc(w, h, format, reqUsage, &handle, &stride);
-    }
-
     if (err == NO_ERROR) {
         this->width  = w;
         this->height = h;
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 4121b5a..a64251f 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -20,6 +20,70 @@
 
 // class KeyEvent
 
+bool KeyEvent::hasDefaultAction(int32_t keyCode) {
+    switch (keyCode) {
+        case KEYCODE_HOME:
+        case KEYCODE_BACK:
+        case KEYCODE_CALL:
+        case KEYCODE_ENDCALL:
+        case KEYCODE_VOLUME_UP:
+        case KEYCODE_VOLUME_DOWN:
+        case KEYCODE_POWER:
+        case KEYCODE_CAMERA:
+        case KEYCODE_HEADSETHOOK:
+        case KEYCODE_MENU:
+        case KEYCODE_NOTIFICATION:
+        case KEYCODE_FOCUS:
+        case KEYCODE_SEARCH:
+        case KEYCODE_MEDIA_PLAY_PAUSE:
+        case KEYCODE_MEDIA_STOP:
+        case KEYCODE_MEDIA_NEXT:
+        case KEYCODE_MEDIA_PREVIOUS:
+        case KEYCODE_MEDIA_REWIND:
+        case KEYCODE_MEDIA_FAST_FORWARD:
+        case KEYCODE_MUTE:
+            return true;
+    }
+    
+    return false;
+}
+
+bool KeyEvent::hasDefaultAction() const {
+    return hasDefaultAction(getKeyCode());
+}
+
+bool KeyEvent::isSystemKey(int32_t keyCode) {
+    switch (keyCode) {
+        case KEYCODE_MENU:
+        case KEYCODE_SOFT_RIGHT:
+        case KEYCODE_HOME:
+        case KEYCODE_BACK:
+        case KEYCODE_CALL:
+        case KEYCODE_ENDCALL:
+        case KEYCODE_VOLUME_UP:
+        case KEYCODE_VOLUME_DOWN:
+        case KEYCODE_MUTE:
+        case KEYCODE_POWER:
+        case KEYCODE_HEADSETHOOK:
+        case KEYCODE_MEDIA_PLAY_PAUSE:
+        case KEYCODE_MEDIA_STOP:
+        case KEYCODE_MEDIA_NEXT:
+        case KEYCODE_MEDIA_PREVIOUS:
+        case KEYCODE_MEDIA_REWIND:
+        case KEYCODE_MEDIA_FAST_FORWARD:
+        case KEYCODE_CAMERA:
+        case KEYCODE_FOCUS:
+        case KEYCODE_SEARCH:
+            return true;
+    }
+    
+    return false;
+}
+
+bool KeyEvent::isSystemKey() const {
+    return isSystemKey(getKeyCode());
+}
+
 void KeyEvent::initialize(
         int32_t deviceId,
         int32_t nature,
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index b3103a4..8f6d1fe 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -8,25 +8,25 @@
 //#define LOG_NDEBUG 0
 
 // Log detailed debug messages about each inbound event notification to the dispatcher.
-#define DEBUG_INBOUND_EVENT_DETAILS 1
+#define DEBUG_INBOUND_EVENT_DETAILS 0
 
 // Log detailed debug messages about each outbound event processed by the dispatcher.
-#define DEBUG_OUTBOUND_EVENT_DETAILS 1
+#define DEBUG_OUTBOUND_EVENT_DETAILS 0
 
 // Log debug messages about batching.
-#define DEBUG_BATCHING 1
+#define DEBUG_BATCHING 0
 
 // Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 1
+#define DEBUG_DISPATCH_CYCLE 0
 
 // Log debug messages about registrations.
-#define DEBUG_REGISTRATION 1
+#define DEBUG_REGISTRATION 0
 
 // Log debug messages about performance statistics.
-#define DEBUG_PERFORMANCE_STATISTICS 1
+#define DEBUG_PERFORMANCE_STATISTICS 0
 
 // Log debug messages about input event injection.
-#define DEBUG_INJECTION 1
+#define DEBUG_INJECTION 0
 
 #include <cutils/log.h>
 #include <ui/InputDispatcher.h>
@@ -232,6 +232,9 @@
     LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime);
 #endif
 
+    // Reset key repeating in case a keyboard device was added or removed or something.
+    resetKeyRepeatLocked();
+
     mLock.unlock();
 
     mPolicy->notifyConfigurationChanged(entry->eventTime);
@@ -249,9 +252,7 @@
             entry->downTime);
 #endif
 
-    // TODO: Poke user activity.
-
-    if (entry->action == KEY_EVENT_ACTION_DOWN) {
+    if (entry->action == KEY_EVENT_ACTION_DOWN && ! 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
@@ -277,14 +278,24 @@
 
 void InputDispatcher::processKeyRepeatLockedInterruptible(
         nsecs_t currentTime, nsecs_t keyRepeatTimeout) {
-    // TODO Old WindowManagerServer code sniffs the input queue for following key up
-    //      events and drops the repeat if one is found.  We should do something similar.
-    //      One good place to do it is in notifyKey as soon as the key up enters the
-    //      inbound event queue.
+    KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+
+    // Search the inbound queue for a key up corresponding to this device.
+    // It doesn't make sense to generate a key repeat event if the key is already up.
+    for (EventEntry* queuedEntry = mInboundQueue.head.next;
+            queuedEntry != & mInboundQueue.tail; queuedEntry = entry->next) {
+        if (queuedEntry->type == EventEntry::TYPE_KEY) {
+            KeyEntry* queuedKeyEntry = static_cast<KeyEntry*>(queuedEntry);
+            if (queuedKeyEntry->deviceId == entry->deviceId
+                    && entry->action == KEY_EVENT_ACTION_UP) {
+                resetKeyRepeatLocked();
+                return;
+            }
+        }
+    }
 
     // Synthesize a key repeat after the repeat timeout expired.
-    // We reuse the previous key entry if otherwise unreferenced.
-    KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+    // Reuse the repeated key entry if it is otherwise unreferenced.
     uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
     if (entry->refCount == 1) {
         entry->eventTime = currentTime;
@@ -366,7 +377,7 @@
             entry->downTime, entry->eventTime);
 
     mCurrentInputTargets.clear();
-    int32_t injectionResult = mPolicy->getKeyEventTargets(& mReusableKeyEvent,
+    int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent,
             entry->policyFlags, entry->injectorPid, entry->injectorUid,
             mCurrentInputTargets);
 
@@ -375,7 +386,9 @@
 
     setInjectionResultLocked(entry, injectionResult);
 
-    dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+    if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+        dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+    }
 }
 
 void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
@@ -395,7 +408,7 @@
             entry->firstSample.pointerCoords);
 
     mCurrentInputTargets.clear();
-    int32_t injectionResult = mPolicy->getMotionEventTargets(& mReusableMotionEvent,
+    int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent,
             entry->policyFlags, entry->injectorPid, entry->injectorUid,
             mCurrentInputTargets);
 
@@ -404,7 +417,9 @@
 
     setInjectionResultLocked(entry, injectionResult);
 
-    dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+    if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+        dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+    }
 }
 
 void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
@@ -514,7 +529,7 @@
                         connection->getInputChannelName());
             } else if (status == status_t(FAILED_TRANSACTION)) {
                 LOGD("channel '%s' ~ Could not append motion sample to currently "
-                        "dispatchedmove event because the event has already been consumed.  "
+                        "dispatched move event because the event has already been consumed.  "
                         "(Waiting for next dispatch cycle to start.)",
                         connection->getInputChannelName());
             } else {
@@ -1253,9 +1268,37 @@
     }
 }
 
+void InputDispatcher::preemptInputDispatch() {
+#if DEBUG_DISPATCH_CYCLE
+    LOGD("preemptInputDispatch");
+#endif
+
+    bool preemptedOne = false;
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        for (size_t i = 0; i < mActiveConnections.size(); i++) {
+            Connection* connection = mActiveConnections[i];
+            if (connection->hasPendingSyncTarget()) {
+#if DEBUG_DISPATCH_CYCLE
+                LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
+                        connection->getInputChannelName());
+#endif
+                connection->outboundQueue.tail.prev->targetFlags &= ~ InputTarget::FLAG_SYNC;
+                preemptedOne = true;
+            }
+        }
+    } // release lock
+
+    if (preemptedOne) {
+        // Wake up the poll loop so it can get a head start dispatching the next event.
+        mPollLoop->wake();
+    }
+}
+
 status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
 #if DEBUG_REGISTRATION
-    LOGD("channel '%s' - Registered", inputChannel->getName().string());
+    LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string());
 #endif
 
     int receiveFd;
@@ -1288,7 +1331,7 @@
 
 status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
 #if DEBUG_REGISTRATION
-    LOGD("channel '%s' - Unregistered", inputChannel->getName().string());
+    LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string());
 #endif
 
     int32_t receiveFd;
diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp
index 32c58b4..e1d15a4 100644
--- a/libs/ui/InputManager.cpp
+++ b/libs/ui/InputManager.cpp
@@ -85,6 +85,10 @@
     return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, sync, timeoutMillis);
 }
 
+void InputManager::preemptInputDispatch() {
+    mDispatcher->preemptInputDispatch();
+}
+
 void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const {
     mReader->getCurrentInputConfiguration(outConfiguration);
 }
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 1824054..899027c 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -11,13 +11,13 @@
 #define DEBUG_RAW_EVENTS 0
 
 // Log debug messages about touch screen filtering hacks.
-#define DEBUG_HACKS 1
+#define DEBUG_HACKS 0
 
 // Log debug messages about virtual key processing.
-#define DEBUG_VIRTUAL_KEYS 1
+#define DEBUG_VIRTUAL_KEYS 0
 
 // Log debug messages about pointers.
-#define DEBUG_POINTERS 1
+#define DEBUG_POINTERS 0
 
 // Log debug messages about pointer assignment calculations.
 #define DEBUG_POINTER_ASSIGNMENT 0
@@ -387,6 +387,11 @@
  * points has moved more than a screen height from the last position,
  * then drop it. */
 bool InputDevice::TouchScreenState::applyBadTouchFilter() {
+    // This hack requires valid axis parameters.
+    if (! parameters.yAxis.valid) {
+        return false;
+    }
+
     uint32_t pointerCount = currentTouch.pointerCount;
 
     // Nothing to do if there are no points.
@@ -466,6 +471,11 @@
  * the coordinate value for one axis has jumped to the other pointer's location.
  */
 bool InputDevice::TouchScreenState::applyJumpyTouchFilter() {
+    // This hack requires valid axis parameters.
+    if (! parameters.yAxis.valid) {
+        return false;
+    }
+
     uint32_t pointerCount = currentTouch.pointerCount;
     if (lastTouch.pointerCount != pointerCount) {
 #if DEBUG_HACKS
@@ -630,7 +640,8 @@
         int32_t pressure = currentTouch.pointers[currentIndex].pressure;
 
         if (lastTouch.idBits.hasBit(id)) {
-            // Pointer still down compute average.
+            // Pointer was down before and is still down now.
+            // Compute average over history trace.
             uint32_t start = averagingTouchFilter.historyStart[id];
             uint32_t end = averagingTouchFilter.historyEnd[id];
 
@@ -644,11 +655,15 @@
 #endif
 
             if (distance < AVERAGING_DISTANCE_LIMIT) {
+                // Increment end index in preparation for recording new historical data.
                 end += 1;
                 if (end > AVERAGING_HISTORY_SIZE) {
                     end = 0;
                 }
 
+                // If the end index has looped back to the start index then we have filled
+                // the historical trace up to the desired size so we drop the historical
+                // data at the start of the trace.
                 if (end == start) {
                     start += 1;
                     if (start > AVERAGING_HISTORY_SIZE) {
@@ -656,23 +671,25 @@
                     }
                 }
 
+                // Add the raw data to the historical trace.
                 averagingTouchFilter.historyStart[id] = start;
                 averagingTouchFilter.historyEnd[id] = end;
                 averagingTouchFilter.historyData[end].pointers[id].x = x;
                 averagingTouchFilter.historyData[end].pointers[id].y = y;
                 averagingTouchFilter.historyData[end].pointers[id].pressure = pressure;
 
+                // Average over all historical positions in the trace by total pressure.
                 int32_t averagedX = 0;
                 int32_t averagedY = 0;
                 int32_t totalPressure = 0;
                 for (;;) {
                     int32_t historicalX = averagingTouchFilter.historyData[start].pointers[id].x;
-                    int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].x;
+                    int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].y;
                     int32_t historicalPressure = averagingTouchFilter.historyData[start]
                             .pointers[id].pressure;
 
-                    averagedX += historicalX;
-                    averagedY += historicalY;
+                    averagedX += historicalX * historicalPressure;
+                    averagedY += historicalY * historicalPressure;
                     totalPressure += historicalPressure;
 
                     if (start == end) {
@@ -717,6 +734,12 @@
 }
 
 bool InputDevice::TouchScreenState::isPointInsideDisplay(int32_t x, int32_t y) const {
+    if (! parameters.xAxis.valid || ! parameters.yAxis.valid) {
+        // Assume all points on a touch screen without valid axis parameters are
+        // inside the display.
+        return true;
+    }
+
     return x >= parameters.xAxis.minValue
         && x <= parameters.xAxis.maxValue
         && y >= parameters.yAxis.minValue
@@ -1144,12 +1167,6 @@
 
 void InputReader::onSingleTouchScreenStateChanged(nsecs_t when,
         InputDevice* device) {
-    static const uint32_t POSITION_FIELDS =
-            InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X
-            | InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y
-            | InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE
-            | InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH;
-
     /* Refresh display properties so we can map touch screen coords into display coords */
 
     if (! refreshDisplayProperties()) {
@@ -1167,10 +1184,19 @@
         in->current.down = in->accumulator.btnTouch;
     }
 
-    if ((fields & POSITION_FIELDS) == POSITION_FIELDS) {
+    if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X) {
         in->current.x = in->accumulator.absX;
+    }
+
+    if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y) {
         in->current.y = in->accumulator.absY;
+    }
+
+    if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE) {
         in->current.pressure = in->accumulator.absPressure;
+    }
+
+    if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH) {
         in->current.size = in->accumulator.absToolWidth;
     }
 
@@ -1323,18 +1349,23 @@
 void InputReader::dispatchVirtualKey(nsecs_t when,
         InputDevice* device, uint32_t policyFlags,
         int32_t keyEventAction, int32_t keyEventFlags) {
+    updateExportedVirtualKeyState();
+
     int32_t keyCode = device->touchScreen.currentVirtualKey.keyCode;
     int32_t scanCode = device->touchScreen.currentVirtualKey.scanCode;
     nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime;
     int32_t metaState = globalMetaState();
 
-    updateExportedVirtualKeyState();
-
     mPolicy->virtualKeyFeedback(when, device->id, keyEventAction, keyEventFlags,
             keyCode, scanCode, metaState, downTime);
 
-    mDispatcher->notifyKey(when, device->id, INPUT_EVENT_NATURE_KEY, policyFlags,
-            keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+    int32_t policyActions = mPolicy->interceptKey(when, device->id,
+            keyEventAction == KEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
+
+    if (applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) {
+        mDispatcher->notifyKey(when, device->id, INPUT_EVENT_NATURE_KEY, policyFlags,
+                keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+    }
 }
 
 void InputReader::dispatchTouches(nsecs_t when,
@@ -1420,6 +1451,9 @@
     int32_t pointerIds[MAX_POINTERS];
     PointerCoords pointerCoords[MAX_POINTERS];
 
+    const InputDevice::TouchScreenState::Precalculated& precalculated =
+            device->touchScreen.precalculated;
+
     // Walk through the the active pointers and map touch screen coordinates (TouchData) into
     // display coordinates (PointerCoords) and adjust for display orientation.
     while (! idBits.isEmpty()) {
@@ -1427,18 +1461,14 @@
         idBits.clearBit(id);
         uint32_t index = touch->idToIndex[id];
 
-        float x = (float(touch->pointers[index].x)
-                        - device->touchScreen.parameters.xAxis.minValue)
-                * device->touchScreen.precalculated.xScale;
-        float y = (float(touch->pointers[index].y)
-                        - device->touchScreen.parameters.yAxis.minValue)
-                * device->touchScreen.precalculated.yScale;
-        float pressure = (float(touch->pointers[index].pressure)
-                        - device->touchScreen.parameters.pressureAxis.minValue)
-                * device->touchScreen.precalculated.pressureScale;
-        float size = (float(touch->pointers[index].size)
-                        - device->touchScreen.parameters.sizeAxis.minValue)
-                * device->touchScreen.precalculated.sizeScale;
+        float x = float(touch->pointers[index].x
+                - precalculated.xOrigin) * precalculated.xScale;
+        float y = float(touch->pointers[index].y
+                - precalculated.yOrigin) * precalculated.yScale;
+        float pressure = float(touch->pointers[index].pressure
+                - precalculated.pressureOrigin) * precalculated.pressureScale;
+        float size = float(touch->pointers[index].size
+                - precalculated.sizeOrigin) * precalculated.sizeScale;
 
         switch (mDisplayOrientation) {
         case InputReaderPolicyInterface::ROTATION_90: {
@@ -1632,7 +1662,11 @@
             }
         }
 
-        mDisplayOrientation = newOrientation;
+        if (newOrientation != mDisplayOrientation) {
+            LOGD("Display orientation changed to %d", mDisplayOrientation);
+
+            mDisplayOrientation = newOrientation;
+        }
         return true;
     } else {
         resetDisplayProperties();
@@ -1721,10 +1755,25 @@
         device->touchScreen.parameters.useJumpyTouchFilter =
                 mPolicy->filterJumpyTouchEvents();
 
-        device->touchScreen.precalculated.pressureScale =
-                1.0f / device->touchScreen.parameters.pressureAxis.range;
-        device->touchScreen.precalculated.sizeScale =
-                1.0f / device->touchScreen.parameters.sizeAxis.range;
+        if (device->touchScreen.parameters.pressureAxis.valid) {
+            device->touchScreen.precalculated.pressureOrigin =
+                    device->touchScreen.parameters.pressureAxis.minValue;
+            device->touchScreen.precalculated.pressureScale =
+                    1.0f / device->touchScreen.parameters.pressureAxis.range;
+        } else {
+            device->touchScreen.precalculated.pressureOrigin = 0;
+            device->touchScreen.precalculated.pressureScale = 1.0f;
+        }
+
+        if (device->touchScreen.parameters.sizeAxis.valid) {
+            device->touchScreen.precalculated.sizeOrigin =
+                    device->touchScreen.parameters.sizeAxis.minValue;
+            device->touchScreen.precalculated.sizeScale =
+                    1.0f / device->touchScreen.parameters.sizeAxis.range;
+        } else {
+            device->touchScreen.precalculated.sizeOrigin = 0;
+            device->touchScreen.precalculated.sizeScale = 1.0f;
+        }
     }
 
     if (device->isTrackball()) {
@@ -1739,22 +1788,42 @@
 
 void InputReader::configureDeviceForCurrentDisplaySize(InputDevice* device) {
     if (device->isTouchScreen()) {
-        if (mDisplayWidth < 0) {
-            LOGD("Skipping part of touch screen configuration since display size is unknown.");
-        } else {
-            LOGI("Device configured: id=0x%x, name=%s (display size was changed)", device->id,
-                    device->name.string());
-            configureVirtualKeys(device);
+        if (device->touchScreen.parameters.xAxis.valid
+                && device->touchScreen.parameters.yAxis.valid) {
+            device->touchScreen.precalculated.xOrigin =
+                    device->touchScreen.parameters.xAxis.minValue;
+            device->touchScreen.precalculated.yOrigin =
+                    device->touchScreen.parameters.yAxis.minValue;
 
-            device->touchScreen.precalculated.xScale =
-                    float(mDisplayWidth) / device->touchScreen.parameters.xAxis.range;
-            device->touchScreen.precalculated.yScale =
-                    float(mDisplayHeight) / device->touchScreen.parameters.yAxis.range;
+            if (mDisplayWidth < 0) {
+                LOGD("Skipping part of touch screen configuration since display size is unknown.");
+
+                device->touchScreen.precalculated.xScale = 1.0f;
+                device->touchScreen.precalculated.yScale = 1.0f;
+            } else {
+                LOGI("Device configured: id=0x%x, name=%s (display size was changed)", device->id,
+                        device->name.string());
+
+                device->touchScreen.precalculated.xScale =
+                        float(mDisplayWidth) / device->touchScreen.parameters.xAxis.range;
+                device->touchScreen.precalculated.yScale =
+                        float(mDisplayHeight) / device->touchScreen.parameters.yAxis.range;
+
+                configureVirtualKeys(device);
+            }
+        } else {
+            device->touchScreen.precalculated.xOrigin = 0;
+            device->touchScreen.precalculated.xScale = 1.0f;
+            device->touchScreen.precalculated.yOrigin = 0;
+            device->touchScreen.precalculated.yScale = 1.0f;
         }
     }
 }
 
 void InputReader::configureVirtualKeys(InputDevice* device) {
+    assert(device->touchScreen.parameters.xAxis.valid
+            && device->touchScreen.parameters.yAxis.valid);
+
     device->touchScreen.virtualKeys.clear();
 
     Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions;
@@ -1818,16 +1887,18 @@
         if (out->range != 0) {
             LOGI("  %s: min=%d max=%d flat=%d fuzz=%d",
                     name, out->minValue, out->maxValue, out->flat, out->fuzz);
+            out->valid = true;
             return;
         }
     }
 
+    out->valid = false;
     out->minValue = 0;
     out->maxValue = 0;
     out->flat = 0;
     out->fuzz = 0;
     out->range = 0;
-    LOGI("  %s: unknown axis values, setting to zero", name);
+    LOGI("  %s: unknown axis values, marking as invalid", name);
 }
 
 void InputReader::configureExcludedDevices() {
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index b2842d0..25def3c 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -430,10 +430,12 @@
             reinterpret_cast<char*>(mSharedMessage);
 
     if (newBytesUsed > mAshmemSize) {
+#if DEBUG_TRANSPORT_ACTIONS
         LOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory "
                 "buffer is full.  Buffer size: %d bytes, pointers: %d, samples: %d",
                 mChannel->getName().string(),
                 mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount);
+#endif
         return NO_MEMORY;
     }
 
@@ -444,8 +446,10 @@
             if (errno == EAGAIN) {
                 // Only possible source of contention is the consumer having consumed (or being in the
                 // process of consuming) the message and left the semaphore count at 0.
+#if DEBUG_TRANSPORT_ACTIONS
                 LOGD("channel '%s' publisher ~ Cannot append motion sample because the message has "
                         "already been consumed.", mChannel->getName().string());
+#endif
                 return FAILED_TRANSACTION;
             } else {
                 LOGE("channel '%s' publisher ~ Error %d in sem_trywait.",
@@ -687,7 +691,7 @@
 
 } // namespace android
 
-// --- input_queue_t ---
+// --- AInputQueue ---
 
 using android::InputEvent;
 using android::InputChannel;
@@ -695,13 +699,13 @@
 using android::sp;
 using android::status_t;
 
-input_queue_t::input_queue_t(const sp<InputChannel>& channel) :
+AInputQueue::AInputQueue(const sp<InputChannel>& channel) :
         mConsumer(channel) {
 }
 
-input_queue_t::~input_queue_t() {
+AInputQueue::~AInputQueue() {
 }
 
-status_t input_queue_t::consume(InputEvent** event) {
+status_t AInputQueue::consume(InputEvent** event) {
     return mConsumer.consume(&mInputEventFactory, event);
 }
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index c9a5950..b205418 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -59,16 +59,13 @@
     // YUV format from the HAL are handled here
     switch (format) {
     case HAL_PIXEL_FORMAT_YCbCr_422_SP:
-    case HAL_PIXEL_FORMAT_YCrCb_422_SP:
-    case HAL_PIXEL_FORMAT_YCbCr_422_P:
     case HAL_PIXEL_FORMAT_YCbCr_422_I:
-    case HAL_PIXEL_FORMAT_CbYCrY_422_I:
+    case HAL_PIXEL_FORMAT_YV16:
         info->bitsPerPixel = 16;
         goto done;
-    case HAL_PIXEL_FORMAT_YCbCr_420_SP:
     case HAL_PIXEL_FORMAT_YCrCb_420_SP:
     case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
-    case HAL_PIXEL_FORMAT_YCbCr_420_P:
+    case HAL_PIXEL_FORMAT_YV12:
         info->bitsPerPixel = 12;
      done:
         info->format = format;
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 50f0674..9212708 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import java.util.NoSuchElementException;
 import android.app.ActivityManagerNative;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -1016,7 +1017,11 @@
                 } else {
                     mStartcount--;
                     if (mStartcount == 0) {
-                        mCb.unlinkToDeath(this, 0);
+                        try {
+                            mCb.unlinkToDeath(this, 0);
+                        } catch (NoSuchElementException e) {
+                            Log.w(TAG, "decCount() going to 0 but not registered to binder");
+                        }
                     }
                     requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
                 }
@@ -1025,8 +1030,14 @@
 
         public void clearCount(boolean stopSco) {
             synchronized(mScoClients) {
+                if (mStartcount != 0) {
+                    try {
+                        mCb.unlinkToDeath(this, 0);
+                    } catch (NoSuchElementException e) {
+                        Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
+                    }
+                }
                 mStartcount = 0;
-                mCb.unlinkToDeath(this, 0);
                 if (stopSco) {
                     requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
                 }
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index 64d6460..a27df57 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -119,15 +119,26 @@
     public int audioChannels;
 
     /**
-     * Returns the camcorder profile for the given quality level.
+     * Returns the camcorder profile for the default camera at the given
+     * quality level.
      * @param quality the target quality level for the camcorder profile
      */
     public static CamcorderProfile get(int quality) {
+        return get(0, quality);
+    }
+
+    /**
+     * Returns the camcorder profile for the given camera at the given
+     * quality level.
+     * @param cameraId the id for the camera
+     * @param quality the target quality level for the camcorder profile
+     */
+    public static CamcorderProfile get(int cameraId, int quality) {
         if (quality < QUALITY_LOW || quality > QUALITY_HIGH) {
             String errMessage = "Unsupported quality level: " + quality;
             throw new IllegalArgumentException(errMessage);
         }
-        return native_get_camcorder_profile(quality);
+        return native_get_camcorder_profile(cameraId, quality);
     }
 
     static {
@@ -165,5 +176,6 @@
 
     // Methods implemented by JNI
     private static native final void native_init();
-    private static native final CamcorderProfile native_get_camcorder_profile(int quality);
+    private static native final CamcorderProfile native_get_camcorder_profile(
+            int cameraId, int quality);
 }
diff --git a/media/java/android/media/CameraProfile.java b/media/java/android/media/CameraProfile.java
index f8d3935..6a0be08 100644
--- a/media/java/android/media/CameraProfile.java
+++ b/media/java/android/media/CameraProfile.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import java.util.Arrays;
+import java.util.HashMap;
 
 /**
  * The CameraProfile class is used to retrieve the pre-defined still image
@@ -40,36 +41,55 @@
     /*
      * Cache the Jpeg encoding quality parameters
      */
-    private static final int[] sJpegEncodingQualityParameters;
+    private static final HashMap<Integer, int[]> sCache = new HashMap<Integer, int[]>();
 
     /**
      * Returns a pre-defined still image capture (jpeg) quality level
-     * used for the given quality level in the Camera application.
+     * used for the given quality level in the Camera application for
+     * the default camera.
      *
      * @param quality The target quality level
      */
     public static int getJpegEncodingQualityParameter(int quality) {
+        return getJpegEncodingQualityParameter(0, quality);
+    }
+
+    /**
+     * Returns a pre-defined still image capture (jpeg) quality level
+     * used for the given quality level in the Camera application for
+     * the specified camera.
+     *
+     * @param cameraId The id of the camera
+     * @param quality The target quality level
+     */
+    public static int getJpegEncodingQualityParameter(int cameraId, int quality) {
         if (quality < QUALITY_LOW || quality > QUALITY_HIGH) {
             throw new IllegalArgumentException("Unsupported quality level: " + quality);
         }
-        return sJpegEncodingQualityParameters[quality];
+        synchronized (sCache) {
+            int[] levels = sCache.get(cameraId);
+            if (levels == null) {
+                levels = getImageEncodingQualityLevels(cameraId);
+                sCache.put(cameraId, levels);
+            }
+            return levels[quality];
+        }
     }
 
     static {
         System.loadLibrary("media_jni");
         native_init();
-        sJpegEncodingQualityParameters = getImageEncodingQualityLevels();
     }
 
-    private static int[] getImageEncodingQualityLevels() {
-        int nLevels = native_get_num_image_encoding_quality_levels();
+    private static int[] getImageEncodingQualityLevels(int cameraId) {
+        int nLevels = native_get_num_image_encoding_quality_levels(cameraId);
         if (nLevels != QUALITY_HIGH + 1) {
             throw new RuntimeException("Unexpected Jpeg encoding quality levels " + nLevels);
         }
 
         int[] levels = new int[nLevels];
         for (int i = 0; i < nLevels; ++i) {
-            levels[i] = native_get_image_encoding_quality_level(i);
+            levels[i] = native_get_image_encoding_quality_level(cameraId, i);
         }
         Arrays.sort(levels);  // Lower quality level ALWAYS comes before higher one
         return levels;
@@ -77,6 +97,6 @@
 
     // Methods implemented by JNI
     private static native final void native_init();
-    private static native final int native_get_num_image_encoding_quality_levels();
-    private static native final int native_get_image_encoding_quality_level(int index);
+    private static native final int native_get_num_image_encoding_quality_levels(int cameraId);
+    private static native final int native_get_image_encoding_quality_level(int cameraId, int index);
 }
diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp
index 7d7533a..cce9fd0 100644
--- a/media/jni/android_media_MediaProfiles.cpp
+++ b/media/jni/android_media_MediaProfiles.cpp
@@ -162,26 +162,26 @@
 }
 
 static jobject
-android_media_MediaProfiles_native_get_camcorder_profile(JNIEnv *env, jobject thiz, jint quality)
+android_media_MediaProfiles_native_get_camcorder_profile(JNIEnv *env, jobject thiz, jint id, jint quality)
 {
-    LOGV("native_get_camcorder_profile: %d", quality);
+    LOGV("native_get_camcorder_profile: %d %d", id, quality);
     if (quality != CAMCORDER_QUALITY_HIGH && quality != CAMCORDER_QUALITY_LOW) {
         jniThrowException(env, "java/lang/RuntimeException", "Unknown camcorder profile quality");
         return NULL;
     }
 
     camcorder_quality q = static_cast<camcorder_quality>(quality);
-    int duration         = sProfiles->getCamcorderProfileParamByName("duration", q);
-    int fileFormat       = sProfiles->getCamcorderProfileParamByName("file.format", q);
-    int videoCodec       = sProfiles->getCamcorderProfileParamByName("vid.codec",   q);
-    int videoBitRate     = sProfiles->getCamcorderProfileParamByName("vid.bps",     q);
-    int videoFrameRate   = sProfiles->getCamcorderProfileParamByName("vid.fps",     q);
-    int videoFrameWidth  = sProfiles->getCamcorderProfileParamByName("vid.width",   q);
-    int videoFrameHeight = sProfiles->getCamcorderProfileParamByName("vid.height",  q);
-    int audioCodec       = sProfiles->getCamcorderProfileParamByName("aud.codec",   q);
-    int audioBitRate     = sProfiles->getCamcorderProfileParamByName("aud.bps",     q);
-    int audioSampleRate  = sProfiles->getCamcorderProfileParamByName("aud.hz",      q);
-    int audioChannels    = sProfiles->getCamcorderProfileParamByName("aud.ch",      q);
+    int duration         = sProfiles->getCamcorderProfileParamByName("duration",    id, q);
+    int fileFormat       = sProfiles->getCamcorderProfileParamByName("file.format", id, q);
+    int videoCodec       = sProfiles->getCamcorderProfileParamByName("vid.codec",   id, q);
+    int videoBitRate     = sProfiles->getCamcorderProfileParamByName("vid.bps",     id, q);
+    int videoFrameRate   = sProfiles->getCamcorderProfileParamByName("vid.fps",     id, q);
+    int videoFrameWidth  = sProfiles->getCamcorderProfileParamByName("vid.width",   id, q);
+    int videoFrameHeight = sProfiles->getCamcorderProfileParamByName("vid.height",  id, q);
+    int audioCodec       = sProfiles->getCamcorderProfileParamByName("aud.codec",   id, q);
+    int audioBitRate     = sProfiles->getCamcorderProfileParamByName("aud.bps",     id, q);
+    int audioSampleRate  = sProfiles->getCamcorderProfileParamByName("aud.hz",      id, q);
+    int audioChannels    = sProfiles->getCamcorderProfileParamByName("aud.ch",      id, q);
 
     // Check on the values retrieved
     if (duration == -1 || fileFormat == -1 || videoCodec == -1 || audioCodec == -1 ||
@@ -253,17 +253,17 @@
 }
 
 static jint
-android_media_MediaProfiles_native_get_num_image_encoding_quality_levels(JNIEnv *env, jobject thiz)
+android_media_MediaProfiles_native_get_num_image_encoding_quality_levels(JNIEnv *env, jobject thiz, jint cameraId)
 {
     LOGV("native_get_num_image_encoding_quality_levels");
-    return sProfiles->getImageEncodingQualityLevels().size();
+    return sProfiles->getImageEncodingQualityLevels(cameraId).size();
 }
 
 static jint
-android_media_MediaProfiles_native_get_image_encoding_quality_level(JNIEnv *env, jobject thiz, jint index)
+android_media_MediaProfiles_native_get_image_encoding_quality_level(JNIEnv *env, jobject thiz, jint cameraId, jint index)
 {
     LOGV("native_get_image_encoding_quality_level");
-    Vector<int> levels = sProfiles->getImageEncodingQualityLevels();
+    Vector<int> levels = sProfiles->getImageEncodingQualityLevels(cameraId);
     if (index < 0 || index >= levels.size()) {
         jniThrowException(env, "java/lang/IllegalArgumentException", "out of array boundary");
         return -1;
@@ -287,7 +287,7 @@
 
 static JNINativeMethod gMethodsForCamcorderProfileClass[] = {
     {"native_init",                            "()V",                    (void *)android_media_MediaProfiles_native_init},
-    {"native_get_camcorder_profile",           "(I)Landroid/media/CamcorderProfile;",
+    {"native_get_camcorder_profile",           "(II)Landroid/media/CamcorderProfile;",
                                                                          (void *)android_media_MediaProfiles_native_get_camcorder_profile},
 };
 
@@ -302,8 +302,8 @@
 static JNINativeMethod gMethodsForCameraProfileClass[] = {
     {"native_init",                            "()V",                    (void *)android_media_MediaProfiles_native_init},
     {"native_get_num_image_encoding_quality_levels",
-                                               "()I",                    (void *)android_media_MediaProfiles_native_get_num_image_encoding_quality_levels},
-    {"native_get_image_encoding_quality_level","(I)I",                   (void *)android_media_MediaProfiles_native_get_image_encoding_quality_level},
+                                               "(I)I",                   (void *)android_media_MediaProfiles_native_get_num_image_encoding_quality_levels},
+    {"native_get_image_encoding_quality_level","(II)I",                   (void *)android_media_MediaProfiles_native_get_image_encoding_quality_level},
 };
 
 static const char* const kEncoderCapabilitiesClassPathName = "android/media/EncoderCapabilities";
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 7908f5d..de9e51d 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -12,6 +12,7 @@
     mediaplayer.cpp \
     IMediaPlayerService.cpp \
     IMediaPlayerClient.cpp \
+    IMediaRecorderClient.cpp \
     IMediaPlayer.cpp \
     IMediaRecorder.cpp \
     Metadata.cpp \
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 2bc2a7e..9fe207c 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -21,7 +21,7 @@
 #include <binder/Parcel.h>
 #include <surfaceflinger/ISurface.h>
 #include <camera/ICamera.h>
-#include <media/IMediaPlayerClient.h>
+#include <media/IMediaRecorderClient.h>
 #include <media/IMediaRecorder.h>
 
 namespace android {
@@ -189,7 +189,7 @@
         return reply.readInt32();
     }
 
-    status_t setListener(const sp<IMediaPlayerClient>& listener)
+    status_t setListener(const sp<IMediaRecorderClient>& listener)
     {
         LOGV("setListener(%p)", listener.get());
         Parcel data, reply;
@@ -399,8 +399,8 @@
         case SET_LISTENER: {
             LOGV("SET_LISTENER");
             CHECK_INTERFACE(IMediaRecorder, data, reply);
-            sp<IMediaPlayerClient> listener =
-                interface_cast<IMediaPlayerClient>(data.readStrongBinder());
+            sp<IMediaRecorderClient> listener =
+                interface_cast<IMediaRecorderClient>(data.readStrongBinder());
             reply->writeInt32(setListener(listener));
             return NO_ERROR;
         } break;
diff --git a/media/libmedia/IMediaRecorderClient.cpp b/media/libmedia/IMediaRecorderClient.cpp
new file mode 100644
index 0000000..ff235c9
--- /dev/null
+++ b/media/libmedia/IMediaRecorderClient.cpp
@@ -0,0 +1,70 @@
+/*
+**
+** Copyright 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 <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+#include <media/IMediaRecorderClient.h>
+
+namespace android {
+
+enum {
+    NOTIFY = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BpMediaRecorderClient: public BpInterface<IMediaRecorderClient>
+{
+public:
+    BpMediaRecorderClient(const sp<IBinder>& impl)
+        : BpInterface<IMediaRecorderClient>(impl)
+    {
+    }
+
+    virtual void notify(int msg, int ext1, int ext2)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaRecorderClient::getInterfaceDescriptor());
+        data.writeInt32(msg);
+        data.writeInt32(ext1);
+        data.writeInt32(ext2);
+        remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(MediaRecorderClient, "android.media.IMediaRecorderClient");
+
+// ----------------------------------------------------------------------
+
+status_t BnMediaRecorderClient::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case NOTIFY: {
+            CHECK_INTERFACE(IMediaRecorderClient, data, reply);
+            int msg = data.readInt32();
+            int ext1 = data.readInt32();
+            int ext2 = data.readInt32();
+            notify(msg, ext1, ext2);
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+}; // namespace android
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 1263373..3869389 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -272,7 +272,7 @@
 }
 
 /*static*/ MediaProfiles::CamcorderProfile*
-MediaProfiles::createCamcorderProfile(const char **atts)
+MediaProfiles::createCamcorderProfile(int cameraId, const char **atts)
 {
     CHECK(!strcmp("quality",    atts[0]) &&
           !strcmp("fileFormat", atts[2]) &&
@@ -287,16 +287,47 @@
     CHECK(fileFormat != -1);
 
     MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
+    profile->mCameraId = cameraId;
     profile->mFileFormat = static_cast<output_format>(fileFormat);
     profile->mQuality = static_cast<camcorder_quality>(quality);
     profile->mDuration = atoi(atts[5]);
     return profile;
 }
 
-/*static*/ int
-MediaProfiles::getImageEncodingQualityLevel(const char** atts)
+MediaProfiles::ImageEncodingQualityLevels*
+MediaProfiles::findImageEncodingQualityLevels(int cameraId) const
+{
+    int n = mImageEncodingQualityLevels.size();
+    for (int i = 0; i < n; i++) {
+        ImageEncodingQualityLevels *levels = mImageEncodingQualityLevels[i];
+        if (levels->mCameraId == cameraId) {
+            return levels;
+        }
+    }
+    return NULL;
+}
+
+void MediaProfiles::addImageEncodingQualityLevel(int cameraId, const char** atts)
 {
     CHECK(!strcmp("quality", atts[0]));
+    int quality = atoi(atts[1]);
+    LOGV("%s: cameraId=%d, quality=%d\n", __func__, cameraId, quality);
+    ImageEncodingQualityLevels *levels = findImageEncodingQualityLevels(cameraId);
+
+    if (levels == NULL) {
+        levels = new ImageEncodingQualityLevels();
+        levels->mCameraId = cameraId;
+        mImageEncodingQualityLevels.add(levels);
+    }
+
+    levels->mLevels.add(quality);
+}
+
+/*static*/ int
+MediaProfiles::getCameraId(const char** atts)
+{
+    if (!atts[0]) return 0;  // default cameraId = 0
+    CHECK(!strcmp("cameraId", atts[0]));
     return atoi(atts[1]);
 }
 
@@ -322,10 +353,13 @@
         profiles->mAudioDecoders.add(createAudioDecoderCap(atts));
     } else if (strcmp("EncoderOutputFileFormat", name) == 0) {
         profiles->mEncoderOutputFileFormats.add(createEncoderOutputFileFormat(atts));
+    } else if (strcmp("CamcorderProfiles", name) == 0) {
+        profiles->mCurrentCameraId = getCameraId(atts);
     } else if (strcmp("EncoderProfile", name) == 0) {
-        profiles->mCamcorderProfiles.add(createCamcorderProfile(atts));
+        profiles->mCamcorderProfiles.add(
+            createCamcorderProfile(profiles->mCurrentCameraId, atts));
     } else if (strcmp("ImageEncoding", name) == 0) {
-        profiles->mImageEncodingQualityLevels.add(getImageEncodingQualityLevel(atts));
+        profiles->addImageEncodingQualityLevel(profiles->mCurrentCameraId, atts);
     }
 }
 
@@ -383,7 +417,8 @@
         new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 360000, 352, 288, 20);
 
     AudioCodec *audioCodec = new AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
-    CamcorderProfile *profile = new CamcorderProfile;
+    CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
+    profile->mCameraId = 0;
     profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
     profile->mQuality = CAMCORDER_QUALITY_HIGH;
     profile->mDuration = 60;
@@ -402,6 +437,7 @@
         new MediaProfiles::AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
 
     MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
+    profile->mCameraId = 0;
     profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
     profile->mQuality = CAMCORDER_QUALITY_LOW;
     profile->mDuration = 30;
@@ -458,9 +494,12 @@
 /*static*/ void
 MediaProfiles::createDefaultImageEncodingQualityLevels(MediaProfiles *profiles)
 {
-    profiles->mImageEncodingQualityLevels.add(70);
-    profiles->mImageEncodingQualityLevels.add(80);
-    profiles->mImageEncodingQualityLevels.add(90);
+    ImageEncodingQualityLevels *levels = new ImageEncodingQualityLevels();
+    levels->mCameraId = 0;
+    levels->mLevels.add(70);
+    levels->mLevels.add(80);
+    levels->mLevels.add(90);
+    profiles->mImageEncodingQualityLevels.add(levels);
 }
 
 /*static*/ MediaProfiles*
@@ -629,19 +668,24 @@
     return decoders;  // copy out
 }
 
-int MediaProfiles::getCamcorderProfileParamByName(const char *name, camcorder_quality quality) const
+int MediaProfiles::getCamcorderProfileParamByName(const char *name,
+                                                  int cameraId,
+                                                  camcorder_quality quality) const
 {
-    LOGV("getCamcorderProfileParamByName: %s for quality %d", name, quality);
+    LOGV("getCamcorderProfileParamByName: %s for camera %d, quality %d",
+         name, cameraId, quality);
 
     int index = -1;
     for (size_t i = 0, n = mCamcorderProfiles.size(); i < n; ++i) {
-        if (mCamcorderProfiles[i]->mQuality == quality) {
+        if (mCamcorderProfiles[i]->mCameraId == cameraId &&
+            mCamcorderProfiles[i]->mQuality == quality) {
             index = i;
             break;
         }
     }
     if (index == -1) {
-        LOGE("The given camcorder profile quality %d is not found", quality);
+        LOGE("The given camcorder profile camera %d quality %d is not found",
+             cameraId, quality);
         return -1;
     }
 
@@ -657,13 +701,18 @@
     if (!strcmp("aud.ch", name)) return mCamcorderProfiles[index]->mAudioCodec->mChannels;
     if (!strcmp("aud.hz", name)) return mCamcorderProfiles[index]->mAudioCodec->mSampleRate;
 
-    LOGE("The given camcorder profile param name %s is not found", name);
+    LOGE("The given camcorder profile param id %d name %s is not found", cameraId, name);
     return -1;
 }
 
-Vector<int> MediaProfiles::getImageEncodingQualityLevels() const
+Vector<int> MediaProfiles::getImageEncodingQualityLevels(int cameraId) const
 {
-    return mImageEncodingQualityLevels;  // copy out
+    Vector<int> result;
+    ImageEncodingQualityLevels *levels = findImageEncodingQualityLevels(cameraId);
+    if (levels != NULL) {
+        result = levels->mLevels;  // copy out
+    }
+    return result;
 }
 
 MediaProfiles::~MediaProfiles()
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 084f039..80b1cfd 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -318,7 +318,7 @@
     release();
 }
 
-status_t MediaRecorderClient::setListener(const sp<IMediaPlayerClient>& listener)
+status_t MediaRecorderClient::setListener(const sp<IMediaRecorderClient>& listener)
 {
     LOGV("setListener");
     Mutex::Autolock lock(mLock);
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index e07306b..b53d950 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -40,7 +40,7 @@
     virtual     status_t        setVideoSize(int width, int height);
     virtual     status_t        setVideoFrameRate(int frames_per_second);
     virtual     status_t        setParameters(const String8& params);
-    virtual     status_t        setListener(const sp<IMediaPlayerClient>& listener);
+    virtual     status_t        setListener(const sp<IMediaRecorderClient>& listener);
     virtual     status_t        prepare();
     virtual     status_t        getMaxAmplitude(int* max);
     virtual     status_t        start();
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 1e20f7e..91c5b92 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -42,11 +42,16 @@
 
 namespace android {
 
-StagefrightRecorder::StagefrightRecorder() {
+StagefrightRecorder::StagefrightRecorder()
+    : mWriter(NULL),
+      mOutputFd(-1) {
+
+    LOGV("Constructor");
     reset();
 }
 
 StagefrightRecorder::~StagefrightRecorder() {
+    LOGV("Destructor");
     stop();
 
     if (mOutputFd >= 0) {
@@ -56,40 +61,97 @@
 }
 
 status_t StagefrightRecorder::init() {
+    LOGV("init");
     return OK;
 }
 
 status_t StagefrightRecorder::setAudioSource(audio_source as) {
-    mAudioSource = as;
+    LOGV("setAudioSource: %d", as);
+    if (as < AUDIO_SOURCE_DEFAULT ||
+        as >= AUDIO_SOURCE_LIST_END) {
+        LOGE("Invalid audio source: %d", as);
+        return BAD_VALUE;
+    }
+
+    if (as == AUDIO_SOURCE_DEFAULT) {
+        mAudioSource = AUDIO_SOURCE_MIC;
+    } else {
+        mAudioSource = as;
+    }
 
     return OK;
 }
 
 status_t StagefrightRecorder::setVideoSource(video_source vs) {
-    mVideoSource = vs;
+    LOGV("setVideoSource: %d", vs);
+    if (vs < VIDEO_SOURCE_DEFAULT ||
+        vs >= VIDEO_SOURCE_LIST_END) {
+        LOGE("Invalid video source: %d", vs);
+        return BAD_VALUE;
+    }
+
+    if (vs == VIDEO_SOURCE_DEFAULT) {
+        mVideoSource = VIDEO_SOURCE_CAMERA;
+    } else {
+        mVideoSource = vs;
+    }
 
     return OK;
 }
 
 status_t StagefrightRecorder::setOutputFormat(output_format of) {
-    mOutputFormat = of;
+    LOGV("setOutputFormat: %d", of);
+    if (of < OUTPUT_FORMAT_DEFAULT ||
+        of >= OUTPUT_FORMAT_LIST_END) {
+        LOGE("Invalid output format: %d", of);
+        return BAD_VALUE;
+    }
+
+    if (of == OUTPUT_FORMAT_DEFAULT) {
+        mOutputFormat = OUTPUT_FORMAT_THREE_GPP;
+    } else {
+        mOutputFormat = of;
+    }
 
     return OK;
 }
 
 status_t StagefrightRecorder::setAudioEncoder(audio_encoder ae) {
-    mAudioEncoder = ae;
+    LOGV("setAudioEncoder: %d", ae);
+    if (ae < AUDIO_ENCODER_DEFAULT ||
+        ae >= AUDIO_ENCODER_LIST_END) {
+        LOGE("Invalid audio encoder: %d", ae);
+        return BAD_VALUE;
+    }
+
+    if (ae == AUDIO_ENCODER_DEFAULT) {
+        mAudioEncoder = AUDIO_ENCODER_AMR_NB;
+    } else {
+        mAudioEncoder = ae;
+    }
 
     return OK;
 }
 
 status_t StagefrightRecorder::setVideoEncoder(video_encoder ve) {
-    mVideoEncoder = ve;
+    LOGV("setVideoEncoder: %d", ve);
+    if (ve < VIDEO_ENCODER_DEFAULT ||
+        ve >= VIDEO_ENCODER_LIST_END) {
+        LOGE("Invalid video encoder: %d", ve);
+        return BAD_VALUE;
+    }
+
+    if (ve == VIDEO_ENCODER_DEFAULT) {
+        mVideoEncoder = VIDEO_ENCODER_H263;
+    } else {
+        mVideoEncoder = ve;
+    }
 
     return OK;
 }
 
 status_t StagefrightRecorder::setVideoSize(int width, int height) {
+    LOGV("setVideoSize: %dx%d", width, height);
     if (width <= 0 || height <= 0) {
         LOGE("Invalid video size: %dx%d", width, height);
         return BAD_VALUE;
@@ -103,6 +165,7 @@
 }
 
 status_t StagefrightRecorder::setVideoFrameRate(int frames_per_second) {
+    LOGV("setVideoFrameRate: %d", frames_per_second);
     if (frames_per_second <= 0 || frames_per_second > 30) {
         LOGE("Invalid video frame rate: %d", frames_per_second);
         return BAD_VALUE;
@@ -118,7 +181,7 @@
     LOGV("setCamera");
     if (camera == 0) {
         LOGE("camera is NULL");
-        return UNKNOWN_ERROR;
+        return BAD_VALUE;
     }
 
     int64_t token = IPCThreadState::self()->clearCallingIdentity();
@@ -127,7 +190,7 @@
     if (mCamera == 0) {
         LOGE("Unable to connect to camera");
         IPCThreadState::self()->restoreCallingIdentity(token);
-        return UNKNOWN_ERROR;
+        return -EBUSY;
     }
 
     LOGV("Connected to camera");
@@ -141,23 +204,31 @@
 }
 
 status_t StagefrightRecorder::setPreviewSurface(const sp<ISurface> &surface) {
+    LOGV("setPreviewSurface: %p", surface.get());
     mPreviewSurface = surface;
 
     return OK;
 }
 
 status_t StagefrightRecorder::setOutputFile(const char *path) {
+    LOGE("setOutputFile(const char*) must not be called");
     // We don't actually support this at all, as the media_server process
     // no longer has permissions to create files.
 
-    return UNKNOWN_ERROR;
+    return -EPERM;
 }
 
 status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t length) {
+    LOGV("setOutputFile: %d, %lld, %lld", fd, offset, length);
     // These don't make any sense, do they?
     CHECK_EQ(offset, 0);
     CHECK_EQ(length, 0);
 
+    if (fd < 0) {
+        LOGE("Invalid file descriptor: %d", fd);
+        return -EBADF;
+    }
+
     if (mOutputFd >= 0) {
         ::close(mOutputFd);
     }
@@ -233,6 +304,7 @@
     LOGV("setParamAudioNumberOfChannels: %d", channels);
     if (channels <= 0 || channels >= 3) {
         LOGE("Invalid number of audio channels: %d", channels);
+        return BAD_VALUE;
     }
 
     // Additional check on the number of channels will be performed later.
@@ -270,21 +342,23 @@
     return OK;
 }
 
-status_t StagefrightRecorder::setParamMaxDurationOrFileSize(int64_t limit,
-        bool limit_is_duration) {
-    LOGV("setParamMaxDurationOrFileSize: limit (%lld) for %s",
-            limit, limit_is_duration?"duration":"size");
-    if (limit_is_duration) {  // limit is in ms
-        if (limit <= 1000) {  // XXX: 1 second
-            LOGE("Max file duration is too short: %lld us", limit);
-        }
-        mMaxFileDurationUs = limit * 1000LL;
-    } else {
-        if (limit <= 1024) {  // XXX: 1 kB
-            LOGE("Max file size is too small: %lld bytes", limit);
-        }
-        mMaxFileSizeBytes = limit;
+status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) {
+    LOGV("setParamMaxFileDurationUs: %lld us", timeUs);
+    if (timeUs <= 1000000LL) {  // XXX: 1 second
+        LOGE("Max file duration is too short: %lld us", timeUs);
+        return BAD_VALUE;
     }
+    mMaxFileDurationUs = timeUs;
+    return OK;
+}
+
+status_t StagefrightRecorder::setParamMaxFileSizeBytes(int64_t bytes) {
+    LOGV("setParamMaxFileSizeBytes: %lld bytes", bytes);
+    if (bytes <= 1024) {  // XXX: 1 kB
+        LOGE("Max file size is too small: %lld bytes", bytes);
+        return BAD_VALUE;
+    }
+    mMaxFileSizeBytes = bytes;
     return OK;
 }
 
@@ -309,7 +383,7 @@
 
 // If interval <  0, only the first frame is I frame, and rest are all P frames
 // If interval == 0, all frames are encoded as I frames. No P frames
-// If interval >  0, it is the time spacing between 2 neighboring I frames
+// If interval >  0, it is the time spacing (seconds) between 2 neighboring I frames
 status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t interval) {
     LOGV("setParamVideoIFramesInterval: %d seconds", interval);
     mIFramesInterval = interval;
@@ -335,6 +409,7 @@
 status_t StagefrightRecorder::setParamTrackFrameStatus(int32_t nFrames) {
     LOGV("setParamTrackFrameStatus: %d", nFrames);
     if (nFrames <= 0) {
+        LOGE("Invalid number of frames to track: %d", nFrames);
         return BAD_VALUE;
     }
     mTrackEveryNumberOfFrames = nFrames;
@@ -344,6 +419,7 @@
 status_t StagefrightRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) {
     LOGV("setParamTrackTimeStatus: %lld", timeDurationUs);
     if (timeDurationUs < 20000) {  // Infeasible if shorter than 20 ms?
+        LOGE("Tracking time duration too short: %lld us", timeDurationUs);
         return BAD_VALUE;
     }
     mTrackEveryTimeDurationUs = timeDurationUs;
@@ -356,14 +432,12 @@
     if (key == "max-duration") {
         int64_t max_duration_ms;
         if (safe_strtoi64(value.string(), &max_duration_ms)) {
-            return setParamMaxDurationOrFileSize(
-                    max_duration_ms, true /* limit_is_duration */);
+            return setParamMaxFileDurationUs(1000LL * max_duration_ms);
         }
     } else if (key == "max-filesize") {
         int64_t max_filesize_bytes;
         if (safe_strtoi64(value.string(), &max_filesize_bytes)) {
-            return setParamMaxDurationOrFileSize(
-                    max_filesize_bytes, false /* limit is filesize */);
+            return setParamMaxFileSizeBytes(max_filesize_bytes);
         }
     } else if (key == "interleave-duration-us") {
         int32_t durationUs;
@@ -456,7 +530,7 @@
     return OK;
 }
 
-status_t StagefrightRecorder::setListener(const sp<IMediaPlayerClient> &listener) {
+status_t StagefrightRecorder::setListener(const sp<IMediaRecorderClient> &listener) {
     mListener = listener;
 
     return OK;
@@ -467,7 +541,10 @@
 }
 
 status_t StagefrightRecorder::start() {
+    CHECK(mOutputFd >= 0);
+
     if (mWriter != NULL) {
+        LOGE("File writer is not avaialble");
         return UNKNOWN_ERROR;
     }
 
@@ -486,6 +563,7 @@
             return startAACRecording();
 
         default:
+            LOGE("Unsupported output file format: %d", mOutputFormat);
             return UNKNOWN_ERROR;
     }
 }
@@ -549,7 +627,6 @@
 
     CHECK(mAudioEncoder == AUDIO_ENCODER_AAC);
     CHECK(mAudioSource != AUDIO_SOURCE_LIST_END);
-    CHECK(mOutputFd >= 0);
 
     CHECK(0 == "AACWriter is not implemented yet");
 
@@ -565,34 +642,34 @@
             mAudioEncoder != AUDIO_ENCODER_AMR_NB) {
             LOGE("Invalid encoder %d used for AMRNB recording",
                     mAudioEncoder);
-            return UNKNOWN_ERROR;
+            return BAD_VALUE;
         }
         if (mSampleRate != 8000) {
             LOGE("Invalid sampling rate %d used for AMRNB recording",
                     mSampleRate);
-            return UNKNOWN_ERROR;
+            return BAD_VALUE;
         }
     } else {  // mOutputFormat must be OUTPUT_FORMAT_AMR_WB
         if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) {
             LOGE("Invlaid encoder %d used for AMRWB recording",
                     mAudioEncoder);
-            return UNKNOWN_ERROR;
+            return BAD_VALUE;
         }
         if (mSampleRate != 16000) {
             LOGE("Invalid sample rate %d used for AMRWB recording",
                     mSampleRate);
-            return UNKNOWN_ERROR;
+            return BAD_VALUE;
         }
     }
     if (mAudioChannels != 1) {
         LOGE("Invalid number of audio channels %d used for amr recording",
                 mAudioChannels);
-        return UNKNOWN_ERROR;
+        return BAD_VALUE;
     }
 
     if (mAudioSource >= AUDIO_SOURCE_LIST_END) {
         LOGE("Invalid audio source: %d", mAudioSource);
-        return UNKNOWN_ERROR;
+        return BAD_VALUE;
     }
 
     sp<MediaSource> audioEncoder = createAudioSource();
@@ -601,7 +678,6 @@
         return UNKNOWN_ERROR;
     }
 
-    CHECK(mOutputFd >= 0);
     mWriter = new AMRWriter(dup(mOutputFd));
     mWriter->addSource(audioEncoder);
 
@@ -668,6 +744,54 @@
     }
 }
 
+status_t StagefrightRecorder::setupCameraSource() {
+    clipVideoBitRate();
+    clipVideoFrameRate();
+    clipVideoFrameWidth();
+    clipVideoFrameHeight();
+
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
+    if (mCamera == 0) {
+        mCamera = Camera::connect(mCameraId);
+        if (mCamera == 0) {
+            LOGE("Camera connection could not be established.");
+            return -EBUSY;
+        }
+        mFlags &= ~FLAGS_HOT_CAMERA;
+        mCamera->lock();
+    }
+
+    // Set the actual video recording frame size
+    CameraParameters params(mCamera->getParameters());
+    params.setPreviewSize(mVideoWidth, mVideoHeight);
+    params.setPreviewFrameRate(mFrameRate);
+    String8 s = params.flatten();
+    CHECK_EQ(OK, mCamera->setParameters(s));
+    CameraParameters newCameraParams(mCamera->getParameters());
+
+    // Check on video frame size
+    int frameWidth = 0, frameHeight = 0;
+    newCameraParams.getPreviewSize(&frameWidth, &frameHeight);
+    if (frameWidth  < 0 || frameWidth  != mVideoWidth ||
+        frameHeight < 0 || frameHeight != mVideoHeight) {
+        LOGE("Failed to set the video frame size to %dx%d",
+                mVideoWidth, mVideoHeight);
+        IPCThreadState::self()->restoreCallingIdentity(token);
+        return UNKNOWN_ERROR;
+    }
+
+    // Check on video frame rate
+    int frameRate = newCameraParams.getPreviewFrameRate();
+    if (frameRate < 0 || (frameRate - mFrameRate) != 0) {
+        LOGE("Failed to set frame rate to %d fps. The actual "
+             "frame rate is %d", mFrameRate, frameRate);
+    }
+
+    CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface));
+    IPCThreadState::self()->restoreCallingIdentity(token);
+    return OK;
+}
+
 void StagefrightRecorder::clipVideoFrameHeight() {
     LOGV("clipVideoFrameHeight: encoder %d", mVideoEncoder);
     int minFrameHeight = mEncoderProfiles->getVideoEncoderParamByName(
@@ -685,140 +809,110 @@
     }
 }
 
+status_t StagefrightRecorder::setupVideoEncoder(const sp<MediaWriter>& writer) {
+    status_t err = setupCameraSource();
+    if (err != OK) return err;
+
+    sp<CameraSource> cameraSource = CameraSource::CreateFromCamera(mCamera);
+    CHECK(cameraSource != NULL);
+
+    sp<MetaData> enc_meta = new MetaData;
+    enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
+    enc_meta->setInt32(kKeySampleRate, mFrameRate);
+
+    switch (mVideoEncoder) {
+        case VIDEO_ENCODER_H263:
+            enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
+            break;
+
+        case VIDEO_ENCODER_MPEG_4_SP:
+            enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+            break;
+
+        case VIDEO_ENCODER_H264:
+            enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+            break;
+
+        default:
+            CHECK(!"Should not be here, unsupported video encoding.");
+            break;
+    }
+
+    sp<MetaData> meta = cameraSource->getFormat();
+
+    int32_t width, height, stride, sliceHeight;
+    CHECK(meta->findInt32(kKeyWidth, &width));
+    CHECK(meta->findInt32(kKeyHeight, &height));
+    CHECK(meta->findInt32(kKeyStride, &stride));
+    CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight));
+
+    enc_meta->setInt32(kKeyWidth, width);
+    enc_meta->setInt32(kKeyHeight, height);
+    enc_meta->setInt32(kKeyIFramesInterval, mIFramesInterval);
+    enc_meta->setInt32(kKeyStride, stride);
+    enc_meta->setInt32(kKeySliceHeight, sliceHeight);
+
+    OMXClient client;
+    CHECK_EQ(client.connect(), OK);
+
+    sp<MediaSource> encoder = OMXCodec::Create(
+            client.interface(), enc_meta,
+            true /* createEncoder */, cameraSource);
+    if (encoder == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    writer->addSource(encoder);
+    return OK;
+}
+
+status_t StagefrightRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) {
+    sp<MediaSource> audioEncoder;
+    switch(mAudioEncoder) {
+        case AUDIO_ENCODER_AMR_NB:
+        case AUDIO_ENCODER_AMR_WB:
+        case AUDIO_ENCODER_AAC:
+            audioEncoder = createAudioSource();
+            break;
+        default:
+            LOGE("Unsupported audio encoder: %d", mAudioEncoder);
+            return UNKNOWN_ERROR;
+    }
+
+    if (audioEncoder == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    writer->addSource(audioEncoder);
+    return OK;
+}
+
 status_t StagefrightRecorder::startMPEG4Recording() {
-    mWriter = new MPEG4Writer(dup(mOutputFd));
     int32_t totalBitRate = 0;
+    status_t err = OK;
+    sp<MediaWriter> writer = new MPEG4Writer(dup(mOutputFd));
 
     // Add audio source first if it exists
     if (mAudioSource != AUDIO_SOURCE_LIST_END) {
-        sp<MediaSource> audioEncoder;
-        switch(mAudioEncoder) {
-            case AUDIO_ENCODER_AMR_NB:
-            case AUDIO_ENCODER_AMR_WB:
-            case AUDIO_ENCODER_AAC:
-                audioEncoder = createAudioSource();
-                break;
-            default:
-                LOGE("Unsupported audio encoder: %d", mAudioEncoder);
-                return UNKNOWN_ERROR;
-        }
-
-        if (audioEncoder == NULL) {
-            return UNKNOWN_ERROR;
-        }
+        err = setupAudioEncoder(writer);
+        if (err != OK) return err;
         totalBitRate += mAudioBitRate;
-        mWriter->addSource(audioEncoder);
     }
     if (mVideoSource == VIDEO_SOURCE_DEFAULT
             || mVideoSource == VIDEO_SOURCE_CAMERA) {
-
-        clipVideoBitRate();
-        clipVideoFrameRate();
-        clipVideoFrameWidth();
-        clipVideoFrameHeight();
-
-        int64_t token = IPCThreadState::self()->clearCallingIdentity();
-        if (mCamera == 0) {
-            mCamera = Camera::connect(mCameraId);
-            mCamera->lock();
-        }
-
-        // Set the actual video recording frame size
-        CameraParameters params(mCamera->getParameters());
-        params.setPreviewSize(mVideoWidth, mVideoHeight);
-        params.setPreviewFrameRate(mFrameRate);
-        String8 s = params.flatten();
-        CHECK_EQ(OK, mCamera->setParameters(s));
-        CameraParameters newCameraParams(mCamera->getParameters());
-
-        // Check on video frame size
-        int frameWidth = 0, frameHeight = 0;
-        newCameraParams.getPreviewSize(&frameWidth, &frameHeight);
-        if (frameWidth  < 0 || frameWidth  != mVideoWidth ||
-            frameHeight < 0 || frameHeight != mVideoHeight) {
-            LOGE("Failed to set the video frame size to %dx%d",
-                    mVideoWidth, mVideoHeight);
-            IPCThreadState::self()->restoreCallingIdentity(token);
-            return UNKNOWN_ERROR;
-        }
-
-        // Check on video frame rate
-        int frameRate = newCameraParams.getPreviewFrameRate();
-        if (frameRate < 0 || (frameRate - mFrameRate) != 0) {
-            LOGE("Failed to set frame rate to %d fps. The actual "
-                 "frame rate is %d", mFrameRate, frameRate);
-        }
-
-        CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface));
-        IPCThreadState::self()->restoreCallingIdentity(token);
-
-        sp<CameraSource> cameraSource =
-            CameraSource::CreateFromCamera(mCamera);
-
-        CHECK(cameraSource != NULL);
-
-        sp<MetaData> enc_meta = new MetaData;
-        enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
-        enc_meta->setInt32(kKeySampleRate, mFrameRate);
-
-        switch (mVideoEncoder) {
-            case VIDEO_ENCODER_H263:
-                enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
-                break;
-
-            case VIDEO_ENCODER_MPEG_4_SP:
-                enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
-                break;
-
-            case VIDEO_ENCODER_H264:
-                enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
-                break;
-
-            default:
-                CHECK(!"Should not be here, unsupported video encoding.");
-                break;
-        }
-
-        sp<MetaData> meta = cameraSource->getFormat();
-
-        int32_t width, height, stride, sliceHeight;
-        CHECK(meta->findInt32(kKeyWidth, &width));
-        CHECK(meta->findInt32(kKeyHeight, &height));
-        CHECK(meta->findInt32(kKeyStride, &stride));
-        CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight));
-
-        enc_meta->setInt32(kKeyWidth, width);
-        enc_meta->setInt32(kKeyHeight, height);
-        enc_meta->setInt32(kKeyIFramesInterval, mIFramesInterval);
-        enc_meta->setInt32(kKeyStride, stride);
-        enc_meta->setInt32(kKeySliceHeight, sliceHeight);
-
-        OMXClient client;
-        CHECK_EQ(client.connect(), OK);
-
-        sp<MediaSource> encoder =
-            OMXCodec::Create(
-                    client.interface(), enc_meta,
-                    true /* createEncoder */, cameraSource);
-
-        CHECK(mOutputFd >= 0);
+        err = setupVideoEncoder(writer);
+        if (err != OK) return err;
         totalBitRate += mVideoBitRate;
-        mWriter->addSource(encoder);
     }
 
-    {
-        // MPEGWriter specific handling
-        MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get());
-        writer->setInterleaveDuration(mInterleaveDurationUs);
-    }
+    reinterpret_cast<MPEG4Writer *>(writer.get())->
+        setInterleaveDuration(mInterleaveDurationUs);
 
     if (mMaxFileDurationUs != 0) {
-        mWriter->setMaxFileDuration(mMaxFileDurationUs);
+        writer->setMaxFileDuration(mMaxFileDurationUs);
     }
     if (mMaxFileSizeBytes != 0) {
-        mWriter->setMaxFileSize(mMaxFileSizeBytes);
+        writer->setMaxFileSize(mMaxFileSizeBytes);
     }
-    mWriter->setListener(mListener);
     sp<MetaData> meta = new MetaData;
     meta->setInt64(kKeyTime, systemTime() / 1000);
     meta->setInt32(kKeyFileType, mOutputFormat);
@@ -830,11 +924,13 @@
     if (mTrackEveryTimeDurationUs > 0) {
         meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
     }
-    mWriter->start(meta.get());
-    return OK;
+    writer->setListener(mListener);
+    mWriter = writer;
+    return mWriter->start(meta.get());
 }
 
 status_t StagefrightRecorder::pause() {
+    LOGV("pause");
     if (mWriter == NULL) {
         return UNKNOWN_ERROR;
     }
@@ -843,34 +939,37 @@
 }
 
 status_t StagefrightRecorder::stop() {
-    if (mWriter == NULL) {
-        return UNKNOWN_ERROR;
+    LOGV("stop");
+    if (mWriter != NULL) {
+        mWriter->stop();
+        mWriter.clear();
     }
 
-    mWriter->stop();
-    mWriter = NULL;
-
-    return OK;
-}
-
-status_t StagefrightRecorder::close() {
-    stop();
-
     if (mCamera != 0) {
+        LOGV("Disconnect camera");
         int64_t token = IPCThreadState::self()->clearCallingIdentity();
         if ((mFlags & FLAGS_HOT_CAMERA) == 0) {
             LOGV("Camera was cold when we started, stopping preview");
             mCamera->stopPreview();
         }
         mCamera->unlock();
-        mCamera = NULL;
+        mCamera.clear();
         IPCThreadState::self()->restoreCallingIdentity(token);
         mFlags = 0;
     }
+
+    return OK;
+}
+
+status_t StagefrightRecorder::close() {
+    LOGV("close");
+    stop();
+
     return OK;
 }
 
 status_t StagefrightRecorder::reset() {
+    LOGV("reset");
     stop();
 
     // No audio or video source by default
@@ -904,6 +1003,13 @@
 }
 
 status_t StagefrightRecorder::getMaxAmplitude(int *max) {
+    LOGV("getMaxAmplitude");
+
+    if (max == NULL) {
+        LOGE("Null pointer argument");
+        return BAD_VALUE;
+    }
+
     if (mAudioSourceNode != 0) {
         *max = mAudioSourceNode->getMaxAmplitude();
     } else {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 9fb7e8f..cb05571 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -46,7 +46,7 @@
     virtual status_t setOutputFile(const char *path);
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
     virtual status_t setParameters(const String8& params);
-    virtual status_t setListener(const sp<IMediaPlayerClient>& listener);
+    virtual status_t setListener(const sp<IMediaRecorderClient>& listener);
     virtual status_t prepare();
     virtual status_t start();
     virtual status_t pause();
@@ -63,7 +63,7 @@
 
     sp<Camera> mCamera;
     sp<ISurface> mPreviewSurface;
-    sp<IMediaPlayerClient> mListener;
+    sp<IMediaRecorderClient> mListener;
     sp<MediaWriter> mWriter;
     sp<AudioSource> mAudioSourceNode;
 
@@ -97,6 +97,11 @@
     status_t startAMRRecording();
     status_t startAACRecording();
     sp<MediaSource> createAudioSource();
+    status_t setupCameraSource();
+    status_t setupAudioEncoder(const sp<MediaWriter>& writer);
+    status_t setupVideoEncoder(const sp<MediaWriter>& writer);
+
+    // Encoding parameter handling utilities
     status_t setParameter(const String8 &key, const String8 &value);
     status_t setParamAudioEncodingBitRate(int32_t bitRate);
     status_t setParamAudioNumberOfChannels(int32_t channles);
@@ -108,7 +113,8 @@
     status_t setParamTrackFrameStatus(int32_t nFrames);
     status_t setParamInterleaveDuration(int32_t durationUs);
     status_t setParam64BitFileOffset(bool use64BitFileOffset);
-    status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration);
+    status_t setParamMaxFileDurationUs(int64_t timeUs);
+    status_t setParamMaxFileSizeBytes(int64_t bytes);
     void clipVideoBitRate();
     void clipVideoFrameRate();
     void clipVideoFrameWidth();
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index b3e1a01..6a4a131 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -34,6 +34,8 @@
 #include <media/mediarecorder.h>
 #include <cutils/properties.h>
 
+#include "include/ESDS.h"
+
 namespace android {
 
 class MPEG4Writer::Track {
@@ -115,12 +117,19 @@
     status_t makeAVCCodecSpecificData(
             const uint8_t *data, size_t size);
     void writeOneChunk(bool isAvc);
-    void logStatisticalData(bool isAudio);
-    void findMinMaxFrameRates(float *minFps, float *maxFps);
-    void findMinMaxChunkDurations(int64_t *min, int64_t *max);
+
+    // Track authoring progress status
     void trackProgressStatus(int32_t nFrames, int64_t timeUs);
     void initTrackingProgressStatus(MetaData *params);
 
+    // Utilities for collecting statistical data
+    void logStatisticalData(bool isAudio);
+    void findMinAvgMaxSampleDurationMs(
+            int32_t *min, int32_t *avg, int32_t *max);
+    void findMinMaxChunkDurations(int64_t *min, int64_t *max);
+
+    void getCodecSpecificDataFromInputFormatIfPossible();
+
     Track(const Track &);
     Track &operator=(const Track &);
 };
@@ -673,6 +682,38 @@
       mCodecSpecificDataSize(0),
       mGotAllCodecSpecificData(false),
       mReachedEOS(false) {
+    getCodecSpecificDataFromInputFormatIfPossible();
+}
+
+void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() {
+    const char *mime;
+    CHECK(mMeta->findCString(kKeyMIMEType, &mime));
+
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        uint32_t type;
+        const void *data;
+        size_t size;
+        if (mMeta->findData(kKeyAVCC, &type, &data, &size)) {
+            mCodecSpecificData = malloc(size);
+            mCodecSpecificDataSize = size;
+            memcpy(mCodecSpecificData, data, size);
+            mGotAllCodecSpecificData = true;
+        }
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
+            || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        uint32_t type;
+        const void *data;
+        size_t size;
+        if (mMeta->findData(kKeyESDS, &type, &data, &size)) {
+            ESDS esds(data, size);
+            if (esds.getCodecSpecificInfo(&data, &size) == OK) {
+                mCodecSpecificData = malloc(size);
+                mCodecSpecificDataSize = size;
+                memcpy(mCodecSpecificData, data, size);
+                mGotAllCodecSpecificData = true;
+            }
+        }
+    }
 }
 
 MPEG4Writer::Track::~Track() {
@@ -716,7 +757,10 @@
     }
 
     int64_t startTimeUs;
-    CHECK(params && params->findInt64(kKeyTime, &startTimeUs));
+    if (params == NULL || !params->findInt64(kKeyTime, &startTimeUs)) {
+        startTimeUs = 0;
+    }
+
     initTrackingProgressStatus(params);
 
     sp<MetaData> meta = new MetaData;
@@ -1184,14 +1228,16 @@
 
 void MPEG4Writer::Track::trackProgressStatus(int32_t nFrames, int64_t timeUs) {
     LOGV("trackProgressStatus: %d frames and %lld us", nFrames, timeUs);
-    if (nFrames % mTrackEveryNumberOfFrames == 0) {
+    if (mTrackEveryNumberOfFrames > 0 &&
+        nFrames % mTrackEveryNumberOfFrames == 0) {
         LOGV("Fire frame tracking progress status at frame %d", nFrames);
         mOwner->notify(MEDIA_RECORDER_EVENT_INFO,
                        MEDIA_RECORDER_INFO_PROGRESS_FRAME_STATUS,
                        nFrames);
     }
 
-    if (timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
+    if (mTrackEveryTimeDurationUs > 0 &&
+        timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
         LOGV("Fire time tracking progress status at %lld us", timeUs);
         mOwner->notify(MEDIA_RECORDER_EVENT_INFO,
                        MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS,
@@ -1200,21 +1246,28 @@
     }
 }
 
-void MPEG4Writer::Track::findMinMaxFrameRates(float *minFps, float *maxFps) {
-    int32_t minSampleDuration = 0x7FFFFFFF;
-    int32_t maxSampleDuration = 0;
+void MPEG4Writer::Track::findMinAvgMaxSampleDurationMs(
+        int32_t *min, int32_t *avg, int32_t *max) {
+    CHECK(!mSampleInfos.empty());
+    int32_t avgSampleDurationMs = mMaxTimeStampUs / 1000/ mSampleInfos.size();
+    int32_t minSampleDurationMs = 0x7FFFFFFF;
+    int32_t maxSampleDurationMs = 0;
     for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
         it != mSttsTableEntries.end(); ++it) {
-        int32_t sampleDuration = static_cast<int32_t>(it->sampleDuration);
-        if (sampleDuration > maxSampleDuration) {
-            maxSampleDuration = sampleDuration;
-        } else if (sampleDuration < minSampleDuration) {
-            minSampleDuration = sampleDuration;
+        int32_t sampleDurationMs = static_cast<int32_t>(it->sampleDuration);
+        if (sampleDurationMs > maxSampleDurationMs) {
+            maxSampleDurationMs = sampleDurationMs;
+        } else if (sampleDurationMs < minSampleDurationMs) {
+            minSampleDurationMs = sampleDurationMs;
         }
+        LOGI("sample duration: %d ms", sampleDurationMs);
     }
-    CHECK(minSampleDuration != 0 && maxSampleDuration != 0);
-    *minFps = 1000.0 / maxSampleDuration;
-    *maxFps = 1000.0 / minSampleDuration;
+    CHECK(minSampleDurationMs != 0);
+    CHECK(avgSampleDurationMs != 0);
+    CHECK(maxSampleDurationMs != 0);
+    *min = minSampleDurationMs;
+    *avg = avgSampleDurationMs;
+    *max = maxSampleDurationMs;
 }
 
 // Don't count the last duration
@@ -1250,16 +1303,18 @@
     }
 
     if (collectStats) {
-        if (isAudio) {
-            LOGI("audio track - duration %lld us", mMaxTimeStampUs);
-        } else {
-            float fps = (mSampleInfos.size() * 1000000.0) / mMaxTimeStampUs;
-            float minFps;
-            float maxFps;
-            findMinMaxFrameRates(&minFps, &maxFps);
-            LOGI("video track - duration %lld us", mMaxTimeStampUs);
+        LOGI("%s track - duration %lld us, total %d frames",
+                isAudio? "audio": "video", mMaxTimeStampUs,
+                mSampleInfos.size());
+        int32_t min, avg, max;
+        findMinAvgMaxSampleDurationMs(&min, &avg, &max);
+        LOGI("min/avg/max sample duration (ms): %d/%d/%d", min, avg, max);
+        if (!isAudio) {
+            float avgFps = 1000.0 / avg;
+            float minFps = 1000.0 / max;
+            float maxFps = 1000.0 / min;
             LOGI("min/avg/max frame rate (fps): %.2f/%.2f/%.2f",
-                minFps, fps, maxFps);
+                minFps, avgFps, maxFps);
         }
 
         int64_t totalBytes = 0;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 02a073e..dacb8d3 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -149,13 +149,16 @@
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },
 //    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },
+    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },
 //    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4dec" },
+    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.decoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },
 //    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263dec" },
+    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.decoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },
@@ -171,16 +174,19 @@
     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBEncoder" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "AACEncoder" },
-    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacenc" },
+//    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacenc" },
+    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.encoder" },
-    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4enc" },
+//    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4enc" },
+    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.encoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.encoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.encoder" },
-    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263enc" },
+//    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263enc" },
+    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.encoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.encoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" },
-    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcenc" },
+//    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcenc" },
 };
 
 #undef OPTIONAL
@@ -334,10 +340,17 @@
         quirks |= kRequiresAllocateBufferOnInputPorts;
         quirks |= kRequiresAllocateBufferOnOutputPorts;
     }
+    if (!strncmp(componentName, "OMX.qcom.7x30.video.encoder.", 28)) {
+    }
     if (!strncmp(componentName, "OMX.qcom.video.decoder.", 23)) {
         quirks |= kRequiresAllocateBufferOnOutputPorts;
         quirks |= kDefersOutputBufferAllocation;
     }
+    if (!strncmp(componentName, "OMX.qcom.7x30.video.decoder.", 28)) {
+        quirks |= kRequiresAllocateBufferOnInputPorts;
+        quirks |= kRequiresAllocateBufferOnOutputPorts;
+        quirks |= kDefersOutputBufferAllocation;
+    }
 
     if (!strncmp(componentName, "OMX.TI.", 7)) {
         // Apparently I must not use OMX_UseBuffer on either input or
@@ -836,6 +849,7 @@
         }
 
         case OMX_VIDEO_CodingH263:
+            CHECK_EQ(setupH263EncoderParameters(meta), OK);
             break;
 
         case OMX_VIDEO_CodingAVC:
@@ -861,6 +875,90 @@
     return ret;
 }
 
+status_t OMXCodec::setupErrorCorrectionParameters() {
+    OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType;
+    InitOMXParams(&errorCorrectionType);
+    errorCorrectionType.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamVideoErrorCorrection,
+            &errorCorrectionType, sizeof(errorCorrectionType));
+    CHECK_EQ(err, OK);
+
+    errorCorrectionType.bEnableHEC = OMX_FALSE;
+    errorCorrectionType.bEnableResync = OMX_TRUE;
+    errorCorrectionType.nResynchMarkerSpacing = 256;
+    errorCorrectionType.bEnableDataPartitioning = OMX_FALSE;
+    errorCorrectionType.bEnableRVLC = OMX_FALSE;
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamVideoErrorCorrection,
+            &errorCorrectionType, sizeof(errorCorrectionType));
+    CHECK_EQ(err, OK);
+    return OK;
+}
+
+status_t OMXCodec::setupBitRate(int32_t bitRate) {
+    OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
+    InitOMXParams(&bitrateType);
+    bitrateType.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamVideoBitrate,
+            &bitrateType, sizeof(bitrateType));
+    CHECK_EQ(err, OK);
+
+    bitrateType.eControlRate = OMX_Video_ControlRateVariable;
+    bitrateType.nTargetBitrate = bitRate;
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamVideoBitrate,
+            &bitrateType, sizeof(bitrateType));
+    CHECK_EQ(err, OK);
+    return OK;
+}
+
+status_t OMXCodec::setupH263EncoderParameters(const sp<MetaData>& meta) {
+    int32_t iFramesInterval, frameRate, bitRate;
+    bool success = meta->findInt32(kKeyBitRate, &bitRate);
+    success = success && meta->findInt32(kKeySampleRate, &frameRate);
+    success = success && meta->findInt32(kKeyIFramesInterval, &iFramesInterval);
+    CHECK(success);
+    OMX_VIDEO_PARAM_H263TYPE h263type;
+    InitOMXParams(&h263type);
+    h263type.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
+    CHECK_EQ(err, OK);
+
+    h263type.nAllowedPictureTypes =
+        OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+
+    h263type.nPFrames = setPFramesSpacing(iFramesInterval, frameRate);
+    if (h263type.nPFrames == 0) {
+        h263type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
+    }
+    h263type.nBFrames = 0;
+
+    h263type.eProfile = OMX_VIDEO_H263ProfileBaseline;
+    h263type.eLevel = OMX_VIDEO_H263Level45;
+
+    h263type.bPLUSPTYPEAllowed = OMX_FALSE;
+    h263type.bForceRoundingTypeToZero = OMX_FALSE;
+    h263type.nPictureHeaderRepetition = 0;
+    h263type.nGOBHeaderInterval = 0;
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
+    CHECK_EQ(err, OK);
+
+    CHECK_EQ(setupBitRate(bitRate), OK);
+    CHECK_EQ(setupErrorCorrectionParameters(), OK);
+
+    return OK;
+}
+
 status_t OMXCodec::setupMPEG4EncoderParameters(const sp<MetaData>& meta) {
     int32_t iFramesInterval, frameRate, bitRate;
     bool success = meta->findInt32(kKeyBitRate, &bitRate);
@@ -894,53 +992,15 @@
     mpeg4type.nHeaderExtension = 0;
     mpeg4type.bReversibleVLC = OMX_FALSE;
 
-    mpeg4type.eProfile = OMX_VIDEO_MPEG4ProfileCore;
+    mpeg4type.eProfile = OMX_VIDEO_MPEG4ProfileSimple;
     mpeg4type.eLevel = OMX_VIDEO_MPEG4Level2;
 
     err = mOMX->setParameter(
             mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
     CHECK_EQ(err, OK);
 
-    // ----------------
-
-    OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
-    InitOMXParams(&bitrateType);
-    bitrateType.nPortIndex = kPortIndexOutput;
-
-    err = mOMX->getParameter(
-            mNode, OMX_IndexParamVideoBitrate,
-            &bitrateType, sizeof(bitrateType));
-    CHECK_EQ(err, OK);
-
-    bitrateType.eControlRate = OMX_Video_ControlRateVariable;
-    bitrateType.nTargetBitrate = bitRate;
-
-    err = mOMX->setParameter(
-            mNode, OMX_IndexParamVideoBitrate,
-            &bitrateType, sizeof(bitrateType));
-    CHECK_EQ(err, OK);
-
-    // ----------------
-
-    OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType;
-    InitOMXParams(&errorCorrectionType);
-    errorCorrectionType.nPortIndex = kPortIndexOutput;
-
-    err = mOMX->getParameter(
-            mNode, OMX_IndexParamVideoErrorCorrection,
-            &errorCorrectionType, sizeof(errorCorrectionType));
-    CHECK_EQ(err, OK);
-
-    errorCorrectionType.bEnableHEC = OMX_FALSE;
-    errorCorrectionType.bEnableResync = OMX_TRUE;
-    errorCorrectionType.nResynchMarkerSpacing = 256;
-    errorCorrectionType.bEnableDataPartitioning = OMX_FALSE;
-    errorCorrectionType.bEnableRVLC = OMX_FALSE;
-
-    err = mOMX->setParameter(
-            mNode, OMX_IndexParamVideoErrorCorrection,
-            &errorCorrectionType, sizeof(errorCorrectionType));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(setupBitRate(bitRate), OK);
+    CHECK_EQ(setupErrorCorrectionParameters(), OK);
 
     return OK;
 }
@@ -991,22 +1051,7 @@
             mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
     CHECK_EQ(err, OK);
 
-    OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
-    InitOMXParams(&bitrateType);
-    bitrateType.nPortIndex = kPortIndexOutput;
-
-    err = mOMX->getParameter(
-            mNode, OMX_IndexParamVideoBitrate,
-            &bitrateType, sizeof(bitrateType));
-    CHECK_EQ(err, OK);
-
-    bitrateType.eControlRate = OMX_Video_ControlRateVariable;
-    bitrateType.nTargetBitrate = bitRate;
-
-    err = mOMX->setParameter(
-            mNode, OMX_IndexParamVideoBitrate,
-            &bitrateType, sizeof(bitrateType));
-    CHECK_EQ(err, OK);
+    CHECK_EQ(setupBitRate(bitRate), OK);
 
     return OK;
 }
diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
index b914023..2317de6 100644
--- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp
+++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
@@ -132,7 +132,10 @@
 }
 
 status_t AACEncoder::start(MetaData *params) {
-    CHECK(!mStarted);
+    if (mStarted) {
+        LOGW("Call start() when encoder already started");
+        return OK;
+    }
 
     mBufferGroup = new MediaBufferGroup;
     mBufferGroup->add_buffer(new MediaBuffer(2048));
@@ -150,7 +153,10 @@
 }
 
 status_t AACEncoder::stop() {
-    CHECK(mStarted);
+    if (!mStarted) {
+        LOGW("Call stop() when encoder has not started");
+        return OK;
+    }
 
     if (mInputBuffer) {
         mInputBuffer->release();
diff --git a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
index 445438f..4c02fe9 100644
--- a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
@@ -70,7 +70,10 @@
 }
 
 status_t AMRNBEncoder::start(MetaData *params) {
-    CHECK(!mStarted);
+    if (mStarted) {
+        LOGW("Call start() when encoder already started");
+        return OK;
+    }
 
     mBufferGroup = new MediaBufferGroup;
     mBufferGroup->add_buffer(new MediaBuffer(32));
@@ -97,7 +100,10 @@
 }
 
 status_t AMRNBEncoder::stop() {
-    CHECK(mStarted);
+    if (!mStarted) {
+        LOGW("Call stop() when encoder has not started.");
+        return OK;
+    }
 
     if (mInputBuffer) {
         mInputBuffer->release();
diff --git a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
index b70cff1..4257c6a 100644
--- a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
+++ b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
@@ -124,7 +124,10 @@
 }
 
 status_t AMRWBEncoder::start(MetaData *params) {
-    CHECK(!mStarted);
+    if (mStarted) {
+        LOGW("Call start() when encoder already started");
+        return OK;
+    }
 
     mBufferGroup = new MediaBufferGroup;
 
@@ -142,8 +145,10 @@
 }
 
 status_t AMRWBEncoder::stop() {
-    CHECK(mStarted);
-
+    if (!mStarted) {
+        LOGW("Call stop() when encoder has not started");
+        return OK;
+    }
 
     if (mInputBuffer) {
         mInputBuffer->release();
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index e74782f..5b16997 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -42,6 +42,7 @@
         case OMX_COLOR_FormatYUV420Planar:
         case OMX_COLOR_FormatCbYCrY:
         case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
+        case OMX_COLOR_FormatYUV420SemiPlanar:
             return true;
 
         default:
@@ -71,6 +72,11 @@
                     width, height, srcBits, srcSkip, dstBits, dstSkip);
             break;
 
+        case OMX_COLOR_FormatYUV420SemiPlanar:
+            convertYUV420SemiPlanar(
+                    width, height, srcBits, srcSkip, dstBits, dstSkip);
+            break;
+
         default:
         {
             CHECK(!"Should not be here. Unknown color conversion.");
@@ -279,6 +285,68 @@
     }
 }
 
+void ColorConverter::convertYUV420SemiPlanar(
+        size_t width, size_t height,
+        const void *srcBits, size_t srcSkip,
+        void *dstBits, size_t dstSkip) {
+    CHECK_EQ(srcSkip, 0);  // Doesn't really make sense for YUV formats.
+    CHECK(dstSkip >= width * 2);
+    CHECK((dstSkip & 3) == 0);
+
+    uint8_t *kAdjustedClip = initClip();
+
+    uint32_t *dst_ptr = (uint32_t *)dstBits;
+    const uint8_t *src_y = (const uint8_t *)srcBits;
+
+    const uint8_t *src_u =
+        (const uint8_t *)src_y + width * height;
+
+    for (size_t y = 0; y < height; ++y) {
+        for (size_t x = 0; x < width; x += 2) {
+            signed y1 = (signed)src_y[x] - 16;
+            signed y2 = (signed)src_y[x + 1] - 16;
+
+            signed v = (signed)src_u[x & ~1] - 128;
+            signed u = (signed)src_u[(x & ~1) + 1] - 128;
+
+            signed u_b = u * 517;
+            signed u_g = -u * 100;
+            signed v_g = -v * 208;
+            signed v_r = v * 409;
+
+            signed tmp1 = y1 * 298;
+            signed b1 = (tmp1 + u_b) / 256;
+            signed g1 = (tmp1 + v_g + u_g) / 256;
+            signed r1 = (tmp1 + v_r) / 256;
+
+            signed tmp2 = y2 * 298;
+            signed b2 = (tmp2 + u_b) / 256;
+            signed g2 = (tmp2 + v_g + u_g) / 256;
+            signed r2 = (tmp2 + v_r) / 256;
+
+            uint32_t rgb1 =
+                ((kAdjustedClip[b1] >> 3) << 11)
+                | ((kAdjustedClip[g1] >> 2) << 5)
+                | (kAdjustedClip[r1] >> 3);
+
+            uint32_t rgb2 =
+                ((kAdjustedClip[b2] >> 3) << 11)
+                | ((kAdjustedClip[g2] >> 2) << 5)
+                | (kAdjustedClip[r2] >> 3);
+
+            dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+        }
+
+        src_y += width;
+
+        if (y & 1) {
+            src_u += width;
+        }
+
+        dst_ptr += dstSkip / 4;
+    }
+}
+
 uint8_t *ColorConverter::initClip() {
     static const signed kClipMin = -278;
     static const signed kClipMax = 535;
diff --git a/media/libstagefright/foundation/ALooper.cpp b/media/libstagefright/foundation/ALooper.cpp
index 831fa2a..cd4f349 100644
--- a/media/libstagefright/foundation/ALooper.cpp
+++ b/media/libstagefright/foundation/ALooper.cpp
@@ -31,8 +31,9 @@
 ALooperRoster gLooperRoster;
 
 struct ALooper::LooperThread : public Thread {
-    LooperThread(ALooper *looper)
-        : mLooper(looper) {
+    LooperThread(ALooper *looper, bool canCallJava)
+        : Thread(canCallJava),
+          mLooper(looper) {
     }
 
     virtual bool threadLoop() {
@@ -72,7 +73,7 @@
     gLooperRoster.unregisterHandler(handlerID);
 }
 
-status_t ALooper::start(bool runOnCallingThread) {
+status_t ALooper::start(bool runOnCallingThread, bool canCallJava) {
     if (runOnCallingThread) {
         {
             Mutex::Autolock autoLock(mLock);
@@ -96,7 +97,7 @@
         return INVALID_OPERATION;
     }
 
-    mThread = new LooperThread(this);
+    mThread = new LooperThread(this, canCallJava);
 
     status_t err = mThread->run("ALooper");
     if (err != OK) {
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index dfd1ae3..26c6d42 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -16,6 +16,8 @@
 
 #include "AMessage.h"
 
+#include <ctype.h>
+
 #include "AAtomizer.h"
 #include "ADebug.h"
 #include "ALooperRoster.h"
@@ -238,4 +240,105 @@
     return msg;
 }
 
+static void appendIndent(AString *s, int32_t indent) {
+    static const char kWhitespace[] =
+        "                                        "
+        "                                        ";
+
+    CHECK_LT((size_t)indent, sizeof(kWhitespace));
+
+    s->append(kWhitespace, indent);
+}
+
+static bool isFourcc(uint32_t what) {
+    return isprint(what & 0xff)
+        && isprint((what >> 8) & 0xff)
+        && isprint((what >> 16) & 0xff)
+        && isprint((what >> 24) & 0xff);
+}
+
+AString AMessage::debugString(int32_t indent) const {
+    AString s = "AMessage(what = ";
+
+    AString tmp;
+    if (isFourcc(mWhat)) {
+        tmp = StringPrintf(
+                "'%c%c%c%c'",
+                (char)(mWhat >> 24),
+                (char)((mWhat >> 16) & 0xff),
+                (char)((mWhat >> 8) & 0xff),
+                (char)(mWhat & 0xff));
+    } else {
+        tmp = StringPrintf("0x%08x", mWhat);
+    }
+    s.append(tmp);
+
+    if (mTarget != 0) {
+        tmp = StringPrintf(", target = %d", mTarget);
+        s.append(tmp);
+    }
+    s.append(") = {\n");
+
+    for (size_t i = 0; i < mNumItems; ++i) {
+        const Item &item = mItems[i];
+
+        switch (item.mType) {
+            case kTypeInt32:
+                tmp = StringPrintf(
+                        "int32_t %s = %d", item.mName, item.u.int32Value);
+                break;
+            case kTypeInt64:
+                tmp = StringPrintf(
+                        "int64_t %s = %lld", item.mName, item.u.int64Value);
+                break;
+            case kTypeSize:
+                tmp = StringPrintf(
+                        "size_t %s = %d", item.mName, item.u.sizeValue);
+                break;
+            case kTypeFloat:
+                tmp = StringPrintf(
+                        "float %s = %f", item.mName, item.u.floatValue);
+                break;
+            case kTypeDouble:
+                tmp = StringPrintf(
+                        "double %s = %f", item.mName, item.u.doubleValue);
+                break;
+            case kTypePointer:
+                tmp = StringPrintf(
+                        "void *%s = %p", item.mName, item.u.ptrValue);
+                break;
+            case kTypeString:
+                tmp = StringPrintf(
+                        "string %s = \"%s\"",
+                        item.mName,
+                        item.u.stringValue->c_str());
+                break;
+            case kTypeObject:
+                tmp = StringPrintf(
+                        "RefBase *%s = %p", item.mName, item.u.refValue);
+                break;
+            case kTypeMessage:
+                tmp = StringPrintf(
+                        "AMessage %s = %s",
+                        item.mName,
+                        static_cast<AMessage *>(
+                            item.u.refValue)->debugString(
+                                indent + strlen(item.mName) + 14).c_str());
+                break;
+            default:
+                TRESPASS();
+        }
+
+        appendIndent(&s, indent);
+        s.append("  ");
+        s.append(tmp);
+        s.append("\n");
+    }
+
+    appendIndent(&s, indent);
+    s.append("}");
+
+    return s;
+}
+
 }  // namespace android
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 8c621b6..fe8ed00 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -7,7 +7,8 @@
 #
 LOCAL_SRC_FILES:= \
     activity.cpp \
-    input.cpp
+    input.cpp \
+    native_window.cpp
 
 LOCAL_SHARED_LIBRARIES := \
     libandroid_runtime \
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 38d8567..8498840 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -27,168 +27,168 @@
 using android::KeyEvent;
 using android::MotionEvent;
 
-int32_t input_event_get_type(const input_event_t* event) {
+int32_t AInputEvent_getType(const AInputEvent* event) {
     return static_cast<const InputEvent*>(event)->getType();
 }
 
-int32_t input_event_get_device_id(const input_event_t* event) {
+int32_t AInputEvent_getDeviceId(const AInputEvent* event) {
     return static_cast<const InputEvent*>(event)->getDeviceId();
 }
 
-int32_t input_event_get_nature(const input_event_t* event) {
+int32_t AInputEvent_getNature(const AInputEvent* event) {
     return static_cast<const InputEvent*>(event)->getNature();
 }
 
-int32_t key_event_get_action(const input_event_t* key_event) {
+int32_t AKeyEvent_getAction(const AInputEvent* key_event) {
     return static_cast<const KeyEvent*>(key_event)->getAction();
 }
 
-int32_t key_event_get_flags(const input_event_t* key_event) {
+int32_t AKeyEvent_getFlags(const AInputEvent* key_event) {
     return static_cast<const KeyEvent*>(key_event)->getFlags();
 }
 
-int32_t key_event_get_key_code(const input_event_t* key_event) {
+int32_t AKeyEvent_getKeyCode(const AInputEvent* key_event) {
     return static_cast<const KeyEvent*>(key_event)->getKeyCode();
 }
 
-int32_t key_event_get_scan_code(const input_event_t* key_event) {
+int32_t AKeyEvent_getScanCode(const AInputEvent* key_event) {
     return static_cast<const KeyEvent*>(key_event)->getScanCode();
 }
 
-int32_t key_event_get_meta_state(const input_event_t* key_event) {
+int32_t AKeyEvent_getMetaState(const AInputEvent* key_event) {
     return static_cast<const KeyEvent*>(key_event)->getMetaState();
 }
-int32_t key_event_get_repeat_count(const input_event_t* key_event) {
+int32_t AKeyEvent_getRepeatCount(const AInputEvent* key_event) {
     return static_cast<const KeyEvent*>(key_event)->getRepeatCount();
 }
 
-int64_t key_event_get_down_time(const input_event_t* key_event) {
+int64_t AKeyEvent_getDownTime(const AInputEvent* key_event) {
     return static_cast<const KeyEvent*>(key_event)->getDownTime();
 }
 
-int64_t key_event_get_event_time(const input_event_t* key_event) {
+int64_t AKeyEvent_getEventTime(const AInputEvent* key_event) {
     return static_cast<const KeyEvent*>(key_event)->getEventTime();
 }
 
-int32_t motion_event_get_action(const input_event_t* motion_event) {
+int32_t AMotionEvent_getAction(const AInputEvent* motion_event) {
     return static_cast<const MotionEvent*>(motion_event)->getAction();
 }
 
-int32_t motion_event_get_meta_state(const input_event_t* motion_event) {
+int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event) {
     return static_cast<const MotionEvent*>(motion_event)->getMetaState();
 }
 
-int32_t motion_event_get_edge_flags(const input_event_t* motion_event) {
+int32_t AMotionEvent_getEdgeFlags(const AInputEvent* motion_event) {
     return reinterpret_cast<const MotionEvent*>(motion_event)->getEdgeFlags();
 }
 
-int64_t motion_event_get_down_time(const input_event_t* motion_event) {
+int64_t AMotionEvent_getDownTime(const AInputEvent* motion_event) {
     return static_cast<const MotionEvent*>(motion_event)->getDownTime();
 }
 
-int64_t motion_event_get_event_time(const input_event_t* motion_event) {
+int64_t AMotionEvent_getEventTime(const AInputEvent* motion_event) {
     return static_cast<const MotionEvent*>(motion_event)->getEventTime();
 }
 
-float motion_event_get_x_offset(const input_event_t* motion_event) {
+float AMotionEvent_getXOffset(const AInputEvent* motion_event) {
     return static_cast<const MotionEvent*>(motion_event)->getXOffset();
 }
 
-float motion_event_get_y_offset(const input_event_t* motion_event) {
+float AMotionEvent_getYOffset(const AInputEvent* motion_event) {
     return static_cast<const MotionEvent*>(motion_event)->getYOffset();
 }
 
-float motion_event_get_x_precision(const input_event_t* motion_event) {
+float AMotionEvent_getXPrecision(const AInputEvent* motion_event) {
     return static_cast<const MotionEvent*>(motion_event)->getXPrecision();
 }
 
-float motion_event_get_y_precision(const input_event_t* motion_event) {
+float AMotionEvent_getYPrecision(const AInputEvent* motion_event) {
     return static_cast<const MotionEvent*>(motion_event)->getYPrecision();
 }
 
-size_t motion_event_get_pointer_count(const input_event_t* motion_event) {
+size_t AMotionEvent_getPointerCount(const AInputEvent* motion_event) {
     return static_cast<const MotionEvent*>(motion_event)->getPointerCount();
 }
 
-int32_t motion_event_get_pointer_id(const input_event_t* motion_event, size_t pointer_index) {
+int32_t AMotionEvent_getPointerId(const AInputEvent* motion_event, size_t pointer_index) {
     return static_cast<const MotionEvent*>(motion_event)->getPointerId(pointer_index);
 }
 
-float motion_event_get_raw_x(const input_event_t* motion_event, size_t pointer_index) {
+float AMotionEvent_getRawX(const AInputEvent* motion_event, size_t pointer_index) {
     return static_cast<const MotionEvent*>(motion_event)->getRawX(pointer_index);
 }
 
-float motion_event_get_raw_y(const input_event_t* motion_event, size_t pointer_index) {
+float AMotionEvent_getRawY(const AInputEvent* motion_event, size_t pointer_index) {
     return static_cast<const MotionEvent*>(motion_event)->getRawY(pointer_index);
 }
 
-float motion_event_get_x(const input_event_t* motion_event, size_t pointer_index) {
+float AMotionEvent_getX(const AInputEvent* motion_event, size_t pointer_index) {
     return static_cast<const MotionEvent*>(motion_event)->getX(pointer_index);
 }
 
-float motion_event_get_y(const input_event_t* motion_event, size_t pointer_index) {
+float AMotionEvent_getY(const AInputEvent* motion_event, size_t pointer_index) {
     return static_cast<const MotionEvent*>(motion_event)->getY(pointer_index);
 }
 
-float motion_event_get_pressure(const input_event_t* motion_event, size_t pointer_index) {
+float AMotionEvent_getPressure(const AInputEvent* motion_event, size_t pointer_index) {
     return static_cast<const MotionEvent*>(motion_event)->getPressure(pointer_index);
 }
 
-float motion_event_get_size(const input_event_t* motion_event, size_t pointer_index) {
+float AMotionEvent_getSize(const AInputEvent* motion_event, size_t pointer_index) {
     return static_cast<const MotionEvent*>(motion_event)->getSize(pointer_index);
 }
 
-size_t motion_event_get_history_size(const input_event_t* motion_event) {
+size_t AMotionEvent_getHistorySize(const AInputEvent* motion_event) {
     return static_cast<const MotionEvent*>(motion_event)->getHistorySize();
 }
 
-int64_t motion_event_get_historical_event_time(input_event_t* motion_event,
+int64_t AMotionEvent_getHistoricalEventTime(AInputEvent* motion_event,
         size_t history_index) {
     return static_cast<const MotionEvent*>(motion_event)->getHistoricalEventTime(
             history_index);
 }
 
-float motion_event_get_historical_raw_x(input_event_t* motion_event, size_t pointer_index,
+float AMotionEvent_getHistoricalRawX(AInputEvent* motion_event, size_t pointer_index,
         size_t history_index) {
     return static_cast<const MotionEvent*>(motion_event)->getHistoricalRawX(
             pointer_index, history_index);
 }
 
-float motion_event_get_historical_raw_y(input_event_t* motion_event, size_t pointer_index,
+float AMotionEvent_getHistoricalRawY(AInputEvent* motion_event, size_t pointer_index,
         size_t history_index) {
     return static_cast<const MotionEvent*>(motion_event)->getHistoricalRawY(
             pointer_index, history_index);
 }
 
-float motion_event_get_historical_x(input_event_t* motion_event, size_t pointer_index,
+float AMotionEvent_getHistoricalX(AInputEvent* motion_event, size_t pointer_index,
         size_t history_index) {
     return static_cast<const MotionEvent*>(motion_event)->getHistoricalX(
             pointer_index, history_index);
 }
 
-float motion_event_get_historical_y(input_event_t* motion_event, size_t pointer_index,
+float AMotionEvent_getHistoricalY(AInputEvent* motion_event, size_t pointer_index,
         size_t history_index) {
     return static_cast<const MotionEvent*>(motion_event)->getHistoricalY(
             pointer_index, history_index);
 }
 
-float motion_event_get_historical_pressure(input_event_t* motion_event, size_t pointer_index,
+float AMotionEvent_getHistoricalPressure(AInputEvent* motion_event, size_t pointer_index,
         size_t history_index) {
     return static_cast<const MotionEvent*>(motion_event)->getHistoricalPressure(
             pointer_index, history_index);
 }
 
-float motion_event_get_historical_size(input_event_t* motion_event, size_t pointer_index,
+float AMotionEvent_getHistoricalSize(AInputEvent* motion_event, size_t pointer_index,
         size_t history_index) {
     return static_cast<const MotionEvent*>(motion_event)->getHistoricalSize(
             pointer_index, history_index);
 }
 
-int input_queue_get_fd(input_queue_t* queue) {
+int AInputQueue_getFd(AInputQueue* queue) {
     return queue->getConsumer().getChannel()->getReceivePipeFd();
 }
 
-int input_queue_has_events(input_queue_t* queue) {
+int AInputQueue_hasEvents(AInputQueue* queue) {
     struct pollfd pfd;
     
     pfd.fd = queue->getConsumer().getChannel()->getReceivePipeFd();
@@ -200,7 +200,7 @@
     return pfd.revents == POLLIN ? 1 : -1;
 }
 
-int32_t input_queue_get_event(input_queue_t* queue, input_event_t** outEvent) {
+int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent) {
     *outEvent = NULL;
     
     int32_t res = queue->getConsumer().receiveDispatchSignal();
@@ -223,8 +223,17 @@
     return 0;
 }
 
-void input_queue_finish_event(input_queue_t* queue, input_event_t* event,
+void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event,
         int handled) {
+    if (!handled && ((InputEvent*)event)->getType() == INPUT_EVENT_TYPE_KEY
+            && ((KeyEvent*)event)->hasDefaultAction()) {
+        // The app didn't handle this, but it may have a default action
+        // associated with it.  We need to hand this back to Java to be
+        // executed.
+        queue->doDefaultKey((KeyEvent*)event);
+        return;
+    }
+    
     int32_t res = queue->getConsumer().sendFinishedSignal();
     if (res != android::OK) {
         LOGW("Failed to send finished signal on channel '%s'.  status=%d",
diff --git a/native/android/native_window.cpp b/native/android/native_window.cpp
new file mode 100644
index 0000000..448cbfc
--- /dev/null
+++ b/native/android/native_window.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Surface"
+#include <utils/Log.h>
+
+#include <android/native_window.h>
+#include <surfaceflinger/Surface.h>
+
+using android::Surface;
+
+static int32_t getWindowProp(ANativeWindow* window, int what) {
+    int value;
+    int res = window->query(window, what, &value);
+    return res < 0 ? res : value;
+}
+
+int32_t ANativeWindow_getWidth(ANativeWindow* window) {
+    return getWindowProp(window, NATIVE_WINDOW_WIDTH);
+}
+
+int32_t ANativeWindow_getHeight(ANativeWindow* window) {
+    return getWindowProp(window, NATIVE_WINDOW_HEIGHT);
+}
+
+int32_t ANativeWindow_getFormat(ANativeWindow* window) {
+    return getWindowProp(window, NATIVE_WINDOW_FORMAT);
+}
+
+int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width,
+        int32_t height, int32_t format) {
+    native_window_set_buffers_geometry(window, width, height, format);
+    return 0;
+}
diff --git a/native/include/android/input.h b/native/include/android/input.h
index 2441af0..7617662 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -125,8 +125,8 @@
  * Input events are opaque structures.  Use the provided accessors functions to
  * read their properties.
  */
-struct input_event_t;
-typedef struct input_event_t input_event_t;
+struct AInputEvent;
+typedef struct AInputEvent AInputEvent;
 
 /*
  * Input event types.
@@ -319,7 +319,7 @@
 /*** Accessors for all input events. ***/
 
 /* Get the input event type. */
-int32_t input_event_get_type(const input_event_t* event);
+int32_t AInputEvent_getType(const AInputEvent* event);
 
 /* Get the id for the device that an input event came from.
  *
@@ -331,128 +331,128 @@
  * other numbers are arbitrary and you shouldn't depend on the values.
  * Use the provided input device query API to obtain information about input devices.
  */
-int32_t input_event_get_device_id(const input_event_t* event);
+int32_t AInputEvent_getDeviceId(const AInputEvent* event);
 
 /* Get the input event nature. */
-int32_t input_event_get_nature(const input_event_t* event);
+int32_t AInputEvent_getNature(const AInputEvent* event);
 
 /*** Accessors for key events only. ***/
 
 /* Get the key event action. */
-int32_t key_event_get_action(const input_event_t* key_event);
+int32_t AKeyEvent_getAction(const AInputEvent* key_event);
 
 /* Get the key event flags. */
-int32_t key_event_get_flags(const input_event_t* key_event);
+int32_t AKeyEvent_getFlags(const AInputEvent* key_event);
 
 /* Get the key code of the key event.
  * This is the physical key that was pressed, not the Unicode character. */
-int32_t key_event_get_key_code(const input_event_t* key_event);
+int32_t AKeyEvent_getKeyCode(const AInputEvent* key_event);
 
 /* Get the hardware key id of this key event.
  * These values are not reliable and vary from device to device. */
-int32_t key_event_get_scan_code(const input_event_t* key_event);
+int32_t AKeyEvent_getScanCode(const AInputEvent* key_event);
 
 /* Get the meta key state. */
-int32_t key_event_get_meta_state(const input_event_t* key_event);
+int32_t AKeyEvent_getMetaState(const AInputEvent* key_event);
 
 /* Get the repeat count of the event.
  * For both key up an key down events, this is the number of times the key has
  * repeated with the first down starting at 0 and counting up from there.  For
  * multiple key events, this is the number of down/up pairs that have occurred. */
-int32_t key_event_get_repeat_count(const input_event_t* key_event);
+int32_t AKeyEvent_getRepeatCount(const AInputEvent* key_event);
 
 /* Get the time of the most recent key down event, in the
  * java.lang.System.nanoTime() time base.  If this is a down event,
  * this will be the same as eventTime.
  * Note that when chording keys, this value is the down time of the most recently
  * pressed key, which may not be the same physical key of this event. */
-int64_t key_event_get_down_time(const input_event_t* key_event);
+int64_t AKeyEvent_getDownTime(const AInputEvent* key_event);
 
 /* Get the time this event occurred, in the
  * java.lang.System.nanoTime() time base. */
-int64_t key_event_get_event_time(const input_event_t* key_event);
+int64_t AKeyEvent_getEventTime(const AInputEvent* key_event);
 
 /*** Accessors for motion events only. ***/
 
 /* Get the combined motion event action code and pointer index. */
-int32_t motion_event_get_action(const input_event_t* motion_event);
+int32_t AMotionEvent_getAction(const AInputEvent* motion_event);
 
 /* Get the state of any meta / modifier keys that were in effect when the
  * event was generated. */
-int32_t motion_event_get_meta_state(const input_event_t* motion_event);
+int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event);
 
 /* Get a bitfield indicating which edges, if any, were touched by this motion event.
  * For touch events, clients can use this to determine if the user's finger was
  * touching the edge of the display. */
-int32_t motion_event_get_edge_flags(const input_event_t* motion_event);
+int32_t AMotionEvent_getEdgeFlags(const AInputEvent* motion_event);
 
 /* Get the time when the user originally pressed down to start a stream of
  * position events, in the java.lang.System.nanoTime() time base. */
-int64_t motion_event_get_down_time(const input_event_t* motion_event);
+int64_t AMotionEvent_getDownTime(const AInputEvent* motion_event);
 
 /* Get the time when this specific event was generated,
  * in the java.lang.System.nanoTime() time base. */
-int64_t motion_event_get_event_time(const input_event_t* motion_event);
+int64_t AMotionEvent_getEventTime(const AInputEvent* motion_event);
 
 /* Get the X coordinate offset.
  * For touch events on the screen, this is the delta that was added to the raw
  * screen coordinates to adjust for the absolute position of the containing windows
  * and views. */
-float motion_event_get_x_offset(const input_event_t* motion_event);
+float AMotionEvent_getXOffset(const AInputEvent* motion_event);
 
 /* Get the precision of the Y coordinates being reported.
  * For touch events on the screen, this is the delta that was added to the raw
  * screen coordinates to adjust for the absolute position of the containing windows
  * and views. */
-float motion_event_get_y_offset(const input_event_t* motion_event);
+float AMotionEvent_getYOffset(const AInputEvent* motion_event);
 
 /* Get the precision of the X coordinates being reported.
  * You can multiply this number with an X coordinate sample to find the
  * actual hardware value of the X coordinate. */
-float motion_event_get_x_precision(const input_event_t* motion_event);
+float AMotionEvent_getXPrecision(const AInputEvent* motion_event);
 
 /* Get the precision of the Y coordinates being reported.
  * You can multiply this number with a Y coordinate sample to find the
  * actual hardware value of the Y coordinate. */
-float motion_event_get_y_precision(const input_event_t* motion_event);
+float AMotionEvent_getYPrecision(const AInputEvent* motion_event);
 
 /* Get the number of pointers of data contained in this event.
  * Always >= 1. */
-size_t motion_event_get_pointer_count(const input_event_t* motion_event);
+size_t AMotionEvent_getPointerCount(const AInputEvent* motion_event);
 
 /* Get the pointer identifier associated with a particular pointer
  * data index is this event.  The identifier tells you the actual pointer
  * number associated with the data, accounting for individual pointers
  * going up and down since the start of the current gesture. */
-int32_t motion_event_get_pointer_id(const input_event_t* motion_event, size_t pointer_index);
+int32_t AMotionEvent_getPointerId(const AInputEvent* motion_event, size_t pointer_index);
 
 /* Get the original raw X coordinate of this event.
  * For touch events on the screen, this is the original location of the event
  * on the screen, before it had been adjusted for the containing window
  * and views. */
-float motion_event_get_raw_x(const input_event_t* motion_event, size_t pointer_index);
+float AMotionEvent_getRawX(const AInputEvent* motion_event, size_t pointer_index);
 
 /* Get the original raw X coordinate of this event.
  * For touch events on the screen, this is the original location of the event
  * on the screen, before it had been adjusted for the containing window
  * and views. */
-float motion_event_get_raw_y(const input_event_t* motion_event, size_t pointer_index);
+float AMotionEvent_getRawY(const AInputEvent* motion_event, size_t pointer_index);
 
 /* Get the current X coordinate of this event for the given pointer index.
  * Whole numbers are pixels; the value may have a fraction for input devices
  * that are sub-pixel precise. */
-float motion_event_get_x(const input_event_t* motion_event, size_t pointer_index);
+float AMotionEvent_getX(const AInputEvent* motion_event, size_t pointer_index);
 
 /* Get the current Y coordinate of this event for the given pointer index.
  * Whole numbers are pixels; the value may have a fraction for input devices
  * that are sub-pixel precise. */
-float motion_event_get_y(const input_event_t* motion_event, size_t pointer_index);
+float AMotionEvent_getY(const AInputEvent* motion_event, size_t pointer_index);
 
 /* Get the current pressure of this event for the given pointer index.
  * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure),
  * however values higher than 1 may be generated depending on the calibration of
  * the input device. */
-float motion_event_get_pressure(const input_event_t* motion_event, size_t pointer_index);
+float AMotionEvent_getPressure(const AInputEvent* motion_event, size_t pointer_index);
 
 /* Get the current scaled value of the approximate size for the given pointer index.
  * This represents some approximation of the area of the screen being
@@ -460,17 +460,17 @@
  * touch is normalized with the device specific range of values
  * and scaled to a value between 0 and 1.  The value of size can be used to
  * determine fat touch events. */
-float motion_event_get_size(const input_event_t* motion_event, size_t pointer_index);
+float AMotionEvent_getSize(const AInputEvent* motion_event, size_t pointer_index);
 
 /* Get the number of historical points in this event.  These are movements that
  * have occurred between this event and the previous event.  This only applies
  * to MOTION_EVENT_ACTION_MOVE events -- all other actions will have a size of 0.
  * Historical samples are indexed from oldest to newest. */
-size_t motion_event_get_history_size(const input_event_t* motion_event);
+size_t AMotionEvent_get_history_size(const AInputEvent* motion_event);
 
 /* Get the time that a historical movement occurred between this event and
  * the previous event, in the java.lang.System.nanoTime() time base. */
-int64_t motion_event_get_historical_event_time(input_event_t* motion_event,
+int64_t AMotionEvent_getHistoricalEventTime(AInputEvent* motion_event,
         size_t history_index);
 
 /* Get the historical raw X coordinate of this event for the given pointer index that
@@ -480,7 +480,7 @@
  * and views.
  * Whole numbers are pixels; the value may have a fraction for input devices
  * that are sub-pixel precise. */
-float motion_event_get_historical_raw_x(const input_event_t* motion_event, size_t pointer_index);
+float AMotionEvent_getHistoricalRawX(const AInputEvent* motion_event, size_t pointer_index);
 
 /* Get the historical raw Y coordinate of this event for the given pointer index that
  * occurred between this event and the previous motion event.
@@ -489,20 +489,20 @@
  * and views.
  * Whole numbers are pixels; the value may have a fraction for input devices
  * that are sub-pixel precise. */
-float motion_event_get_historical_raw_y(const input_event_t* motion_event, size_t pointer_index);
+float AMotionEvent_getHistoricalRawY(const AInputEvent* motion_event, size_t pointer_index);
 
 /* Get the historical X coordinate of this event for the given pointer index that
  * occurred between this event and the previous motion event.
  * Whole numbers are pixels; the value may have a fraction for input devices
  * that are sub-pixel precise. */
-float motion_event_get_historical_x(input_event_t* motion_event, size_t pointer_index,
+float AMotionEvent_getHistoricalX(AInputEvent* motion_event, size_t pointer_index,
         size_t history_index);
 
 /* Get the historical Y coordinate of this event for the given pointer index that
  * occurred between this event and the previous motion event.
  * Whole numbers are pixels; the value may have a fraction for input devices
  * that are sub-pixel precise. */
-float motion_event_get_historical_y(input_event_t* motion_event, size_t pointer_index,
+float AMotionEvent_getHistoricalY(AInputEvent* motion_event, size_t pointer_index,
         size_t history_index);
 
 /* Get the historical pressure of this event for the given pointer index that
@@ -510,7 +510,7 @@
  * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure),
  * however values higher than 1 may be generated depending on the calibration of
  * the input device. */
-float motion_event_get_historical_pressure(input_event_t* motion_event, size_t pointer_index,
+float AMotionEvent_getHistoricalPressure(AInputEvent* motion_event, size_t pointer_index,
         size_t history_index);
 
 /* Get the current scaled value of the approximate size for the given pointer index that
@@ -520,7 +520,7 @@
  * touch is normalized with the device specific range of values
  * and scaled to a value between 0 and 1.  The value of size can be used to
  * determine fat touch events. */
-float motion_event_get_historical_size(input_event_t* motion_event, size_t pointer_index,
+float AMotionEvent_getHistoricalSize(AInputEvent* motion_event, size_t pointer_index,
         size_t history_index);
 
 /*
@@ -529,8 +529,8 @@
  * An input queue is the facility through which you retrieve input
  * events.
  */
-struct input_queue_t;
-typedef struct input_queue_t input_queue_t;
+struct AInputQueue;
+typedef struct AInputQueue AInputQueue;
 
 /*
  * Return a file descriptor for the queue, which you
@@ -538,26 +538,26 @@
  * is typically used with select() or poll() to multiplex
  * with other kinds of events.
  */
-int input_queue_get_fd(input_queue_t* queue);
+int AInputQueue_getFd(AInputQueue* queue);
 
 /*
  * Returns true if there are one or more events available in the
  * input queue.  Returns 1 if the queue has events; 0 if
  * it does not have events; and a negative value if there is an error.
  */
-int input_queue_has_events(input_queue_t* queue);
+int AInputQueue_hasEvents(AInputQueue* queue);
 
 /*
  * Returns the next available event from the queue.  Returns a negative
  * value if no events are available or an error has occurred.
  */
-int32_t input_queue_get_event(input_queue_t* queue, input_event_t** outEvent);
+int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent);
 
 /*
  * Report that dispatching has finished with the given event.
- * This must be called after receiving an event with input_queue_get_event().
+ * This must be called after receiving an event with AInputQueue_get_event().
  */
-void input_queue_finish_event(input_queue_t* queue, input_event_t* event, int handled);
+void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled);
 
 #ifdef __cplusplus
 }
diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h
index a58a7d2..bf5c641 100644
--- a/native/include/android/native_activity.h
+++ b/native/include/android/native_activity.h
@@ -24,36 +24,40 @@
 #include <jni.h>
 
 #include <android/input.h>
+#include <android/native_window.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-// Temporary until native surface API is defined.
-struct android_surface_t;
-typedef struct android_surface_t android_surface_t;
-
-struct android_activity_callbacks_t;
+struct ANativeActivityCallbacks;
 
 /**
  * This structure defines the native side of an android.app.NativeActivity.
  * It is created by the framework, and handed to the application's native
  * code as it is being launched.
  */
-typedef struct android_activity_t {
+typedef struct ANativeActivity {
     /**
      * Pointer to the callback function table of the native application.
      * You can set the functions here to your own callbacks.  The callbacks
      * pointer itself here should not be changed; it is allocated and managed
      * for you by the framework.
      */
-    struct android_activity_callbacks_t* callbacks;
+    struct ANativeActivityCallbacks* callbacks;
 
     /**
-     * JNI context for the main thread of the app.
+     * The global handle on the process's Java VM.
+     */
+    JavaVM* vm;
+
+    /**
+     * JNI context for the main thread of the app.  Note that this field
+     * can ONLY be used from the main thread of the process; that is, the
+     * thread that calls into the ANativeActivityCallbacks.
      */
     JNIEnv* env;
-    
+
     /**
      * The NativeActivity Java class.
      */
@@ -65,7 +69,7 @@
      * state.
      */
     void* instance;
-} android_activity_t;
+} ANativeActivity;
 
 /**
  * These are the callbacks the framework makes into a native application.
@@ -73,18 +77,18 @@
  * By default, all callbacks are NULL; set to a pointer to your own function
  * to have it called.
  */
-typedef struct android_activity_callbacks_t {
+typedef struct ANativeActivityCallbacks {
     /**
      * NativeActivity has started.  See Java documentation for Activity.onStart()
      * for more information.
      */
-    void (*onStart)(android_activity_t* activity);
+    void (*onStart)(ANativeActivity* activity);
     
     /**
      * NativeActivity has resumed.  See Java documentation for Activity.onResume()
      * for more information.
      */
-    void (*onResume)(android_activity_t* activity);
+    void (*onResume)(ANativeActivity* activity);
     
     /**
      * Framework is asking NativeActivity to save its current instance state.
@@ -95,78 +99,68 @@
      * saved state will be persisted, so it can not contain any active
      * entities (pointers to memory, file descriptors, etc).
      */
-    void* (*onSaveInstanceState)(android_activity_t* activity, size_t* outSize);
+    void* (*onSaveInstanceState)(ANativeActivity* activity, size_t* outSize);
     
     /**
      * NativeActivity has paused.  See Java documentation for Activity.onPause()
      * for more information.
      */
-    void (*onPause)(android_activity_t* activity);
+    void (*onPause)(ANativeActivity* activity);
     
     /**
      * NativeActivity has stopped.  See Java documentation for Activity.onStop()
      * for more information.
      */
-    void (*onStop)(android_activity_t* activity);
+    void (*onStop)(ANativeActivity* activity);
     
     /**
      * NativeActivity is being destroyed.  See Java documentation for Activity.onDestroy()
      * for more information.
      */
-    void (*onDestroy)(android_activity_t* activity);
+    void (*onDestroy)(ANativeActivity* activity);
 
     /**
      * Focus has changed in this NativeActivity's window.  This is often used,
      * for example, to pause a game when it loses input focus.
      */
-    void (*onWindowFocusChanged)(android_activity_t* activity, int hasFocus);
+    void (*onWindowFocusChanged)(ANativeActivity* activity, int hasFocus);
     
     /**
-     * The drawing surface for this native activity has been created.  You
-     * can use the given surface object to start drawing.  NOTE: surface
-     * drawing API is not yet defined.
+     * The drawing window for this native activity has been created.  You
+     * can use the given native window object to start drawing.
      */
-    void (*onSurfaceCreated)(android_activity_t* activity, android_surface_t* surface);
+    void (*onNativeWindowCreated)(ANativeActivity* activity, ANativeWindow* window);
 
     /**
-     * The drawing surface for this native activity has changed.  The surface
-     * given here is guaranteed to be the same as the one last given to
-     * onSurfaceCreated.  This is simply to inform you about interesting
-     * changed to that surface.
-     */
-    void (*onSurfaceChanged)(android_activity_t* activity, android_surface_t* surface,
-            int format, int width, int height);
-
-    /**
-     * The drawing surface for this native activity is going to be destroyed.
-     * You MUST ensure that you do not touch the surface object after returning
-     * from this function: in the common case of drawing to the surface from
+     * The drawing window for this native activity is going to be destroyed.
+     * You MUST ensure that you do not touch the window object after returning
+     * from this function: in the common case of drawing to the window from
      * another thread, that means the implementation of this callback must
      * properly synchronize with the other thread to stop its drawing before
      * returning from here.
      */
-    void (*onSurfaceDestroyed)(android_activity_t* activity, android_surface_t* surface);
+    void (*onNativeWindowDestroyed)(ANativeActivity* activity, ANativeWindow* window);
     
     /**
      * The input queue for this native activity's window has been created.
      * You can use the given input queue to start retrieving input events.
      */
-    void (*onInputQueueCreated)(android_activity_t* activity, input_queue_t* queue);
+    void (*onInputQueueCreated)(ANativeActivity* activity, AInputQueue* queue);
     
     /**
      * The input queue for this native activity's window is being destroyed.
      * You should no longer try to reference this object upon returning from this
      * function.
      */
-    void (*onInputQueueDestroyed)(android_activity_t* activity, input_queue_t* queue);
+    void (*onInputQueueDestroyed)(ANativeActivity* activity, AInputQueue* queue);
 
     /**
      * The system is running low on memory.  Use this callback to release
      * resources you do not need, to help the system avoid killing more
      * important processes.
      */
-    void (*onLowMemory)(android_activity_t* activity);
-} android_activity_callbacks_t;
+    void (*onLowMemory)(ANativeActivity* activity);
+} ANativeActivityCallbacks;
 
 /**
  * This is the function that must be in the native code to instantiate the
@@ -174,14 +168,14 @@
  * above); if the code is being instantiated from a previously saved instance,
  * the savedState will be non-NULL and point to the saved data.
  */
-typedef void android_activity_create_t(android_activity_t* activity,
+typedef void ANativeActivity_createFunc(ANativeActivity* activity,
         void* savedState, size_t savedStateSize);
 
 /**
  * The name of the function that NativeInstance looks for when launching its
  * native code.
  */
-extern android_activity_create_t android_onCreateActivity;
+extern ANativeActivity_createFunc ANativeActivity_onCreate;
 
 #ifdef __cplusplus
 };
diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h
new file mode 100644
index 0000000..678ba3d
--- /dev/null
+++ b/native/include/android/native_window.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_NATIVE_WINDOW_H
+#define ANDROID_NATIVE_WINDOW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Pixel formats that a window can use.
+ */
+enum {
+    WINDOW_FORMAT_RGBA_8888          = 1,
+    WINDOW_FORMAT_RGBX_8888          = 2,
+    WINDOW_FORMAT_RGB_565            = 4,
+};
+
+struct ANativeWindow;
+typedef struct ANativeWindow ANativeWindow;
+
+/*
+ * Return the current width in pixels of the window surface.  Returns a
+ * negative value on error.
+ */
+int32_t ANativeWindow_getWidth(ANativeWindow* window);
+
+/*
+ * Return the current height in pixels of the window surface.  Returns a
+ * negative value on error.
+ */
+int32_t ANativeWindow_getHeight(ANativeWindow* window);
+
+/*
+ * Return the current pixel format of the window surface.  Returns a
+ * negative value on error.
+ */
+int32_t ANativeWindow_getFormat(ANativeWindow* window);
+
+/*
+ * Change the format and size of the window buffers.
+ *
+ * The width and height control the number of pixels in the buffers, not the
+ * dimensions of the window on screen.  If these are different than the
+ * window's physical size, then it buffer will be scaled to match that size
+ * when compositing it to the screen.
+ *
+ * The format may be one of the window format constants above.
+ *
+ * For all of these parameters, if 0 is supplied than the window's base
+ * value will come back in force.
+ */
+int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width,
+        int32_t height, int32_t format);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_NATIVE_WINDOW_H
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index a2c6a7d..25d7697 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -91,10 +91,11 @@
 
 #elif defined(ANDROID)
 
-struct android_native_window_t;
+#include <android/native_window.h>
+
 struct egl_native_pixmap_t;
 
-typedef struct android_native_window_t* EGLNativeWindowType;
+typedef struct ANativeWindow*           EGLNativeWindowType;
 typedef struct egl_native_pixmap_t*     EGLNativePixmapType;
 typedef void*                           EGLNativeDisplayType;
 
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 7cb01d0..54d7307 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -213,7 +213,7 @@
     egl_window_surface_v2_t(
             EGLDisplay dpy, EGLConfig config,
             int32_t depthFormat,
-            android_native_window_t* window);
+            ANativeWindow* window);
 
     ~egl_window_surface_v2_t();
 
@@ -235,7 +235,7 @@
 private:
     status_t lock(android_native_buffer_t* buf, int usage, void** vaddr);
     status_t unlock(android_native_buffer_t* buf);
-    android_native_window_t*   nativeWindow;
+    ANativeWindow*   nativeWindow;
     android_native_buffer_t*   buffer;
     android_native_buffer_t*   previousBuffer;
     gralloc_module_t const*    module;
@@ -355,7 +355,7 @@
 egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy,
         EGLConfig config,
         int32_t depthFormat,
-        android_native_window_t* window)
+        ANativeWindow* window)
     : egl_surface_t(dpy, config, depthFormat), 
     nativeWindow(window), buffer(0), previousBuffer(0), module(0),
     blitengine(0), bits(NULL)
@@ -1300,7 +1300,7 @@
     if (!(surfaceType & EGL_WINDOW_BIT))
         return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
 
-    if (static_cast<android_native_window_t*>(window)->common.magic !=
+    if (static_cast<ANativeWindow*>(window)->common.magic !=
             ANDROID_NATIVE_WINDOW_MAGIC) {
         return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
     }
@@ -1323,7 +1323,7 @@
 
     egl_surface_t* surface;
     surface = new egl_window_surface_v2_t(dpy, config, depthFormat,
-            static_cast<android_native_window_t*>(window));
+            static_cast<ANativeWindow*>(window));
 
     if (!surface->initCheck()) {
         // there was a problem in the ctor, the error
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 74d87ba..75045d7 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -1,6 +1,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.systemui"
-        android:sharedUserId="android.uid.system">
+        android:sharedUserId="android.uid.system"
+        android:process="system"
+        >
 
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
 
@@ -10,7 +12,7 @@
         android:icon="@drawable/ic_launcher_settings">
                  
         <service
-            android:name=".statusbar.PhoneStatusBarService"
+            android:name=".statusbar.StatusBarService"
             android:exported="false"
             />
 
diff --git a/packages/SystemUI/res/drawable-hdpi/alert_bar_background_normal.9.png b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_normal.9.png
new file mode 100644
index 0000000..bc127bd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/alert_bar_background_pressed.9.png b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_pressed.9.png
new file mode 100644
index 0000000..59af804
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/alert_bar_background_normal.9.png b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_normal.9.png
new file mode 100644
index 0000000..258de13
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/alert_bar_background_pressed.9.png b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_pressed.9.png
new file mode 100644
index 0000000..258de13
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/alert_bar_background.xml b/packages/SystemUI/res/drawable/alert_bar_background.xml
new file mode 100644
index 0000000..24b6aa3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/alert_bar_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/alert_bar_background_pressed" />
+    <item
+         android:drawable="@drawable/alert_bar_background_normal" />
+</selector>
+
diff --git a/packages/SystemUI/res/layout/intruder_alert.xml b/packages/SystemUI/res/layout/intruder_alert.xml
new file mode 100644
index 0000000..24eb960
--- /dev/null
+++ b/packages/SystemUI/res/layout/intruder_alert.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<!--    android:background="@drawable/status_bar_closed_default_background" -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="32dip"
+    android:layout_width="match_parent"
+    android:paddingLeft="8dip"
+    android:paddingRight="8dip"
+    >
+        
+    <LinearLayout 
+        android:id="@+id/intruder_alert_content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:animationCache="false"
+        android:orientation="horizontal"
+        android:background="@drawable/alert_bar_background"
+        android:clickable="true"
+        android:focusable="true"
+        android:descendantFocusability="afterDescendants"
+        >
+
+        <ImageView
+            android:id="@+id/alertIcon"
+            android:layout_width="25dip"
+            android:layout_height="25dip"
+            android:paddingLeft="6dip"
+            android:layout_marginRight="8dip"
+            />
+        <TextView
+            android:id="@+id/alertText"
+            android:textAppearance="@style/TextAppearance.StatusBar.IntruderAlert"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            />
+    </LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 10fa930..816f34a4 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -22,6 +22,7 @@
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
-
-
+    <style name="TextAppearance.StatusBar.IntruderAlert"
+        parent="@android:style/TextAppearance.StatusBar">
+    </style>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
index 0f6723e..f45caf51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
@@ -23,7 +23,7 @@
 
 
 public class CloseDragHandle extends LinearLayout {
-    PhoneStatusBarService mService;
+    StatusBarService mService;
 
     public CloseDragHandle(Context context, AttributeSet attrs) {
         super(context, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
index c5b901f..3d85f27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
@@ -27,7 +27,7 @@
 
 
 public class ExpandedView extends LinearLayout {
-    PhoneStatusBarService mService;
+    StatusBarService mService;
     int mPrevHeight = -1;
 
     public ExpandedView(Context context, AttributeSet attrs) {
@@ -50,10 +50,10 @@
          super.onLayout(changed, left, top, right, bottom);
          int height = bottom - top;
          if (height != mPrevHeight) {
-             //Slog.d(PhoneStatusBarService.TAG, "height changed old=" + mPrevHeight
+             //Slog.d(StatusBarService.TAG, "height changed old=" + mPrevHeight
              //     + " new=" + height);
              mPrevHeight = height;
-             mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
+             mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
          }
      }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
deleted file mode 100644
index 5d16e93..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
+++ /dev/null
@@ -1,1493 +0,0 @@
-/*
- * 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.systemui.statusbar;
-
-import com.android.internal.statusbar.IStatusBar;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
-
-import android.app.ActivityManagerNative;
-import android.app.Dialog;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.LinearLayout;
-import android.widget.RemoteViews;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import android.widget.FrameLayout;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Set;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.StatusBarPolicy;
-
-public class PhoneStatusBarService extends StatusBarService {
-    static final String TAG = "PhoneStatusBarService";
-    static final boolean SPEW = false;
-
-    public static final String ACTION_STATUSBAR_START
-            = "com.android.internal.policy.statusbar.START";
-
-    static final int EXPANDED_LEAVE_ALONE = -10000;
-    static final int EXPANDED_FULL_OPEN = -10001;
-
-    private static final int MSG_ANIMATE = 1000;
-    private static final int MSG_ANIMATE_REVEAL = 1001;
-
-    private class ExpandedDialog extends Dialog {
-        ExpandedDialog(Context context) {
-            super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
-        }
-
-        @Override
-        public boolean dispatchKeyEvent(KeyEvent event) {
-            boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
-            switch (event.getKeyCode()) {
-            case KeyEvent.KEYCODE_BACK:
-                if (!down) {
-                    animateCollapse();
-                }
-                return true;
-            }
-            return super.dispatchKeyEvent(event);
-        }
-    }
-
-    StatusBarPolicy mIconPolicy;
-    
-    int mHeight;
-    int mIconWidth;
-
-    Display mDisplay;
-    StatusBarView mStatusBarView;
-    int mPixelFormat;
-    H mHandler = new H();
-    Object mQueueLock = new Object();
-    
-    // icons
-    LinearLayout mIcons;
-    IconMerger mNotificationIcons;
-    LinearLayout mStatusIcons;
-
-    // expanded notifications
-    Dialog mExpandedDialog;
-    ExpandedView mExpandedView;
-    WindowManager.LayoutParams mExpandedParams;
-    ScrollView mScrollView;
-    View mNotificationLinearLayout;
-    View mExpandedContents;
-    // top bar
-    TextView mNoNotificationsTitle;
-    TextView mClearButton;
-    // drag bar
-    CloseDragHandle mCloseView;
-    // ongoing
-    NotificationData mOngoing = new NotificationData();
-    TextView mOngoingTitle;
-    LinearLayout mOngoingItems;
-    // latest
-    NotificationData mLatest = new NotificationData();
-    TextView mLatestTitle;
-    LinearLayout mLatestItems;
-    // position
-    int[] mPositionTmp = new int[2];
-    boolean mExpanded;
-    boolean mExpandedVisible;
-
-    // the date view
-    DateView mDateView;
-
-    // the tracker view
-    TrackingView mTrackingView;
-    WindowManager.LayoutParams mTrackingParams;
-    int mTrackingPosition; // the position of the top of the tracking view.
-    private boolean mPanelSlightlyVisible;
-
-    // ticker
-    private Ticker mTicker;
-    private View mTickerView;
-    private boolean mTicking;
-    
-    // Tracking finger for opening/closing.
-    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
-    boolean mTracking;
-    VelocityTracker mVelocityTracker;
-    
-    static final int ANIM_FRAME_DURATION = (1000/60);
-    
-    boolean mAnimating;
-    long mCurAnimationTime;
-    float mDisplayHeight;
-    float mAnimY;
-    float mAnimVel;
-    float mAnimAccel;
-    long mAnimLastTime;
-    boolean mAnimatingReveal = false;
-    int mViewDelta;
-    int[] mAbsPos = new int[2];
-    
-    // for disabling the status bar
-    int mDisabled = 0;
-
-    /**
-     * Construct the service, add the status bar view to the window manager
-     */
-    @Override
-    public void onCreate() {
-        // First set up our views and stuff.
-        mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
-        makeStatusBarView(this);
-
-        // Next, call super.onCreate(), which will populate our views.
-        super.onCreate();
-
-        // Lastly, call to the icon policy to install/update all the icons.
-        mIconPolicy = new StatusBarPolicy(this);
-    }
-
-    // ================================================================================
-    // Constructing the view
-    // ================================================================================
-    private void makeStatusBarView(Context context) {
-        Resources res = context.getResources();
-
-        mHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
-        mIconWidth = mHeight;
-
-        ExpandedView expanded = (ExpandedView)View.inflate(context,
-                R.layout.status_bar_expanded, null);
-        expanded.mService = this;
-        StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
-        sb.mService = this;
-
-        // figure out which pixel-format to use for the status bar.
-        mPixelFormat = PixelFormat.TRANSLUCENT;
-        Drawable bg = sb.getBackground();
-        if (bg != null) {
-            mPixelFormat = bg.getOpacity();
-        }
-
-        mStatusBarView = sb;
-        mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
-        mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
-        mIcons = (LinearLayout)sb.findViewById(R.id.icons);
-        mTickerView = sb.findViewById(R.id.ticker);
-        mDateView = (DateView)sb.findViewById(R.id.date);
-
-        mExpandedDialog = new ExpandedDialog(context);
-        mExpandedView = expanded;
-        mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
-        mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
-        mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
-        mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
-        mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
-        mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
-        mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
-        mClearButton.setOnClickListener(mClearButtonListener);
-        mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
-        mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
-
-        mOngoingTitle.setVisibility(View.GONE);
-        mLatestTitle.setVisibility(View.GONE);
-        
-        mTicker = new MyTicker(context, sb);
-
-        TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
-        tickerView.mTicker = mTicker;
-
-        mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null);
-        mTrackingView.mService = this;
-        mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
-        mCloseView.mService = this;
-
-        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
-        // the more notifications icon
-        StatusBarIconView moreView = new StatusBarIconView(this, "more");
-        moreView.set(new StatusBarIcon(null, R.drawable.stat_notify_more, 0));
-        mNotificationIcons.addMoreView(moreView,
-                new LinearLayout.LayoutParams(mIconWidth, mHeight));
-
-        // set the inital view visibility
-        setAreThereNotifications();
-        mDateView.setVisibility(View.INVISIBLE);
-
-        // receive broadcasts
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        context.registerReceiver(mBroadcastReceiver, filter);
-    }
-
-    @Override
-    protected void addStatusBarView() {
-        final StatusBarView view = mStatusBarView;
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                mHeight,
-                WindowManager.LayoutParams.TYPE_STATUS_BAR,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
-                PixelFormat.RGBX_8888);
-        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
-        lp.setTitle("StatusBar");
-        // TODO lp.windowAnimations = R.style.Animation_StatusBar;
-
-        WindowManagerImpl.getDefault().addView(view, lp);
-    }
-
-    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
-        Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
-                + " icon=" + icon);
-        StatusBarIconView view = new StatusBarIconView(this, slot);
-        view.set(icon);
-        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconWidth, mHeight));
-    }
-
-    public void updateIcon(String slot, int index, int viewIndex,
-            StatusBarIcon old, StatusBarIcon icon) {
-        Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
-                + " old=" + old + " icon=" + icon);
-        StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
-        view.set(icon);
-    }
-
-    public void removeIcon(String slot, int index, int viewIndex) {
-        Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
-        mStatusIcons.removeViewAt(viewIndex);
-    }
-
-    public void addNotification(IBinder key, StatusBarNotification notification) {
-        addNotificationViews(key, notification);
-
-        boolean immersive = false;
-        try {
-            immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
-            Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
-        } catch (RemoteException ex) {
-        }
-        if (immersive) {
-            if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
-                Slog.d(TAG, "Presenting high-priority notification in immersive activity");
-                // @@@ special new transient ticker mode
-                /*
-                // 1. Populate mAlertBarView
-
-                ImageView alertIcon = (ImageView) mAlertBarView.findViewById(R.id.alertIcon);
-                TextView alertText = (TextView) mAlertBarView.findViewById(R.id.alertText);
-                alertIcon.setImageDrawable(StatusBarIconView.getIcon(
-                    alertIcon.getContext(), 
-                    iconView.getStatusBarIcon()));
-                alertText.setText(notification.notification.tickerText);
-
-                // 2. Animate mAlertBarView in
-                mAlertBarView.setVisibility(View.VISIBLE);
-
-                // 3. Set alarm to age the notification off (TODO)
-                */
-            }
-        } else if (notification.notification.fullScreenIntent != null) {
-            // not immersive & a full-screen alert should be shown
-            Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive; sending fullScreenIntent");
-            try {
-                notification.notification.fullScreenIntent.send();
-            } catch (PendingIntent.CanceledException e) {
-            }
-        } else {
-            // usual case: status bar visible & not immersive
-
-            // show the ticker
-            tick(notification);
-        }
-
-        // Recalculate the position of the sliding windows and the titles.
-        setAreThereNotifications();
-        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
-    }
-
-    public void updateNotification(IBinder key, StatusBarNotification notification) {
-        Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification);
-
-        NotificationData oldList;
-        int oldIndex = mOngoing.findEntry(key);
-        if (oldIndex >= 0) {
-            oldList = mOngoing;
-        } else {
-            oldIndex = mLatest.findEntry(key);
-            if (oldIndex < 0) {
-                Slog.w(TAG, "updateNotification for unknown key: " + key);
-                return;
-            }
-            oldList = mLatest;
-        }
-        final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex);
-        final StatusBarNotification oldNotification = oldEntry.notification;
-        final RemoteViews oldContentView = oldNotification.notification.contentView;
-
-        final RemoteViews contentView = notification.notification.contentView;
-
-        if (false) {
-            Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
-                    + " ongoing=" + oldNotification.isOngoing()
-                    + " expanded=" + oldEntry.expanded
-                    + " contentView=" + oldContentView);
-            Slog.d(TAG, "new notification: when=" + notification.notification.when
-                    + " ongoing=" + oldNotification.isOngoing()
-                    + " contentView=" + contentView);
-        }
-
-        // Can we just reapply the RemoteViews in place?  If when didn't change, the order
-        // didn't change.
-        if (notification.notification.when == oldNotification.notification.when
-                && notification.isOngoing() == oldNotification.isOngoing()
-                && oldEntry.expanded != null
-                && contentView != null && oldContentView != null
-                && contentView.getPackage() != null
-                && oldContentView.getPackage() != null
-                && oldContentView.getPackage().equals(contentView.getPackage())
-                && oldContentView.getLayoutId() == contentView.getLayoutId()) {
-            Slog.d(TAG, "reusing notification");
-            oldEntry.notification = notification;
-            try {
-                // Reapply the RemoteViews
-                contentView.reapply(this, oldEntry.content);
-                // update the contentIntent
-                final PendingIntent contentIntent = notification.notification.contentIntent;
-                if (contentIntent != null) {
-                    oldEntry.content.setOnClickListener(new Launcher(contentIntent,
-                                notification.pkg, notification.tag, notification.id));
-                }
-                // Update the icon.
-                final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
-                        notification.notification.icon, notification.notification.iconLevel,
-                        notification.notification.number);
-                if (!oldEntry.icon.set(ic)) {
-                    handleNotificationError(key, notification, "Couldn't update icon: " + ic);
-                    return;
-                }
-            }
-            catch (RuntimeException e) {
-                // It failed to add cleanly.  Log, and remove the view from the panel.
-                Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
-                removeNotificationViews(key);
-                addNotificationViews(key, notification);
-            }
-        } else {
-            Slog.d(TAG, "not reusing notification");
-            removeNotificationViews(key);
-            addNotificationViews(key, notification);
-        }
-
-        // Restart the ticker if it's still running
-        tick(notification);
-
-        // Recalculate the position of the sliding windows and the titles.
-        setAreThereNotifications();
-        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
-    }
-
-    public void removeNotification(IBinder key) {
-        Slog.d(TAG, "removeNotification key=" + key);
-        StatusBarNotification old = removeNotificationViews(key);
-
-        if (old != null) {
-            // Cancel the ticker if it's still running
-            mTicker.removeEntry(old);
-
-            // Recalculate the position of the sliding windows and the titles.
-            setAreThereNotifications();
-            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
-        }
-    }
-
-    private int chooseIconIndex(boolean isOngoing, int viewIndex) {
-        final int latestSize = mLatest.size();
-        if (isOngoing) {
-            return latestSize + (mOngoing.size() - viewIndex);
-        } else {
-            return latestSize - viewIndex;
-        }
-    }
-
-    View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
-        Notification n = notification.notification;
-        RemoteViews remoteViews = n.contentView;
-        if (remoteViews == null) {
-            return null;
-        }
-
-        // create the row view
-        LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
-
-        // bind the click event to the content area
-        ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
-        content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-        content.setOnFocusChangeListener(mFocusChangeListener);
-        PendingIntent contentIntent = n.contentIntent;
-        if (contentIntent != null) {
-            content.setOnClickListener(new Launcher(contentIntent, notification.pkg,
-                        notification.tag, notification.id));
-        }
-
-        View expanded = null;
-        Exception exception = null;
-        try {
-            expanded = remoteViews.apply(this, content);
-        }
-        catch (RuntimeException e) {
-            exception = e;
-        }
-        if (expanded == null) {
-            String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id);
-            Slog.e(TAG, "couldn't inflate view for notification " + ident);
-            return null;
-        } else {
-            content.addView(expanded);
-            row.setDrawingCacheEnabled(true);
-        }
-
-        return new View[] { row, content, expanded };
-    }
-
-    void addNotificationViews(IBinder key, StatusBarNotification notification) {
-        NotificationData list;
-        ViewGroup parent;
-        final boolean isOngoing = notification.isOngoing();
-        if (isOngoing) {
-            list = mOngoing;
-            parent = mOngoingItems;
-        } else {
-            list = mLatest;
-            parent = mLatestItems;
-        }
-        // Construct the expanded view.
-        final View[] views = makeNotificationView(notification, parent);
-        if (views == null) {
-            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
-                    + notification);
-            return;
-        }
-        final View row = views[0];
-        final View content = views[1];
-        final View expanded = views[2];
-        // Construct the icon.
-        final StatusBarIconView iconView = new StatusBarIconView(this,
-                notification.pkg + "/0x" + Integer.toHexString(notification.id));
-        final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
-                    notification.notification.iconLevel, notification.notification.number);
-        if (!iconView.set(ic)) {
-            handleNotificationError(key, notification, "Coulding create icon: " + ic);
-            return;
-        }
-        // Add the expanded view.
-        final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
-        parent.addView(row, viewIndex);
-        // Add the icon.
-        final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
-        mNotificationIcons.addView(iconView, iconIndex,
-                new LinearLayout.LayoutParams(mIconWidth, mHeight));
-    }
-
-    StatusBarNotification removeNotificationViews(IBinder key) {
-        NotificationData.Entry entry = mOngoing.remove(key);
-        if (entry == null) {
-            entry = mLatest.remove(key);
-            if (entry == null) {
-                Slog.w(TAG, "removeNotification for unknown key: " + key);
-                return null;
-            }
-        }
-        // Remove the expanded view.
-        ((ViewGroup)entry.row.getParent()).removeView(entry.row);
-        // Remove the icon.
-        ((ViewGroup)entry.icon.getParent()).removeView(entry.icon);
-
-        return entry.notification;
-    }
-
-    private void setAreThereNotifications() {
-        boolean ongoing = mOngoing.hasVisibleItems();
-        boolean latest = mLatest.hasVisibleItems();
-
-        // (no ongoing notifications are clearable)
-        if (mLatest.hasClearableItems()) {
-            mClearButton.setVisibility(View.VISIBLE);
-        } else {
-            mClearButton.setVisibility(View.INVISIBLE);
-        }
-
-        mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
-        mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
-
-        if (ongoing || latest) {
-            mNoNotificationsTitle.setVisibility(View.GONE);
-        } else {
-            mNoNotificationsTitle.setVisibility(View.VISIBLE);
-        }
-    }
-
-
-    /**
-     * State is one or more of the DISABLE constants from StatusBarManager.
-     */
-    public void disable(int state) {
-        final int old = mDisabled;
-        final int diff = state ^ old;
-        mDisabled = state;
-
-        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
-            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
-                Slog.d(TAG, "DISABLE_EXPAND: yes");
-                animateCollapse();
-            }
-        }
-        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
-            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
-                if (mTicking) {
-                    mTicker.halt();
-                } else {
-                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
-                }
-            } else {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
-                if (!mExpandedVisible) {
-                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
-                }
-            }
-        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-            if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
-                mTicker.halt();
-            }
-        }
-    }
-
-    /**
-     * All changes to the status bar and notifications funnel through here and are batched.
-     */
-    private class H extends Handler {
-        public void handleMessage(Message m) {
-            switch (m.what) {
-                case MSG_ANIMATE:
-                    doAnimation();
-                    break;
-                case MSG_ANIMATE_REVEAL:
-                    doRevealAnimation();
-                    break;
-            }
-        }
-    }
-
-    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
-        public void onFocusChange(View v, boolean hasFocus) {
-            // Because 'v' is a ViewGroup, all its children will be (un)selected
-            // too, which allows marqueeing to work.
-            v.setSelected(hasFocus);
-        }
-    };
-
-    private void makeExpandedVisible() {
-        if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
-        if (mExpandedVisible) {
-            return;
-        }
-        mExpandedVisible = true;
-        visibilityChanged(true);
-        
-        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
-        mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-        mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-        mExpandedView.requestFocus(View.FOCUS_FORWARD);
-        mTrackingView.setVisibility(View.VISIBLE);
-        
-        if (!mTicking) {
-            setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
-        }
-    }
-    
-    public void animateExpand() {
-        if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
-        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
-            return ;
-        }
-        if (mExpanded) {
-            return;
-        }
-
-        prepareTracking(0, true);
-        performFling(0, 2000.0f, true);
-    }
-    
-    public void animateCollapse() {
-        if (SPEW) {
-            Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
-                    + " mExpandedVisible=" + mExpandedVisible
-                    + " mExpanded=" + mExpanded
-                    + " mAnimating=" + mAnimating
-                    + " mAnimY=" + mAnimY
-                    + " mAnimVel=" + mAnimVel);
-        }
-        
-        if (!mExpandedVisible) {
-            return;
-        }
-
-        int y;
-        if (mAnimating) {
-            y = (int)mAnimY;
-        } else {
-            y = mDisplay.getHeight()-1;
-        }
-        // Let the fling think that we're open so it goes in the right direction
-        // and doesn't try to re-open the windowshade.
-        mExpanded = true;
-        prepareTracking(y, false);
-        performFling(y, -2000.0f, true);
-    }
-    
-    void performExpand() {
-        if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
-        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
-            return ;
-        }
-        if (mExpanded) {
-            return;
-        }
-
-        mExpanded = true;
-        makeExpandedVisible();
-        updateExpandedViewPos(EXPANDED_FULL_OPEN);
-
-        if (false) postStartTracing();
-    }
-
-    void performCollapse() {
-        if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
-                + " mExpandedVisible=" + mExpandedVisible);
-        
-        if (!mExpandedVisible) {
-            return;
-        }
-        mExpandedVisible = false;
-        visibilityChanged(false);
-        mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-        mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-        mTrackingView.setVisibility(View.GONE);
-
-        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
-            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
-        }
-        setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
-        
-        if (!mExpanded) {
-            return;
-        }
-        mExpanded = false;
-    }
-
-    void doAnimation() {
-        if (mAnimating) {
-            if (SPEW) Slog.d(TAG, "doAnimation");
-            if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
-            incrementAnim();
-            if (SPEW) Slog.d(TAG, "doAnimation after  mAnimY=" + mAnimY);
-            if (mAnimY >= mDisplay.getHeight()-1) {
-                if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
-                mAnimating = false;
-                updateExpandedViewPos(EXPANDED_FULL_OPEN);
-                performExpand();
-            }
-            else if (mAnimY < mStatusBarView.getHeight()) {
-                if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
-                mAnimating = false;
-                updateExpandedViewPos(0);
-                performCollapse();
-            }
-            else {
-                updateExpandedViewPos((int)mAnimY);
-                mCurAnimationTime += ANIM_FRAME_DURATION;
-                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
-            }
-        }
-    }
-
-    void stopTracking() {
-        mTracking = false;
-        mVelocityTracker.recycle();
-        mVelocityTracker = null;
-    }
-
-    void incrementAnim() {
-        long now = SystemClock.uptimeMillis();
-        float t = ((float)(now - mAnimLastTime)) / 1000;            // ms -> s
-        final float y = mAnimY;
-        final float v = mAnimVel;                                   // px/s
-        final float a = mAnimAccel;                                 // px/s/s
-        mAnimY = y + (v*t) + (0.5f*a*t*t);                          // px
-        mAnimVel = v + (a*t);                                       // px/s
-        mAnimLastTime = now;                                        // ms
-        //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
-        //        + " mAnimAccel=" + mAnimAccel);
-    }
-
-    void doRevealAnimation() {
-        final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
-        if (mAnimatingReveal && mAnimating && mAnimY < h) {
-            incrementAnim();
-            if (mAnimY >= h) {
-                mAnimY = h;
-                updateExpandedViewPos((int)mAnimY);
-            } else {
-                updateExpandedViewPos((int)mAnimY);
-                mCurAnimationTime += ANIM_FRAME_DURATION;
-                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
-                        mCurAnimationTime);
-            }
-        }
-    }
-    
-    void prepareTracking(int y, boolean opening) {
-        mTracking = true;
-        mVelocityTracker = VelocityTracker.obtain();
-        if (opening) {
-            mAnimAccel = 2000.0f;
-            mAnimVel = 200;
-            mAnimY = mStatusBarView.getHeight();
-            updateExpandedViewPos((int)mAnimY);
-            mAnimating = true;
-            mAnimatingReveal = true;
-            mHandler.removeMessages(MSG_ANIMATE);
-            mHandler.removeMessages(MSG_ANIMATE_REVEAL);
-            long now = SystemClock.uptimeMillis();
-            mAnimLastTime = now;
-            mCurAnimationTime = now + ANIM_FRAME_DURATION;
-            mAnimating = true;
-            mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
-                    mCurAnimationTime);
-            makeExpandedVisible();
-        } else {
-            // it's open, close it?
-            if (mAnimating) {
-                mAnimating = false;
-                mHandler.removeMessages(MSG_ANIMATE);
-            }
-            updateExpandedViewPos(y + mViewDelta);
-        }
-    }
-    
-    void performFling(int y, float vel, boolean always) {
-        mAnimatingReveal = false;
-        mDisplayHeight = mDisplay.getHeight();
-
-        mAnimY = y;
-        mAnimVel = vel;
-
-        //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
-
-        if (mExpanded) {
-            if (!always && (
-                    vel > 200.0f
-                    || (y > (mDisplayHeight-25) && vel > -200.0f))) {
-                // We are expanded, but they didn't move sufficiently to cause
-                // us to retract.  Animate back to the expanded position.
-                mAnimAccel = 2000.0f;
-                if (vel < 0) {
-                    mAnimVel = 0;
-                }
-            }
-            else {
-                // We are expanded and are now going to animate away.
-                mAnimAccel = -2000.0f;
-                if (vel > 0) {
-                    mAnimVel = 0;
-                }
-            }
-        } else {
-            if (always || (
-                    vel > 200.0f
-                    || (y > (mDisplayHeight/2) && vel > -200.0f))) {
-                // We are collapsed, and they moved enough to allow us to
-                // expand.  Animate in the notifications.
-                mAnimAccel = 2000.0f;
-                if (vel < 0) {
-                    mAnimVel = 0;
-                }
-            }
-            else {
-                // We are collapsed, but they didn't move sufficiently to cause
-                // us to retract.  Animate back to the collapsed position.
-                mAnimAccel = -2000.0f;
-                if (vel > 0) {
-                    mAnimVel = 0;
-                }
-            }
-        }
-        //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
-        //        + " mAnimAccel=" + mAnimAccel);
-
-        long now = SystemClock.uptimeMillis();
-        mAnimLastTime = now;
-        mCurAnimationTime = now + ANIM_FRAME_DURATION;
-        mAnimating = true;
-        mHandler.removeMessages(MSG_ANIMATE);
-        mHandler.removeMessages(MSG_ANIMATE_REVEAL);
-        mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
-        stopTracking();
-    }
-    
-    boolean interceptTouchEvent(MotionEvent event) {
-        if (SPEW) {
-            Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
-                + mDisabled);
-        }
-
-        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
-            return false;
-        }
-        
-        final int statusBarSize = mStatusBarView.getHeight();
-        final int hitSize = statusBarSize*2;
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            final int y = (int)event.getRawY();
-
-            if (!mExpanded) {
-                mViewDelta = statusBarSize - y;
-            } else {
-                mTrackingView.getLocationOnScreen(mAbsPos);
-                mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
-            }
-            if ((!mExpanded && y < hitSize) ||
-                    (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
-
-                // We drop events at the edge of the screen to make the windowshade come
-                // down by accident less, especially when pushing open a device with a keyboard
-                // that rotates (like g1 and droid)
-                int x = (int)event.getRawX();
-                final int edgeBorder = mEdgeBorder;
-                if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
-                    prepareTracking(y, !mExpanded);// opening if we're not already fully visible
-                    mVelocityTracker.addMovement(event);
-                }
-            }
-        } else if (mTracking) {
-            mVelocityTracker.addMovement(event);
-            final int minY = statusBarSize + mCloseView.getHeight();
-            if (event.getAction() == MotionEvent.ACTION_MOVE) {
-                int y = (int)event.getRawY();
-                if (mAnimatingReveal && y < minY) {
-                    // nothing
-                } else  {
-                    mAnimatingReveal = false;
-                    updateExpandedViewPos(y + mViewDelta);
-                }
-            } else if (event.getAction() == MotionEvent.ACTION_UP) {
-                mVelocityTracker.computeCurrentVelocity(1000);
-
-                float yVel = mVelocityTracker.getYVelocity();
-                boolean negative = yVel < 0;
-
-                float xVel = mVelocityTracker.getXVelocity();
-                if (xVel < 0) {
-                    xVel = -xVel;
-                }
-                if (xVel > 150.0f) {
-                    xVel = 150.0f; // limit how much we care about the x axis
-                }
-
-                float vel = (float)Math.hypot(yVel, xVel);
-                if (negative) {
-                    vel = -vel;
-                }
-                
-                performFling((int)event.getRawY(), vel, false);
-            }
-            
-        }
-        return false;
-    }
-
-    private class Launcher implements View.OnClickListener {
-        private PendingIntent mIntent;
-        private String mPkg;
-        private String mTag;
-        private int mId;
-
-        Launcher(PendingIntent intent, String pkg, String tag, int id) {
-            mIntent = intent;
-            mPkg = pkg;
-            mTag = tag;
-            mId = id;
-        }
-
-        public void onClick(View v) {
-            try {
-                // The intent we are sending is for the application, which
-                // won't have permission to immediately start an activity after
-                // the user switches to home.  We know it is safe to do at this
-                // point, so make sure new activity switches are now allowed.
-                ActivityManagerNative.getDefault().resumeAppSwitches();
-            } catch (RemoteException e) {
-            }
-            int[] pos = new int[2];
-            v.getLocationOnScreen(pos);
-            Intent overlay = new Intent();
-            overlay.setSourceBounds(
-                    new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
-            try {
-                mIntent.send(PhoneStatusBarService.this, 0, overlay);
-            } catch (PendingIntent.CanceledException e) {
-                // the stack trace isn't very helpful here.  Just log the exception message.
-                Slog.w(TAG, "Sending contentIntent failed: " + e);
-            }
-            try {
-                mBarService.onNotificationClick(mPkg, mTag, mId);
-            } catch (RemoteException ex) {
-                // system process is dead if we're here.
-            }
-            animateCollapse();
-        }
-    }
-
-    private void tick(StatusBarNotification n) {
-        // Show the ticker if one is requested. Also don't do this
-        // until status bar window is attached to the window manager,
-        // because...  well, what's the point otherwise?  And trying to
-        // run a ticker without being attached will crash!
-        if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
-            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
-                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
-                mTicker.addEntry(n);
-            }
-        }
-    }
-
-    /**
-     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
-     * about the failure.
-     *
-     * WARNING: this will call back into us.  Don't hold any locks.
-     */
-    void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
-        removeNotification(key);
-        try {
-            mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
-        } catch (RemoteException ex) {
-            // The end is nigh.
-        }
-    }
-
-    private class MyTicker extends Ticker {
-        MyTicker(Context context, StatusBarView sb) {
-            super(context, sb);
-        }
-        
-        @Override
-        void tickerStarting() {
-            mTicking = true;
-            mIcons.setVisibility(View.GONE);
-            mTickerView.setVisibility(View.VISIBLE);
-            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
-            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
-            if (mExpandedVisible) {
-                setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
-            }
-        }
-
-        @Override
-        void tickerDone() {
-            mIcons.setVisibility(View.VISIBLE);
-            mTickerView.setVisibility(View.GONE);
-            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
-            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
-                        mTickingDoneListener));
-            if (mExpandedVisible) {
-                setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
-            }
-        }
-
-        void tickerHalting() {
-            mIcons.setVisibility(View.VISIBLE);
-            mTickerView.setVisibility(View.GONE);
-            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
-            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
-                        mTickingDoneListener));
-            if (mExpandedVisible) {
-                setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
-            }
-        }
-    }
-
-    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
-        public void onAnimationEnd(Animation animation) {
-            mTicking = false;
-        }
-        public void onAnimationRepeat(Animation animation) {
-        }
-        public void onAnimationStart(Animation animation) {
-        }
-    };
-
-    private Animation loadAnim(int id, Animation.AnimationListener listener) {
-        Animation anim = AnimationUtils.loadAnimation(PhoneStatusBarService.this, id);
-        if (listener != null) {
-            anim.setAnimationListener(listener);
-        }
-        return anim;
-    }
-
-    public String viewInfo(View v) {
-        return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
-                + " " + v.getWidth() + "x" + v.getHeight() + ")";
-    }
-
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
-                != PackageManager.PERMISSION_GRANTED) {
-            pw.println("Permission Denial: can't dump StatusBar from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
-            return;
-        }
-        
-        synchronized (mQueueLock) {
-            pw.println("Current Status Bar state:");
-            pw.println("  mExpanded=" + mExpanded
-                    + ", mExpandedVisible=" + mExpandedVisible);
-            pw.println("  mTicking=" + mTicking);
-            pw.println("  mTracking=" + mTracking);
-            pw.println("  mAnimating=" + mAnimating
-                    + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
-                    + ", mAnimAccel=" + mAnimAccel);
-            pw.println("  mCurAnimationTime=" + mCurAnimationTime
-                    + " mAnimLastTime=" + mAnimLastTime);
-            pw.println("  mDisplayHeight=" + mDisplayHeight
-                    + " mAnimatingReveal=" + mAnimatingReveal
-                    + " mViewDelta=" + mViewDelta);
-            pw.println("  mDisplayHeight=" + mDisplayHeight);
-            pw.println("  mExpandedParams: " + mExpandedParams);
-            pw.println("  mExpandedView: " + viewInfo(mExpandedView));
-            pw.println("  mExpandedDialog: " + mExpandedDialog);
-            pw.println("  mTrackingParams: " + mTrackingParams);
-            pw.println("  mTrackingView: " + viewInfo(mTrackingView));
-            pw.println("  mOngoingTitle: " + viewInfo(mOngoingTitle));
-            pw.println("  mOngoingItems: " + viewInfo(mOngoingItems));
-            pw.println("  mLatestTitle: " + viewInfo(mLatestTitle));
-            pw.println("  mLatestItems: " + viewInfo(mLatestItems));
-            pw.println("  mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
-            pw.println("  mCloseView: " + viewInfo(mCloseView));
-            pw.println("  mTickerView: " + viewInfo(mTickerView));
-            pw.println("  mScrollView: " + viewInfo(mScrollView)
-                    + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
-            pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
-        }
-        /*
-        synchronized (mNotificationData) {
-            int N = mNotificationData.ongoingCount();
-            pw.println("  ongoingCount.size=" + N);
-            for (int i=0; i<N; i++) {
-                StatusBarNotification n = mNotificationData.getOngoing(i);
-                pw.println("    [" + i + "] key=" + n.key + " view=" + n.view);
-                pw.println("           data=" + n.data);
-            }
-            N = mNotificationData.latestCount();
-            pw.println("  ongoingCount.size=" + N);
-            for (int i=0; i<N; i++) {
-                StatusBarNotification n = mNotificationData.getLatest(i);
-                pw.println("    [" + i + "] key=" + n.key + " view=" + n.view);
-                pw.println("           data=" + n.data);
-            }
-        }
-        */
-        
-        if (false) {
-            pw.println("see the logcat for a dump of the views we have created.");
-            // must happen on ui thread
-            mHandler.post(new Runnable() {
-                    public void run() {
-                        mStatusBarView.getLocationOnScreen(mAbsPos);
-                        Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
-                                + ") " + mStatusBarView.getWidth() + "x"
-                                + mStatusBarView.getHeight());
-                        mStatusBarView.debug();
-
-                        mExpandedView.getLocationOnScreen(mAbsPos);
-                        Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
-                                + ") " + mExpandedView.getWidth() + "x"
-                                + mExpandedView.getHeight());
-                        mExpandedView.debug();
-
-                        mTrackingView.getLocationOnScreen(mAbsPos);
-                        Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
-                                + ") " + mTrackingView.getWidth() + "x"
-                                + mTrackingView.getHeight());
-                        mTrackingView.debug();
-                    }
-                });
-        }
-    }
-
-    void onBarViewAttached() {
-        WindowManager.LayoutParams lp;
-        int pixelFormat;
-        Drawable bg;
-
-        /// ---------- Tracking View --------------
-        pixelFormat = PixelFormat.RGBX_8888;
-        bg = mTrackingView.getBackground();
-        if (bg != null) {
-            pixelFormat = bg.getOpacity();
-        }
-
-        lp = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
-                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
-                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
-                pixelFormat);
-//        lp.token = mStatusBarView.getWindowToken();
-        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
-        lp.setTitle("TrackingView");
-        lp.y = mTrackingPosition;
-        mTrackingParams = lp;
-
-        WindowManagerImpl.getDefault().addView(mTrackingView, lp);
-    }
-
-    void onTrackingViewAttached() {
-        WindowManager.LayoutParams lp;
-        int pixelFormat;
-        Drawable bg;
-
-        /// ---------- Expanded View --------------
-        pixelFormat = PixelFormat.TRANSLUCENT;
-
-        final int disph = mDisplay.getHeight();
-        lp = mExpandedDialog.getWindow().getAttributes();
-        lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
-        lp.height = getExpandedHeight();
-        lp.x = 0;
-        mTrackingPosition = lp.y = -disph; // sufficiently large negative
-        lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
-        lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
-                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                | WindowManager.LayoutParams.FLAG_DITHER
-                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        lp.format = pixelFormat;
-        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
-        lp.setTitle("StatusBarExpanded");
-        mExpandedDialog.getWindow().setAttributes(lp);
-        mExpandedDialog.getWindow().setFormat(pixelFormat);
-        mExpandedParams = lp;
-
-        mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
-        mExpandedDialog.setContentView(mExpandedView,
-                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                                           ViewGroup.LayoutParams.MATCH_PARENT));
-        mExpandedDialog.getWindow().setBackgroundDrawable(null);
-        mExpandedDialog.show();
-        FrameLayout hack = (FrameLayout)mExpandedView.getParent();
-    }
-
-    void setDateViewVisibility(boolean visible, int anim) {
-        mDateView.setUpdates(visible);
-        mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-        mDateView.startAnimation(loadAnim(anim, null));
-    }
-
-    void setNotificationIconVisibility(boolean visible, int anim) {
-        int old = mNotificationIcons.getVisibility();
-        int v = visible ? View.VISIBLE : View.INVISIBLE;
-        if (old != v) {
-            mNotificationIcons.setVisibility(v);
-            mNotificationIcons.startAnimation(loadAnim(anim, null));
-        }
-    }
-
-    void updateExpandedViewPos(int expandedPosition) {
-        if (SPEW) {
-            Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
-                    + " mTrackingParams.y=" + mTrackingParams.y
-                    + " mTrackingPosition=" + mTrackingPosition);
-        }
-
-        int h = mStatusBarView.getHeight();
-        int disph = mDisplay.getHeight();
-
-        // If the expanded view is not visible, make sure they're still off screen.
-        // Maybe the view was resized.
-        if (!mExpandedVisible) {
-            if (mTrackingView != null) {
-                mTrackingPosition = -disph;
-                if (mTrackingParams != null) {
-                    mTrackingParams.y = mTrackingPosition;
-                    WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
-                }
-            }
-            if (mExpandedParams != null) {
-                mExpandedParams.y = -disph;
-                mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-            }
-            return;
-        }
-
-        // tracking view...
-        int pos;
-        if (expandedPosition == EXPANDED_FULL_OPEN) {
-            pos = h;
-        }
-        else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
-            pos = mTrackingPosition;
-        }
-        else {
-            if (expandedPosition <= disph) {
-                pos = expandedPosition;
-            } else {
-                pos = disph;
-            }
-            pos -= disph-h;
-        }
-        mTrackingPosition = mTrackingParams.y = pos;
-        mTrackingParams.height = disph-h;
-        WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
-
-        if (mExpandedParams != null) {
-            mCloseView.getLocationInWindow(mPositionTmp);
-            final int closePos = mPositionTmp[1];
-
-            mExpandedContents.getLocationInWindow(mPositionTmp);
-            final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
-
-            mExpandedParams.y = pos + mTrackingView.getHeight()
-                    - (mTrackingParams.height-closePos) - contentsBottom;
-            int max = h;
-            if (mExpandedParams.y > max) {
-                mExpandedParams.y = max;
-            }
-            int min = mTrackingPosition;
-            if (mExpandedParams.y < min) {
-                mExpandedParams.y = min;
-            }
-
-            boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
-            if (!visible) {
-                // if the contents aren't visible, move the expanded view way off screen
-                // because the window itself extends below the content view.
-                mExpandedParams.y = -disph;
-            }
-            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-
-            // As long as this isn't just a repositioning that's not supposed to affect
-            // the user's perception of what's showing, call to say that the visibility
-            // has changed. (Otherwise, someone else will call to do that).
-            if (expandedPosition != EXPANDED_LEAVE_ALONE) {
-                Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
-                visibilityChanged(visible);
-            }
-        }
-
-        if (SPEW) {
-            Slog.d(TAG, "updateExpandedViewPos after  expandedPosition=" + expandedPosition
-                    + " mTrackingParams.y=" + mTrackingParams.y
-                    + " mTrackingPosition=" + mTrackingPosition
-                    + " mExpandedParams.y=" + mExpandedParams.y
-                    + " mExpandedParams.height=" + mExpandedParams.height);
-        }
-    }
-
-    int getExpandedHeight() {
-        return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
-    }
-
-    void updateExpandedHeight() {
-        if (mExpandedView != null) {
-            mExpandedParams.height = getExpandedHeight();
-            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-        }
-    }
-
-    /**
-     * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
-     * This was added last-minute and is inconsistent with the way the rest of the notifications
-     * are handled, because the notification isn't really cancelled.  The lights are just
-     * turned off.  If any other notifications happen, the lights will turn back on.  Steve says
-     * this is what he wants. (see bug 1131461)
-     */
-    void visibilityChanged(boolean visible) {
-        if (mPanelSlightlyVisible != visible) {
-            mPanelSlightlyVisible = visible;
-            try {
-                mBarService.onPanelRevealed();
-            } catch (RemoteException ex) {
-                // Won't fail unless the world has ended.
-            }
-        }
-    }
-
-    void performDisableActions(int net) {
-        int old = mDisabled;
-        int diff = net ^ old;
-        mDisabled = net;
-
-        // act accordingly
-        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
-            if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
-                Slog.d(TAG, "DISABLE_EXPAND: yes");
-                animateCollapse();
-            }
-        }
-        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
-            if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
-                if (mTicking) {
-                    mNotificationIcons.setVisibility(View.INVISIBLE);
-                    mTicker.halt();
-                } else {
-                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
-                }
-            } else {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
-                if (!mExpandedVisible) {
-                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
-                }
-            }
-        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-                mTicker.halt();
-            }
-        }
-    }
-
-    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
-        public void onClick(View v) {
-            try {
-                mBarService.onClearAllNotifications();
-            } catch (RemoteException ex) {
-                // system process is dead if we're here.
-            }
-            animateCollapse();
-        }
-    };
-
-    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
-                    || Intent.ACTION_SCREEN_OFF.equals(action)) {
-                //collapse();
-            }
-            else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
-                updateResources();
-            }
-        }
-    };
-
-    /**
-     * Reload some of our resources when the configuration changes.
-     * 
-     * We don't reload everything when the configuration changes -- we probably
-     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
-     * meantime, just update the things that we know change.
-     */
-    void updateResources() {
-        Resources res = getResources();
-
-        mClearButton.setText(getText(R.string.status_bar_clear_all_button));
-        mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title));
-        mLatestTitle.setText(getText(R.string.status_bar_latest_events_title));
-        mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title));
-
-        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
-        if (false) Slog.v(TAG, "updateResources");
-    }
-
-    //
-    // tracing
-    //
-
-    void postStartTracing() {
-        mHandler.postDelayed(mStartTracing, 3000);
-    }
-
-    void vibrate() {
-        android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
-        vib.vibrate(250);
-    }
-
-    Runnable mStartTracing = new Runnable() {
-        public void run() {
-            vibrate();
-            SystemClock.sleep(250);
-            Slog.d(TAG, "startTracing");
-            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
-            mHandler.postDelayed(mStopTracing, 10000);
-        }
-    };
-
-    Runnable mStopTracing = new Runnable() {
-        public void run() {
-            android.os.Debug.stopMethodTracing();
-            Slog.d(TAG, "stopTracing");
-            vibrate();
-        }
-    };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index bc1e798..d98bd7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -65,7 +65,7 @@
         if (!iconEquals) {
             Drawable drawable = getIcon(icon);
             if (drawable == null) {
-                Slog.w(PhoneStatusBarService.TAG, "No icon for slot " + mSlot);
+                Slog.w(StatusBarService.TAG, "No icon for slot " + mSlot);
                 return false;
             }
             setImageDrawable(drawable);
@@ -99,7 +99,7 @@
             try {
                 r = context.getPackageManager().getResourcesForApplication(icon.iconPackage);
             } catch (PackageManager.NameNotFoundException ex) {
-                Slog.e(PhoneStatusBarService.TAG, "Icon package not found: " + icon.iconPackage);
+                Slog.e(StatusBarService.TAG, "Icon package not found: " + icon.iconPackage);
                 return null;
             }
         } else {
@@ -113,7 +113,7 @@
         try {
             return r.getDrawable(icon.iconId);
         } catch (RuntimeException e) {
-            Slog.w(PhoneStatusBarService.TAG, "Icon not found in "
+            Slog.w(StatusBarService.TAG, "Icon not found in "
                   + (icon.iconPackage != null ? icon.iconId : "<system>")
                   + ": " + Integer.toHexString(icon.iconId));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
index d200886..07bcce7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
@@ -17,39 +17,189 @@
 package com.android.systemui.statusbar;
 
 import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.PixelFormat;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.Slog;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarIconList;
 import com.android.internal.statusbar.StatusBarNotification;
 
-import java.util.Arrays;
-import java.util.ArrayList;
+import android.app.ActivityManagerNative;
+import android.app.Dialog;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.Log;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.FrameLayout;
 
-public abstract class StatusBarService extends Service implements CommandQueue.Callbacks {
-    private static final String TAG = "StatusBarService";
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Set;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.StatusBarPolicy;
+
+
+
+public class StatusBarService extends Service implements CommandQueue.Callbacks {
+    static final String TAG = "StatusBarService";
+    static final boolean SPEW = false;
+
+    public static final String ACTION_STATUSBAR_START
+            = "com.android.internal.policy.statusbar.START";
+
+    static final int EXPANDED_LEAVE_ALONE = -10000;
+    static final int EXPANDED_FULL_OPEN = -10001;
+
+    private static final int MSG_ANIMATE = 1000;
+    private static final int MSG_ANIMATE_REVEAL = 1001;
+    private static final int MSG_SHOW_INTRUDER = 1002;
+    private static final int MSG_HIDE_INTRUDER = 1003;
+
+    // will likely move to a resource or other tunable param at some point
+    private static final int INTRUDER_ALERT_DECAY_MS = 10000;
+
+    StatusBarPolicy mIconPolicy;
 
     CommandQueue mCommandQueue;
     IStatusBarService mBarService;
 
+    int mIconSize;
+    Display mDisplay;
+    StatusBarView mStatusBarView;
+    int mPixelFormat;
+    H mHandler = new H();
+    Object mQueueLock = new Object();
+
+    // icons
+    LinearLayout mIcons;
+    IconMerger mNotificationIcons;
+    LinearLayout mStatusIcons;
+
+    // expanded notifications
+    Dialog mExpandedDialog;
+    ExpandedView mExpandedView;
+    WindowManager.LayoutParams mExpandedParams;
+    ScrollView mScrollView;
+    View mNotificationLinearLayout;
+    View mExpandedContents;
+    // top bar
+    TextView mNoNotificationsTitle;
+    TextView mClearButton;
+    // drag bar
+    CloseDragHandle mCloseView;
+    // ongoing
+    NotificationData mOngoing = new NotificationData();
+    TextView mOngoingTitle;
+    LinearLayout mOngoingItems;
+    // latest
+    NotificationData mLatest = new NotificationData();
+    TextView mLatestTitle;
+    LinearLayout mLatestItems;
+    // position
+    int[] mPositionTmp = new int[2];
+    boolean mExpanded;
+    boolean mExpandedVisible;
+
+    // the date view
+    DateView mDateView;
+
+    // the tracker view
+    TrackingView mTrackingView;
+    WindowManager.LayoutParams mTrackingParams;
+    int mTrackingPosition; // the position of the top of the tracking view.
+    private boolean mPanelSlightlyVisible;
+
+    // ticker
+    private Ticker mTicker;
+    private View mTickerView;
+    private boolean mTicking;
+
+    // Tracking finger for opening/closing.
+    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
+    boolean mTracking;
+    VelocityTracker mVelocityTracker;
+
+    static final int ANIM_FRAME_DURATION = (1000/60);
+
+    boolean mAnimating;
+    long mCurAnimationTime;
+    float mDisplayHeight;
+    float mAnimY;
+    float mAnimVel;
+    float mAnimAccel;
+    long mAnimLastTime;
+    boolean mAnimatingReveal = false;
+    int mViewDelta;
+    int[] mAbsPos = new int[2];
+
+    // for disabling the status bar
+    int mDisabled = 0;
+
+    private class ExpandedDialog extends Dialog {
+        ExpandedDialog(Context context) {
+            super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
+        }
+
+        @Override
+        public boolean dispatchKeyEvent(KeyEvent event) {
+            boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+            switch (event.getKeyCode()) {
+            case KeyEvent.KEYCODE_BACK:
+                if (!down) {
+                    animateCollapse();
+                }
+                return true;
+            }
+            return super.dispatchKeyEvent(event);
+        }
+    }
+
+
     @Override
     public void onCreate() {
+        // First set up our views and stuff.
+        mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+        makeStatusBarView(this);
+
         // Connect in to the status bar manager service
         StatusBarIconList iconList = new StatusBarIconList();
         ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
@@ -87,6 +237,9 @@
 
         // Put up the view
         addStatusBarView();
+
+        // Lastly, call to the icon policy to install/update all the icons.
+        mIconPolicy = new StatusBarPolicy(this);
     }
 
     @Override
@@ -94,6 +247,9 @@
         // we're never destroyed
     }
 
+    // for immersive activities
+    private View mIntruderAlertView;
+
     /**
      * Nobody binds to us.
      */
@@ -102,9 +258,1349 @@
         return null;
     }
 
+    // ================================================================================
+    // Constructing the view
+    // ================================================================================
+    private void makeStatusBarView(Context context) {
+        Resources res = context.getResources();
+
+        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+
+        ExpandedView expanded = (ExpandedView)View.inflate(context,
+                R.layout.status_bar_expanded, null);
+        expanded.mService = this;
+
+        mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null);
+        mIntruderAlertView.setVisibility(View.GONE);
+        mIntruderAlertView.setClickable(true);
+
+        StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
+        sb.mService = this;
+
+        // figure out which pixel-format to use for the status bar.
+        mPixelFormat = PixelFormat.TRANSLUCENT;
+        Drawable bg = sb.getBackground();
+        if (bg != null) {
+            mPixelFormat = bg.getOpacity();
+        }
+
+        mStatusBarView = sb;
+        mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
+        mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
+        mIcons = (LinearLayout)sb.findViewById(R.id.icons);
+        mTickerView = sb.findViewById(R.id.ticker);
+        mDateView = (DateView)sb.findViewById(R.id.date);
+
+        mExpandedDialog = new ExpandedDialog(context);
+        mExpandedView = expanded;
+        mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
+        mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
+        mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
+        mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
+        mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
+        mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
+        mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
+        mClearButton.setOnClickListener(mClearButtonListener);
+        mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
+        mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
+
+        mOngoingTitle.setVisibility(View.GONE);
+        mLatestTitle.setVisibility(View.GONE);
+
+        mTicker = new MyTicker(context, sb);
+
+        TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
+        tickerView.mTicker = mTicker;
+
+        mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null);
+        mTrackingView.mService = this;
+        mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
+        mCloseView.mService = this;
+
+        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
+        // the more notifications icon
+        StatusBarIconView moreView = new StatusBarIconView(this, "more");
+        moreView.set(new StatusBarIcon(null, R.drawable.stat_notify_more, 0));
+        mNotificationIcons.addMoreView(moreView,
+                new LinearLayout.LayoutParams(mIconSize, mIconSize));
+
+        // set the inital view visibility
+        setAreThereNotifications();
+        mDateView.setVisibility(View.INVISIBLE);
+
+        // receive broadcasts
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        context.registerReceiver(mBroadcastReceiver, filter);
+    }
+
+    protected void addStatusBarView() {
+        Resources res = getResources();
+        final int height= res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+
+        final StatusBarView view = mStatusBarView;
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                height,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
+                PixelFormat.RGBX_8888);
+        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        lp.setTitle("StatusBar");
+        // TODO lp.windowAnimations = R.style.Animation_StatusBar;
+
+        WindowManagerImpl.getDefault().addView(view, lp);
+
+        lp = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                PixelFormat.TRANSLUCENT);
+        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        lp.y += height * 1.5; // FIXME
+        lp.setTitle("IntruderAlert");
+        lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar_IntruderAlert;
+
+        WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp);
+    }
+
+    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
+        if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+                + " icon=" + icon);
+        StatusBarIconView view = new StatusBarIconView(this, slot);
+        view.set(icon);
+        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
+    }
+
+    public void updateIcon(String slot, int index, int viewIndex,
+            StatusBarIcon old, StatusBarIcon icon) {
+        if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+                + " old=" + old + " icon=" + icon);
+        StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
+        view.set(icon);
+    }
+
+    public void removeIcon(String slot, int index, int viewIndex) {
+        if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
+        mStatusIcons.removeViewAt(viewIndex);
+    }
+
+    public void addNotification(IBinder key, StatusBarNotification notification) {
+        StatusBarIconView iconView = addNotificationViews(key, notification);
+        if (iconView == null) return;
+
+        boolean immersive = false;
+        try {
+            immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
+            Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
+        } catch (RemoteException ex) {
+        }
+        if (immersive) {
+            if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
+                Slog.d(TAG, "Presenting high-priority notification in immersive activity");
+                // @@@ special new transient ticker mode
+                // 1. Populate mIntruderAlertView
+
+                ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon);
+                TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText);
+                alertIcon.setImageDrawable(StatusBarIconView.getIcon(
+                    alertIcon.getContext(),
+                    iconView.getStatusBarIcon()));
+                alertText.setText(notification.notification.tickerText);
+
+                View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content);
+                button.setOnClickListener(
+                    new Launcher(notification.notification.contentIntent,
+                        notification.pkg, notification.tag, notification.id));
+
+                // 2. Animate mIntruderAlertView in
+                mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
+
+                // 3. Set alarm to age the notification off (TODO)
+                mHandler.removeMessages(MSG_HIDE_INTRUDER);
+                mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
+            }
+        } else if (notification.notification.fullScreenIntent != null) {
+            // not immersive & a full-screen alert should be shown
+            Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;"
+                    + " sending fullScreenIntent");
+            try {
+                notification.notification.fullScreenIntent.send();
+            } catch (PendingIntent.CanceledException e) {
+            }
+        } else {
+            // usual case: status bar visible & not immersive
+
+            // show the ticker
+            tick(notification);
+        }
+
+        // Recalculate the position of the sliding windows and the titles.
+        setAreThereNotifications();
+        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+    }
+
+    public void updateNotification(IBinder key, StatusBarNotification notification) {
+        Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification);
+
+        NotificationData oldList;
+        int oldIndex = mOngoing.findEntry(key);
+        if (oldIndex >= 0) {
+            oldList = mOngoing;
+        } else {
+            oldIndex = mLatest.findEntry(key);
+            if (oldIndex < 0) {
+                Slog.w(TAG, "updateNotification for unknown key: " + key);
+                return;
+            }
+            oldList = mLatest;
+        }
+        final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex);
+        final StatusBarNotification oldNotification = oldEntry.notification;
+        final RemoteViews oldContentView = oldNotification.notification.contentView;
+
+        final RemoteViews contentView = notification.notification.contentView;
+
+        if (false) {
+            Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
+                    + " ongoing=" + oldNotification.isOngoing()
+                    + " expanded=" + oldEntry.expanded
+                    + " contentView=" + oldContentView);
+            Slog.d(TAG, "new notification: when=" + notification.notification.when
+                    + " ongoing=" + oldNotification.isOngoing()
+                    + " contentView=" + contentView);
+        }
+
+        // Can we just reapply the RemoteViews in place?  If when didn't change, the order
+        // didn't change.
+        if (notification.notification.when == oldNotification.notification.when
+                && notification.isOngoing() == oldNotification.isOngoing()
+                && oldEntry.expanded != null
+                && contentView != null && oldContentView != null
+                && contentView.getPackage() != null
+                && oldContentView.getPackage() != null
+                && oldContentView.getPackage().equals(contentView.getPackage())
+                && oldContentView.getLayoutId() == contentView.getLayoutId()) {
+            if (SPEW) Slog.d(TAG, "reusing notification");
+            oldEntry.notification = notification;
+            try {
+                // Reapply the RemoteViews
+                contentView.reapply(this, oldEntry.content);
+                // update the contentIntent
+                final PendingIntent contentIntent = notification.notification.contentIntent;
+                if (contentIntent != null) {
+                    oldEntry.content.setOnClickListener(new Launcher(contentIntent,
+                                notification.pkg, notification.tag, notification.id));
+                }
+                // Update the icon.
+                final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
+                        notification.notification.icon, notification.notification.iconLevel,
+                        notification.notification.number);
+                if (!oldEntry.icon.set(ic)) {
+                    handleNotificationError(key, notification, "Couldn't update icon: " + ic);
+                    return;
+                }
+            }
+            catch (RuntimeException e) {
+                // It failed to add cleanly.  Log, and remove the view from the panel.
+                Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
+                removeNotificationViews(key);
+                addNotificationViews(key, notification);
+            }
+        } else {
+            if (SPEW) Slog.d(TAG, "not reusing notification");
+            removeNotificationViews(key);
+            addNotificationViews(key, notification);
+        }
+
+        // Restart the ticker if it's still running
+        tick(notification);
+
+        // Recalculate the position of the sliding windows and the titles.
+        setAreThereNotifications();
+        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+    }
+
+    public void removeNotification(IBinder key) {
+        if (SPEW) Slog.d(TAG, "removeNotification key=" + key);
+        StatusBarNotification old = removeNotificationViews(key);
+
+        if (old != null) {
+            // Cancel the ticker if it's still running
+            mTicker.removeEntry(old);
+
+            // Recalculate the position of the sliding windows and the titles.
+            setAreThereNotifications();
+            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+        }
+    }
+
+    private int chooseIconIndex(boolean isOngoing, int viewIndex) {
+        final int latestSize = mLatest.size();
+        if (isOngoing) {
+            return latestSize + (mOngoing.size() - viewIndex);
+        } else {
+            return latestSize - viewIndex;
+        }
+    }
+
+    View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
+        Notification n = notification.notification;
+        RemoteViews remoteViews = n.contentView;
+        if (remoteViews == null) {
+            return null;
+        }
+
+        // create the row view
+        LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
+
+        // bind the click event to the content area
+        ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
+        content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+        content.setOnFocusChangeListener(mFocusChangeListener);
+        PendingIntent contentIntent = n.contentIntent;
+        if (contentIntent != null) {
+            content.setOnClickListener(new Launcher(contentIntent, notification.pkg,
+                        notification.tag, notification.id));
+        }
+
+        View expanded = null;
+        Exception exception = null;
+        try {
+            expanded = remoteViews.apply(this, content);
+        }
+        catch (RuntimeException e) {
+            exception = e;
+        }
+        if (expanded == null) {
+            String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id);
+            Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
+            return null;
+        } else {
+            content.addView(expanded);
+            row.setDrawingCacheEnabled(true);
+        }
+
+        return new View[] { row, content, expanded };
+    }
+
+    StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
+        NotificationData list;
+        ViewGroup parent;
+        final boolean isOngoing = notification.isOngoing();
+        if (isOngoing) {
+            list = mOngoing;
+            parent = mOngoingItems;
+        } else {
+            list = mLatest;
+            parent = mLatestItems;
+        }
+        // Construct the expanded view.
+        final View[] views = makeNotificationView(notification, parent);
+        if (views == null) {
+            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+                    + notification);
+            return null;
+        }
+        final View row = views[0];
+        final View content = views[1];
+        final View expanded = views[2];
+        // Construct the icon.
+        final StatusBarIconView iconView = new StatusBarIconView(this,
+                notification.pkg + "/0x" + Integer.toHexString(notification.id));
+        final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
+                    notification.notification.iconLevel, notification.notification.number);
+        if (!iconView.set(ic)) {
+            handleNotificationError(key, notification, "Coulding create icon: " + ic);
+            return null;
+        }
+        // Add the expanded view.
+        final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
+        parent.addView(row, viewIndex);
+        // Add the icon.
+        final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
+        mNotificationIcons.addView(iconView, iconIndex,
+                new LinearLayout.LayoutParams(mIconSize, mIconSize));
+        return iconView;
+    }
+
+    StatusBarNotification removeNotificationViews(IBinder key) {
+        NotificationData.Entry entry = mOngoing.remove(key);
+        if (entry == null) {
+            entry = mLatest.remove(key);
+            if (entry == null) {
+                Slog.w(TAG, "removeNotification for unknown key: " + key);
+                return null;
+            }
+        }
+        // Remove the expanded view.
+        ((ViewGroup)entry.row.getParent()).removeView(entry.row);
+        // Remove the icon.
+        ((ViewGroup)entry.icon.getParent()).removeView(entry.icon);
+
+        return entry.notification;
+    }
+
+    private void setAreThereNotifications() {
+        boolean ongoing = mOngoing.hasVisibleItems();
+        boolean latest = mLatest.hasVisibleItems();
+
+        // (no ongoing notifications are clearable)
+        if (mLatest.hasClearableItems()) {
+            mClearButton.setVisibility(View.VISIBLE);
+        } else {
+            mClearButton.setVisibility(View.INVISIBLE);
+        }
+
+        mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
+        mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
+
+        if (ongoing || latest) {
+            mNoNotificationsTitle.setVisibility(View.GONE);
+        } else {
+            mNoNotificationsTitle.setVisibility(View.VISIBLE);
+        }
+    }
+
+
     /**
-     * Implement this to add the main status bar view.
+     * State is one or more of the DISABLE constants from StatusBarManager.
      */
-    protected abstract void addStatusBarView();
+    public void disable(int state) {
+        final int old = mDisabled;
+        final int diff = state ^ old;
+        mDisabled = state;
+
+        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
+                Slog.d(TAG, "DISABLE_EXPAND: yes");
+                animateCollapse();
+            }
+        }
+        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+                if (mTicking) {
+                    mTicker.halt();
+                } else {
+                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
+                }
+            } else {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+                if (!mExpandedVisible) {
+                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+                }
+            }
+        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+            if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
+                mTicker.halt();
+            }
+        }
+    }
+
+    /**
+     * All changes to the status bar and notifications funnel through here and are batched.
+     */
+    private class H extends Handler {
+        public void handleMessage(Message m) {
+            switch (m.what) {
+                case MSG_ANIMATE:
+                    doAnimation();
+                    break;
+                case MSG_ANIMATE_REVEAL:
+                    doRevealAnimation();
+                    break;
+                case MSG_SHOW_INTRUDER:
+                    setIntruderAlertVisibility(true);
+                    break;
+                case MSG_HIDE_INTRUDER:
+                    setIntruderAlertVisibility(false);
+                    break;
+            }
+        }
+    }
+
+    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
+        public void onFocusChange(View v, boolean hasFocus) {
+            // Because 'v' is a ViewGroup, all its children will be (un)selected
+            // too, which allows marqueeing to work.
+            v.setSelected(hasFocus);
+        }
+    };
+
+    private void makeExpandedVisible() {
+        if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
+        if (mExpandedVisible) {
+            return;
+        }
+        mExpandedVisible = true;
+        visibilityChanged(true);
+
+        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+        mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+        mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+        mExpandedView.requestFocus(View.FOCUS_FORWARD);
+        mTrackingView.setVisibility(View.VISIBLE);
+
+        if (!mTicking) {
+            setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+        }
+    }
+
+    public void animateExpand() {
+        if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+            return ;
+        }
+        if (mExpanded) {
+            return;
+        }
+
+        prepareTracking(0, true);
+        performFling(0, 2000.0f, true);
+    }
+
+    public void animateCollapse() {
+        if (SPEW) {
+            Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
+                    + " mExpandedVisible=" + mExpandedVisible
+                    + " mExpanded=" + mExpanded
+                    + " mAnimating=" + mAnimating
+                    + " mAnimY=" + mAnimY
+                    + " mAnimVel=" + mAnimVel);
+        }
+
+        if (!mExpandedVisible) {
+            return;
+        }
+
+        int y;
+        if (mAnimating) {
+            y = (int)mAnimY;
+        } else {
+            y = mDisplay.getHeight()-1;
+        }
+        // Let the fling think that we're open so it goes in the right direction
+        // and doesn't try to re-open the windowshade.
+        mExpanded = true;
+        prepareTracking(y, false);
+        performFling(y, -2000.0f, true);
+    }
+
+    void performExpand() {
+        if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+            return ;
+        }
+        if (mExpanded) {
+            return;
+        }
+
+        mExpanded = true;
+        makeExpandedVisible();
+        updateExpandedViewPos(EXPANDED_FULL_OPEN);
+
+        if (false) postStartTracing();
+    }
+
+    void performCollapse() {
+        if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
+                + " mExpandedVisible=" + mExpandedVisible);
+
+        if (!mExpandedVisible) {
+            return;
+        }
+        mExpandedVisible = false;
+        visibilityChanged(false);
+        mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+        mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+        mTrackingView.setVisibility(View.GONE);
+
+        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
+            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+        }
+        setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
+
+        if (!mExpanded) {
+            return;
+        }
+        mExpanded = false;
+    }
+
+    void doAnimation() {
+        if (mAnimating) {
+            if (SPEW) Slog.d(TAG, "doAnimation");
+            if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
+            incrementAnim();
+            if (SPEW) Slog.d(TAG, "doAnimation after  mAnimY=" + mAnimY);
+            if (mAnimY >= mDisplay.getHeight()-1) {
+                if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
+                mAnimating = false;
+                updateExpandedViewPos(EXPANDED_FULL_OPEN);
+                performExpand();
+            }
+            else if (mAnimY < mStatusBarView.getHeight()) {
+                if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
+                mAnimating = false;
+                updateExpandedViewPos(0);
+                performCollapse();
+            }
+            else {
+                updateExpandedViewPos((int)mAnimY);
+                mCurAnimationTime += ANIM_FRAME_DURATION;
+                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+            }
+        }
+    }
+
+    void stopTracking() {
+        mTracking = false;
+        mVelocityTracker.recycle();
+        mVelocityTracker = null;
+    }
+
+    void incrementAnim() {
+        long now = SystemClock.uptimeMillis();
+        float t = ((float)(now - mAnimLastTime)) / 1000;            // ms -> s
+        final float y = mAnimY;
+        final float v = mAnimVel;                                   // px/s
+        final float a = mAnimAccel;                                 // px/s/s
+        mAnimY = y + (v*t) + (0.5f*a*t*t);                          // px
+        mAnimVel = v + (a*t);                                       // px/s
+        mAnimLastTime = now;                                        // ms
+        //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
+        //        + " mAnimAccel=" + mAnimAccel);
+    }
+
+    void doRevealAnimation() {
+        final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
+        if (mAnimatingReveal && mAnimating && mAnimY < h) {
+            incrementAnim();
+            if (mAnimY >= h) {
+                mAnimY = h;
+                updateExpandedViewPos((int)mAnimY);
+            } else {
+                updateExpandedViewPos((int)mAnimY);
+                mCurAnimationTime += ANIM_FRAME_DURATION;
+                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+                        mCurAnimationTime);
+            }
+        }
+    }
+
+    void prepareTracking(int y, boolean opening) {
+        mTracking = true;
+        mVelocityTracker = VelocityTracker.obtain();
+        if (opening) {
+            mAnimAccel = 2000.0f;
+            mAnimVel = 200;
+            mAnimY = mStatusBarView.getHeight();
+            updateExpandedViewPos((int)mAnimY);
+            mAnimating = true;
+            mAnimatingReveal = true;
+            mHandler.removeMessages(MSG_ANIMATE);
+            mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+            long now = SystemClock.uptimeMillis();
+            mAnimLastTime = now;
+            mCurAnimationTime = now + ANIM_FRAME_DURATION;
+            mAnimating = true;
+            mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+                    mCurAnimationTime);
+            makeExpandedVisible();
+        } else {
+            // it's open, close it?
+            if (mAnimating) {
+                mAnimating = false;
+                mHandler.removeMessages(MSG_ANIMATE);
+            }
+            updateExpandedViewPos(y + mViewDelta);
+        }
+    }
+
+    void performFling(int y, float vel, boolean always) {
+        mAnimatingReveal = false;
+        mDisplayHeight = mDisplay.getHeight();
+
+        mAnimY = y;
+        mAnimVel = vel;
+
+        //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
+
+        if (mExpanded) {
+            if (!always && (
+                    vel > 200.0f
+                    || (y > (mDisplayHeight-25) && vel > -200.0f))) {
+                // We are expanded, but they didn't move sufficiently to cause
+                // us to retract.  Animate back to the expanded position.
+                mAnimAccel = 2000.0f;
+                if (vel < 0) {
+                    mAnimVel = 0;
+                }
+            }
+            else {
+                // We are expanded and are now going to animate away.
+                mAnimAccel = -2000.0f;
+                if (vel > 0) {
+                    mAnimVel = 0;
+                }
+            }
+        } else {
+            if (always || (
+                    vel > 200.0f
+                    || (y > (mDisplayHeight/2) && vel > -200.0f))) {
+                // We are collapsed, and they moved enough to allow us to
+                // expand.  Animate in the notifications.
+                mAnimAccel = 2000.0f;
+                if (vel < 0) {
+                    mAnimVel = 0;
+                }
+            }
+            else {
+                // We are collapsed, but they didn't move sufficiently to cause
+                // us to retract.  Animate back to the collapsed position.
+                mAnimAccel = -2000.0f;
+                if (vel > 0) {
+                    mAnimVel = 0;
+                }
+            }
+        }
+        //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
+        //        + " mAnimAccel=" + mAnimAccel);
+
+        long now = SystemClock.uptimeMillis();
+        mAnimLastTime = now;
+        mCurAnimationTime = now + ANIM_FRAME_DURATION;
+        mAnimating = true;
+        mHandler.removeMessages(MSG_ANIMATE);
+        mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+        mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+        stopTracking();
+    }
+
+    boolean interceptTouchEvent(MotionEvent event) {
+        if (SPEW) {
+            Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
+                + mDisabled);
+        }
+
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+            return false;
+        }
+
+        final int statusBarSize = mStatusBarView.getHeight();
+        final int hitSize = statusBarSize*2;
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            final int y = (int)event.getRawY();
+
+            if (!mExpanded) {
+                mViewDelta = statusBarSize - y;
+            } else {
+                mTrackingView.getLocationOnScreen(mAbsPos);
+                mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
+            }
+            if ((!mExpanded && y < hitSize) ||
+                    (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
+
+                // We drop events at the edge of the screen to make the windowshade come
+                // down by accident less, especially when pushing open a device with a keyboard
+                // that rotates (like g1 and droid)
+                int x = (int)event.getRawX();
+                final int edgeBorder = mEdgeBorder;
+                if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
+                    prepareTracking(y, !mExpanded);// opening if we're not already fully visible
+                    mVelocityTracker.addMovement(event);
+                }
+            }
+        } else if (mTracking) {
+            mVelocityTracker.addMovement(event);
+            final int minY = statusBarSize + mCloseView.getHeight();
+            if (event.getAction() == MotionEvent.ACTION_MOVE) {
+                int y = (int)event.getRawY();
+                if (mAnimatingReveal && y < minY) {
+                    // nothing
+                } else  {
+                    mAnimatingReveal = false;
+                    updateExpandedViewPos(y + mViewDelta);
+                }
+            } else if (event.getAction() == MotionEvent.ACTION_UP) {
+                mVelocityTracker.computeCurrentVelocity(1000);
+
+                float yVel = mVelocityTracker.getYVelocity();
+                boolean negative = yVel < 0;
+
+                float xVel = mVelocityTracker.getXVelocity();
+                if (xVel < 0) {
+                    xVel = -xVel;
+                }
+                if (xVel > 150.0f) {
+                    xVel = 150.0f; // limit how much we care about the x axis
+                }
+
+                float vel = (float)Math.hypot(yVel, xVel);
+                if (negative) {
+                    vel = -vel;
+                }
+
+                performFling((int)event.getRawY(), vel, false);
+            }
+
+        }
+        return false;
+    }
+
+    private class Launcher implements View.OnClickListener {
+        private PendingIntent mIntent;
+        private String mPkg;
+        private String mTag;
+        private int mId;
+
+        Launcher(PendingIntent intent, String pkg, String tag, int id) {
+            mIntent = intent;
+            mPkg = pkg;
+            mTag = tag;
+            mId = id;
+        }
+
+        public void onClick(View v) {
+            try {
+                // The intent we are sending is for the application, which
+                // won't have permission to immediately start an activity after
+                // the user switches to home.  We know it is safe to do at this
+                // point, so make sure new activity switches are now allowed.
+                ActivityManagerNative.getDefault().resumeAppSwitches();
+            } catch (RemoteException e) {
+            }
+
+            if (mIntent != null) {
+                int[] pos = new int[2];
+                v.getLocationOnScreen(pos);
+                Intent overlay = new Intent();
+                overlay.setSourceBounds(
+                        new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+                try {
+                    mIntent.send(StatusBarService.this, 0, overlay);
+                } catch (PendingIntent.CanceledException e) {
+                    // the stack trace isn't very helpful here.  Just log the exception message.
+                    Slog.w(TAG, "Sending contentIntent failed: " + e);
+                }
+            }
+
+            try {
+                mBarService.onNotificationClick(mPkg, mTag, mId);
+            } catch (RemoteException ex) {
+                // system process is dead if we're here.
+            }
+
+            // close the shade if it was open
+            animateCollapse();
+
+            // If this click was on the intruder alert, hide that instead
+            mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
+        }
+    }
+
+    private void tick(StatusBarNotification n) {
+        // Show the ticker if one is requested. Also don't do this
+        // until status bar window is attached to the window manager,
+        // because...  well, what's the point otherwise?  And trying to
+        // run a ticker without being attached will crash!
+        if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
+            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
+                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
+                mTicker.addEntry(n);
+            }
+        }
+    }
+
+    /**
+     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+     * about the failure.
+     *
+     * WARNING: this will call back into us.  Don't hold any locks.
+     */
+    void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
+        removeNotification(key);
+        try {
+            mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
+        } catch (RemoteException ex) {
+            // The end is nigh.
+        }
+    }
+
+    private class MyTicker extends Ticker {
+        MyTicker(Context context, StatusBarView sb) {
+            super(context, sb);
+        }
+
+        @Override
+        void tickerStarting() {
+            mTicking = true;
+            mIcons.setVisibility(View.GONE);
+            mTickerView.setVisibility(View.VISIBLE);
+            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
+            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
+            if (mExpandedVisible) {
+                setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
+            }
+        }
+
+        @Override
+        void tickerDone() {
+            mIcons.setVisibility(View.VISIBLE);
+            mTickerView.setVisibility(View.GONE);
+            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
+            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
+                        mTickingDoneListener));
+            if (mExpandedVisible) {
+                setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
+            }
+        }
+
+        void tickerHalting() {
+            mIcons.setVisibility(View.VISIBLE);
+            mTickerView.setVisibility(View.GONE);
+            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
+            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
+                        mTickingDoneListener));
+            if (mExpandedVisible) {
+                setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+            }
+        }
+    }
+
+    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
+        public void onAnimationEnd(Animation animation) {
+            mTicking = false;
+        }
+        public void onAnimationRepeat(Animation animation) {
+        }
+        public void onAnimationStart(Animation animation) {
+        }
+    };
+
+    private Animation loadAnim(int id, Animation.AnimationListener listener) {
+        Animation anim = AnimationUtils.loadAnimation(StatusBarService.this, id);
+        if (listener != null) {
+            anim.setAnimationListener(listener);
+        }
+        return anim;
+    }
+
+    public String viewInfo(View v) {
+        return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
+                + " " + v.getWidth() + "x" + v.getHeight() + ")";
+    }
+
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump StatusBar from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        synchronized (mQueueLock) {
+            pw.println("Current Status Bar state:");
+            pw.println("  mExpanded=" + mExpanded
+                    + ", mExpandedVisible=" + mExpandedVisible);
+            pw.println("  mTicking=" + mTicking);
+            pw.println("  mTracking=" + mTracking);
+            pw.println("  mAnimating=" + mAnimating
+                    + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
+                    + ", mAnimAccel=" + mAnimAccel);
+            pw.println("  mCurAnimationTime=" + mCurAnimationTime
+                    + " mAnimLastTime=" + mAnimLastTime);
+            pw.println("  mDisplayHeight=" + mDisplayHeight
+                    + " mAnimatingReveal=" + mAnimatingReveal
+                    + " mViewDelta=" + mViewDelta);
+            pw.println("  mDisplayHeight=" + mDisplayHeight);
+            pw.println("  mExpandedParams: " + mExpandedParams);
+            pw.println("  mExpandedView: " + viewInfo(mExpandedView));
+            pw.println("  mExpandedDialog: " + mExpandedDialog);
+            pw.println("  mTrackingParams: " + mTrackingParams);
+            pw.println("  mTrackingView: " + viewInfo(mTrackingView));
+            pw.println("  mOngoingTitle: " + viewInfo(mOngoingTitle));
+            pw.println("  mOngoingItems: " + viewInfo(mOngoingItems));
+            pw.println("  mLatestTitle: " + viewInfo(mLatestTitle));
+            pw.println("  mLatestItems: " + viewInfo(mLatestItems));
+            pw.println("  mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
+            pw.println("  mCloseView: " + viewInfo(mCloseView));
+            pw.println("  mTickerView: " + viewInfo(mTickerView));
+            pw.println("  mScrollView: " + viewInfo(mScrollView)
+                    + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
+            pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
+        }
+        /*
+        synchronized (mNotificationData) {
+            int N = mNotificationData.ongoingCount();
+            pw.println("  ongoingCount.size=" + N);
+            for (int i=0; i<N; i++) {
+                StatusBarNotification n = mNotificationData.getOngoing(i);
+                pw.println("    [" + i + "] key=" + n.key + " view=" + n.view);
+                pw.println("           data=" + n.data);
+            }
+            N = mNotificationData.latestCount();
+            pw.println("  ongoingCount.size=" + N);
+            for (int i=0; i<N; i++) {
+                StatusBarNotification n = mNotificationData.getLatest(i);
+                pw.println("    [" + i + "] key=" + n.key + " view=" + n.view);
+                pw.println("           data=" + n.data);
+            }
+        }
+        */
+
+        if (false) {
+            pw.println("see the logcat for a dump of the views we have created.");
+            // must happen on ui thread
+            mHandler.post(new Runnable() {
+                    public void run() {
+                        mStatusBarView.getLocationOnScreen(mAbsPos);
+                        Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+                                + ") " + mStatusBarView.getWidth() + "x"
+                                + mStatusBarView.getHeight());
+                        mStatusBarView.debug();
+
+                        mExpandedView.getLocationOnScreen(mAbsPos);
+                        Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+                                + ") " + mExpandedView.getWidth() + "x"
+                                + mExpandedView.getHeight());
+                        mExpandedView.debug();
+
+                        mTrackingView.getLocationOnScreen(mAbsPos);
+                        Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+                                + ") " + mTrackingView.getWidth() + "x"
+                                + mTrackingView.getHeight());
+                        mTrackingView.debug();
+                    }
+                });
+        }
+    }
+
+    void onBarViewAttached() {
+        WindowManager.LayoutParams lp;
+        int pixelFormat;
+        Drawable bg;
+
+        /// ---------- Tracking View --------------
+        pixelFormat = PixelFormat.RGBX_8888;
+        bg = mTrackingView.getBackground();
+        if (bg != null) {
+            pixelFormat = bg.getOpacity();
+        }
+
+        lp = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                pixelFormat);
+//        lp.token = mStatusBarView.getWindowToken();
+        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        lp.setTitle("TrackingView");
+        lp.y = mTrackingPosition;
+        mTrackingParams = lp;
+
+        WindowManagerImpl.getDefault().addView(mTrackingView, lp);
+    }
+
+    void onTrackingViewAttached() {
+        WindowManager.LayoutParams lp;
+        int pixelFormat;
+        Drawable bg;
+
+        /// ---------- Expanded View --------------
+        pixelFormat = PixelFormat.TRANSLUCENT;
+
+        final int disph = mDisplay.getHeight();
+        lp = mExpandedDialog.getWindow().getAttributes();
+        lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
+        lp.height = getExpandedHeight();
+        lp.x = 0;
+        mTrackingPosition = lp.y = -disph; // sufficiently large negative
+        lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+        lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | WindowManager.LayoutParams.FLAG_DITHER
+                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        lp.format = pixelFormat;
+        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        lp.setTitle("StatusBarExpanded");
+        mExpandedDialog.getWindow().setAttributes(lp);
+        mExpandedDialog.getWindow().setFormat(pixelFormat);
+        mExpandedParams = lp;
+
+        mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+        mExpandedDialog.setContentView(mExpandedView,
+                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                                           ViewGroup.LayoutParams.MATCH_PARENT));
+        mExpandedDialog.getWindow().setBackgroundDrawable(null);
+        mExpandedDialog.show();
+        FrameLayout hack = (FrameLayout)mExpandedView.getParent();
+    }
+
+    void setDateViewVisibility(boolean visible, int anim) {
+        mDateView.setUpdates(visible);
+        mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+        mDateView.startAnimation(loadAnim(anim, null));
+    }
+
+    void setNotificationIconVisibility(boolean visible, int anim) {
+        int old = mNotificationIcons.getVisibility();
+        int v = visible ? View.VISIBLE : View.INVISIBLE;
+        if (old != v) {
+            mNotificationIcons.setVisibility(v);
+            mNotificationIcons.startAnimation(loadAnim(anim, null));
+        }
+    }
+
+    void updateExpandedViewPos(int expandedPosition) {
+        if (SPEW) {
+            Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
+                    + " mTrackingParams.y=" + mTrackingParams.y
+                    + " mTrackingPosition=" + mTrackingPosition);
+        }
+
+        int h = mStatusBarView.getHeight();
+        int disph = mDisplay.getHeight();
+
+        // If the expanded view is not visible, make sure they're still off screen.
+        // Maybe the view was resized.
+        if (!mExpandedVisible) {
+            if (mTrackingView != null) {
+                mTrackingPosition = -disph;
+                if (mTrackingParams != null) {
+                    mTrackingParams.y = mTrackingPosition;
+                    WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+                }
+            }
+            if (mExpandedParams != null) {
+                mExpandedParams.y = -disph;
+                mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+            }
+            return;
+        }
+
+        // tracking view...
+        int pos;
+        if (expandedPosition == EXPANDED_FULL_OPEN) {
+            pos = h;
+        }
+        else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
+            pos = mTrackingPosition;
+        }
+        else {
+            if (expandedPosition <= disph) {
+                pos = expandedPosition;
+            } else {
+                pos = disph;
+            }
+            pos -= disph-h;
+        }
+        mTrackingPosition = mTrackingParams.y = pos;
+        mTrackingParams.height = disph-h;
+        WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+
+        if (mExpandedParams != null) {
+            mCloseView.getLocationInWindow(mPositionTmp);
+            final int closePos = mPositionTmp[1];
+
+            mExpandedContents.getLocationInWindow(mPositionTmp);
+            final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
+
+            mExpandedParams.y = pos + mTrackingView.getHeight()
+                    - (mTrackingParams.height-closePos) - contentsBottom;
+            int max = h;
+            if (mExpandedParams.y > max) {
+                mExpandedParams.y = max;
+            }
+            int min = mTrackingPosition;
+            if (mExpandedParams.y < min) {
+                mExpandedParams.y = min;
+            }
+
+            boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
+            if (!visible) {
+                // if the contents aren't visible, move the expanded view way off screen
+                // because the window itself extends below the content view.
+                mExpandedParams.y = -disph;
+            }
+            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+
+            // As long as this isn't just a repositioning that's not supposed to affect
+            // the user's perception of what's showing, call to say that the visibility
+            // has changed. (Otherwise, someone else will call to do that).
+            if (expandedPosition != EXPANDED_LEAVE_ALONE) {
+                if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
+                visibilityChanged(visible);
+            }
+        }
+
+        if (SPEW) {
+            Slog.d(TAG, "updateExpandedViewPos after  expandedPosition=" + expandedPosition
+                    + " mTrackingParams.y=" + mTrackingParams.y
+                    + " mTrackingPosition=" + mTrackingPosition
+                    + " mExpandedParams.y=" + mExpandedParams.y
+                    + " mExpandedParams.height=" + mExpandedParams.height);
+        }
+    }
+
+    int getExpandedHeight() {
+        return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
+    }
+
+    void updateExpandedHeight() {
+        if (mExpandedView != null) {
+            mExpandedParams.height = getExpandedHeight();
+            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+        }
+    }
+
+    /**
+     * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
+     * This was added last-minute and is inconsistent with the way the rest of the notifications
+     * are handled, because the notification isn't really cancelled.  The lights are just
+     * turned off.  If any other notifications happen, the lights will turn back on.  Steve says
+     * this is what he wants. (see bug 1131461)
+     */
+    void visibilityChanged(boolean visible) {
+        if (mPanelSlightlyVisible != visible) {
+            mPanelSlightlyVisible = visible;
+            try {
+                mBarService.onPanelRevealed();
+            } catch (RemoteException ex) {
+                // Won't fail unless the world has ended.
+            }
+        }
+    }
+
+    void performDisableActions(int net) {
+        int old = mDisabled;
+        int diff = net ^ old;
+        mDisabled = net;
+
+        // act accordingly
+        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+            if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
+                Slog.d(TAG, "DISABLE_EXPAND: yes");
+                animateCollapse();
+            }
+        }
+        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+            if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+                if (mTicking) {
+                    mNotificationIcons.setVisibility(View.INVISIBLE);
+                    mTicker.halt();
+                } else {
+                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
+                }
+            } else {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+                if (!mExpandedVisible) {
+                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+                }
+            }
+        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+                mTicker.halt();
+            }
+        }
+    }
+
+    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            try {
+                mBarService.onClearAllNotifications();
+            } catch (RemoteException ex) {
+                // system process is dead if we're here.
+            }
+            animateCollapse();
+        }
+    };
+
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+                    || Intent.ACTION_SCREEN_OFF.equals(action)) {
+                //collapse();
+            }
+            else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+                updateResources();
+            }
+        }
+    };
+
+    private void setIntruderAlertVisibility(boolean vis) {
+        mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
+    }
+
+    /**
+     * Reload some of our resources when the configuration changes.
+     *
+     * We don't reload everything when the configuration changes -- we probably
+     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
+     * meantime, just update the things that we know change.
+     */
+    void updateResources() {
+        Resources res = getResources();
+
+        mClearButton.setText(getText(R.string.status_bar_clear_all_button));
+        mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title));
+        mLatestTitle.setText(getText(R.string.status_bar_latest_events_title));
+        mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title));
+
+        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
+        if (false) Slog.v(TAG, "updateResources");
+    }
+
+    //
+    // tracing
+    //
+
+    void postStartTracing() {
+        mHandler.postDelayed(mStartTracing, 3000);
+    }
+
+    void vibrate() {
+        android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
+        vib.vibrate(250);
+    }
+
+    Runnable mStartTracing = new Runnable() {
+        public void run() {
+            vibrate();
+            SystemClock.sleep(250);
+            Slog.d(TAG, "startTracing");
+            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
+            mHandler.postDelayed(mStopTracing, 10000);
+        }
+    };
+
+    Runnable mStopTracing = new Runnable() {
+        public void run() {
+            android.os.Debug.stopMethodTracing();
+            Slog.d(TAG, "stopTracing");
+            vibrate();
+        }
+    };
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
index 466cc75..1e140b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
@@ -34,7 +34,7 @@
 
     static final int DIM_ANIM_TIME = 400;
     
-    PhoneStatusBarService mService;
+    StatusBarService mService;
     boolean mTracking;
     int mStartX, mStartY;
     ViewGroup mNotificationIcons;
@@ -94,7 +94,7 @@
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
-        mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
+        mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java
index c59eb6a..9108eee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java
@@ -26,7 +26,7 @@
 
 public class TrackingView extends LinearLayout {
     final Display mDisplay;
-    PhoneStatusBarService mService;
+    StatusBarService mService;
     boolean mTracking;
     int mStartX, mStartY;
 
diff --git a/core/jni/android_view_InputTarget.h b/services/java/com/android/server/InputApplication.java
similarity index 62%
copy from core/jni/android_view_InputTarget.h
copy to services/java/com/android/server/InputApplication.java
index 9230b1b..38420d4 100644
--- a/core/jni/android_view_InputTarget.h
+++ b/services/java/com/android/server/InputApplication.java
@@ -14,18 +14,20 @@
  * limitations under the License.
  */
 
-#ifndef _ANDROID_VIEW_INPUTTARGET_H
-#define _ANDROID_VIEW_INPUTTARGET_H
+package com.android.server;
 
-#include "jni.h"
-
-namespace android {
-
-class InputTarget;
-
-extern void android_view_InputTarget_toNative(JNIEnv* env, jobject inputTargetObj,
-        InputTarget* outInputTarget);
-
-} // namespace android
-
-#endif // _ANDROID_OS_INPUTTARGET_H
+/**
+ * Describes input-related application properties for use by the input dispatcher.
+ * 
+ * @hide
+ */
+public final class InputApplication {
+    // Application name.
+    public String name;
+    
+    // Dispatching timeout.
+    public long dispatchingTimeoutNanos;
+    
+    // The application window token.
+    public Object token;
+}
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 8d9bb29..2ba2914 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -17,23 +17,20 @@
 package com.android.server;
 
 import com.android.internal.util.XmlUtils;
-import com.android.server.KeyInputQueue.VirtualKey;
 
 import org.xmlpull.v1.XmlPullParser;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.os.Environment;
 import android.os.LocalPowerManager;
 import android.os.PowerManager;
-import android.util.Log;
 import android.util.Slog;
 import android.util.Xml;
 import android.view.InputChannel;
-import android.view.InputTarget;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
-import android.view.RawInputEvent;
 import android.view.Surface;
 import android.view.WindowManagerPolicy;
 
@@ -85,6 +82,10 @@
             int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
     private static native int nativeInjectMotionEvent(MotionEvent event, int nature,
             int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
+    private static native void nativeSetInputWindows(InputWindow[] windows);
+    private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
+    private static native void nativeSetFocusedApplication(InputApplication application);
+    private static native void nativePreemptInputDispatch();
     
     // Device class as defined by EventHub.
     private static final int CLASS_KEYBOARD = 0x00000001;
@@ -284,6 +285,22 @@
                 sync, timeoutMillis);
     }
     
+    public void setInputWindows(InputWindow[] windows) {
+        nativeSetInputWindows(windows);
+    }
+    
+    public void setFocusedApplication(InputApplication application) {
+        nativeSetFocusedApplication(application);
+    }
+    
+    public void preemptInputDispatch() {
+        nativePreemptInputDispatch();
+    }
+    
+    public void setInputDispatchMode(boolean enabled, boolean frozen) {
+        nativeSetInputDispatchMode(enabled, frozen);
+    }
+    
     public void dump(PrintWriter pw) {
         // TODO
     }
@@ -344,32 +361,43 @@
         
         @SuppressWarnings("unused")
         public void notifyInputChannelBroken(InputChannel inputChannel) {
-            mWindowManagerService.notifyInputChannelBroken(inputChannel);
+            mWindowManagerService.mInputMonitor.notifyInputChannelBroken(inputChannel);
         }
 
         @SuppressWarnings("unused")
         public long notifyInputChannelANR(InputChannel inputChannel) {
-            return mWindowManagerService.notifyInputChannelANR(inputChannel);
+            return mWindowManagerService.mInputMonitor.notifyInputChannelANR(inputChannel);
         }
 
         @SuppressWarnings("unused")
         public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
-            mWindowManagerService.notifyInputChannelRecoveredFromANR(inputChannel);
+            mWindowManagerService.mInputMonitor.notifyInputChannelRecoveredFromANR(inputChannel);
         }
         
         @SuppressWarnings("unused")
-        public int hackInterceptKey(int deviceId, int type, int scanCode,
+        public long notifyANR(Object token) {
+            return mWindowManagerService.mInputMonitor.notifyANR(token);
+        }
+        
+        @SuppressWarnings("unused")
+        public int interceptKeyBeforeQueueing(int deviceId, int type, int scanCode,
                 int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) {
-            RawInputEvent event = new RawInputEvent();
-            event.deviceId = deviceId;
-            event.type = type;
-            event.scancode = scanCode;
-            event.keycode = keyCode;
-            event.flags = policyFlags;
-            event.value = value;
-            event.when = whenNanos / 1000000;
-            
-            return mWindowManagerPolicy.interceptKeyTq(event, isScreenOn);
+            return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(deviceId, type,
+                    scanCode, keyCode, policyFlags, value, whenNanos, isScreenOn);
+        }
+        
+        @SuppressWarnings("unused")
+        public boolean interceptKeyBeforeDispatching(InputChannel focus, int keyCode,
+                int metaState, boolean down, int repeatCount, int policyFlags) {
+            return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus,
+                    keyCode, metaState, down, repeatCount, policyFlags);
+        }
+        
+        @SuppressWarnings("unused")
+        public boolean checkInjectEventsPermission(int injectorPid, int injectorUid) {
+            return mContext.checkPermission(
+                    android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
+                    == PackageManager.PERMISSION_GRANTED;
         }
         
         @SuppressWarnings("unused")
@@ -379,15 +407,14 @@
         }
         
         @SuppressWarnings("unused")
-        public void pokeUserActivityForKey(long whenNanos) {
-            long when = whenNanos / 1000000;
-            mPowerManagerService.userActivity(when, false,
-                    LocalPowerManager.BUTTON_EVENT, false);
+        public void pokeUserActivity(long eventTimeNanos, int eventType) {
+            long eventTime = eventTimeNanos / 1000000;
+            mPowerManagerService.userActivity(eventTime, false, eventType, false);
         }
         
         @SuppressWarnings("unused")
         public void notifyAppSwitchComing() {
-            mWindowManagerService.mKeyWaiter.appSwitchComing();
+            mWindowManagerService.mInputMonitor.notifyAppSwitchComing();
         }
         
         @SuppressWarnings("unused")
@@ -485,24 +512,5 @@
             
             return names.toArray(new String[names.size()]);
         }
-        
-        // TODO All code related to target identification should be moved down into native.
-        @SuppressWarnings("unused")
-        public int getKeyEventTargets(InputTargetList inputTargets,
-                KeyEvent event, int nature, int policyFlags,
-                int injectorPid, int injectorUid) {
-            inputTargets.clear();
-            return mWindowManagerService.getKeyEventTargetsTd(
-                    inputTargets, event, nature, policyFlags, injectorPid, injectorUid);
-        }
-        
-        @SuppressWarnings("unused")
-        public int getMotionEventTargets(InputTargetList inputTargets,
-                MotionEvent event, int nature, int policyFlags,
-                int injectorPid, int injectorUid) {
-            inputTargets.clear();
-            return mWindowManagerService.getMotionEventTargetsTd(
-                    inputTargets, event, nature, policyFlags, injectorPid, injectorUid);
-        }
     }
 }
diff --git a/services/java/com/android/server/InputTargetList.java b/services/java/com/android/server/InputTargetList.java
deleted file mode 100644
index 83acc8f..0000000
--- a/services/java/com/android/server/InputTargetList.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.server;
-
-import android.view.InputChannel;
-import android.view.InputTarget;
-
-/**
- * A specialized list of input targets backed by an array.
- * 
- * This class is part of an InputManager optimization to avoid allocating and copying
- * input target arrays unnecessarily on return from JNI callbacks.  Internally, it keeps
- * an array full of demand-allocated InputTarget objects that it recycles each time the
- * list is cleared.  The used portion of the array is padded with a null.
- * 
- * @hide
- */
-public final class InputTargetList {
-    private InputTarget[] mArray;
-    private int mCount;
-    
-    /**
-     * Creates an empty input target list.
-     */
-    public InputTargetList() {
-        mArray = new InputTarget[8];
-    }
-    
-    /**
-     * Clears the input target list.
-     */
-    public void clear() {
-        if (mCount == 0) {
-            return;
-        }
-        
-        int count = mCount;
-        mCount = 0;
-        mArray[count] = mArray[0];
-        while (count > 0) {
-            count -= 1;
-            mArray[count].recycle();
-        }
-        mArray[0] = null;
-    }
-    
-    /**
-     * Adds a new input target to the input target list.
-     * @param inputChannel The input channel of the target window.
-     * @param flags Input target flags.
-     * @param timeoutNanos The input dispatch timeout (before ANR) in nanoseconds or -1 if none.
-     * @param xOffset An offset to add to motion X coordinates during delivery.
-     * @param yOffset An offset to add to motion Y coordinates during delivery.
-     */
-    public void add(InputChannel inputChannel, int flags, long timeoutNanos,
-            float xOffset, float yOffset) {
-        if (inputChannel == null) {
-            throw new IllegalArgumentException("inputChannel must not be null");
-        }
-        
-        if (mCount + 1 == mArray.length) {
-            InputTarget[] oldArray = mArray;
-            mArray = new InputTarget[oldArray.length * 2];
-            System.arraycopy(oldArray, 0, mArray, 0, mCount);
-        }
-        
-        // Grab InputTarget from tail (after used section) if available.
-        InputTarget inputTarget = mArray[mCount + 1];
-        if (inputTarget == null) {
-            inputTarget = new InputTarget();
-        }
-        inputTarget.mInputChannel = inputChannel;
-        inputTarget.mFlags = flags;
-        inputTarget.mTimeoutNanos = timeoutNanos;
-        inputTarget.mXOffset = xOffset;
-        inputTarget.mYOffset = yOffset;
-        
-        mArray[mCount] = inputTarget;
-        mCount += 1;
-        mArray[mCount] = null;
-    }
-    
-    /**
-     * Gets the input targets as a null-terminated array.
-     * @return The input target array.
-     */
-    public InputTarget[] toNullTerminatedArray() {
-        return mArray;
-    }
-}
\ No newline at end of file
diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/InputWindow.java
new file mode 100644
index 0000000..8da0cf1
--- /dev/null
+++ b/services/java/com/android/server/InputWindow.java
@@ -0,0 +1,66 @@
+/*
+ * 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.server;
+
+import android.view.InputChannel;
+
+/**
+ * Describes input-related window properties for use by the input dispatcher.
+ * 
+ * @hide
+ */
+public final class InputWindow {
+    // The input channel associated with the window.
+    public InputChannel inputChannel;
+    
+    // Window layout params attributes.  (WindowManager.LayoutParams)
+    public int layoutParamsFlags;
+    public int layoutParamsType;
+    
+    // Dispatching timeout.
+    public long dispatchingTimeoutNanos;
+    
+    // Window frame position.
+    public int frameLeft;
+    public int frameTop;
+    
+    // Window touchable area.
+    public int touchableAreaLeft;
+    public int touchableAreaTop;
+    public int touchableAreaRight;
+    public int touchableAreaBottom;
+    
+    // Window is visible.
+    public boolean visible;
+    
+    // Window has focus.
+    public boolean hasFocus;
+    
+    // Window has wallpaper.  (window is the current wallpaper target)
+    public boolean hasWallpaper;
+    
+    // Input event dispatching is paused.
+    public boolean paused;
+    
+    // Id of process and user that owns the window.
+    public int ownerPid;
+    public int ownerUid;
+    
+    public void recycle() {
+        inputChannel = null;
+    }
+}
diff --git a/services/java/com/android/server/InputWindowList.java b/services/java/com/android/server/InputWindowList.java
new file mode 100644
index 0000000..1cbb2cc
--- /dev/null
+++ b/services/java/com/android/server/InputWindowList.java
@@ -0,0 +1,89 @@
+/*
+ * 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.server;
+
+
+/**
+ * A specialized list of window information objects backed by an array.
+ * 
+ * This class is part of an InputManager optimization to avoid allocating objects and arrays
+ * unnecessarily.  Internally, it keeps an array full of demand-allocated objects that it
+ * recycles each time the list is cleared.  The used portion of the array is padded with a null.
+ * 
+ * The contents of the list are intended to be Z-ordered from top to bottom.
+ * 
+ * @hide
+ */
+public final class InputWindowList {
+    private InputWindow[] mArray;
+    private int mCount;
+    
+    /**
+     * Creates an empty list.
+     */
+    public InputWindowList() {
+        mArray = new InputWindow[8];
+    }
+    
+    /**
+     * Clears the list.
+     */
+    public void clear() {
+        if (mCount == 0) {
+            return;
+        }
+        
+        int count = mCount;
+        mCount = 0;
+        mArray[count] = mArray[0];
+        while (count > 0) {
+            count -= 1;
+            mArray[count].recycle();
+        }
+        mArray[0] = null;
+    }
+    
+    /**
+     * Adds an uninitialized input window object to the list and returns it.
+     */
+    public InputWindow add() {
+        if (mCount + 1 == mArray.length) {
+            InputWindow[] oldArray = mArray;
+            mArray = new InputWindow[oldArray.length * 2];
+            System.arraycopy(oldArray, 0, mArray, 0, mCount);
+        }
+        
+        // Grab object from tail (after used section) if available.
+        InputWindow item = mArray[mCount + 1];
+        if (item == null) {
+            item = new InputWindow();
+        }
+        
+        mArray[mCount] = item;
+        mCount += 1;
+        mArray[mCount] = null;
+        return item;
+    }
+    
+    /**
+     * Gets the input window objects as a null-terminated array.
+     * @return The input window array.
+     */
+    public InputWindow[] toNullTerminatedArray() {
+        return mArray;
+    }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 3e2c122..25de8b0 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -38,9 +38,11 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.hardware.Usb;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.BatteryManager;
+import android.os.Bundle;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -111,8 +113,6 @@
     private boolean mNotificationPulseEnabled;
 
     // for adb connected notifications
-    private boolean mUsbConnected;
-    private boolean mAdbEnabled = false;
     private boolean mAdbNotificationShown = false;
     private Notification mAdbNotification;
 
@@ -346,12 +346,14 @@
                     mBatteryFull = batteryFull;
                     updateLights();
                 }
-            } else if (action.equals(Intent.ACTION_UMS_CONNECTED)) {
-                mUsbConnected = true;
-                updateAdbNotification();
-            } else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) {
-                mUsbConnected = false;
-                updateAdbNotification();
+            } else if (action.equals(Usb.ACTION_USB_STATE)) {
+                Bundle extras = intent.getExtras();
+                boolean usbConnected = extras.getBoolean(Usb.USB_CONNECTED);
+                boolean adbEnabled = (Usb.USB_FUNCTION_ENABLED.equals(
+                                    extras.getString(Usb.USB_FUNCTION_ADB)));
+                updateAdbNotification(usbConnected && adbEnabled);
+            } else if (action.equals(Usb.ACTION_USB_DISCONNECTED)) {
+                updateAdbNotification(false);
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
                     || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
@@ -397,8 +399,6 @@
 
         void observe() {
             ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.ADB_ENABLED), false, this);
             resolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
             update();
@@ -410,12 +410,6 @@
 
         public void update() {
             ContentResolver resolver = mContext.getContentResolver();
-            boolean adbEnabled = Settings.Secure.getInt(resolver,
-                        Settings.Secure.ADB_ENABLED, 0) != 0;
-            if (mAdbEnabled != adbEnabled) {
-                mAdbEnabled = adbEnabled;
-                updateAdbNotification();
-            }
             boolean pulseEnabled = Settings.System.getInt(resolver,
                         Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
             if (mNotificationPulseEnabled != pulseEnabled) {
@@ -464,8 +458,7 @@
         // register for battery changed notifications
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-        filter.addAction(Intent.ACTION_UMS_CONNECTED);
-        filter.addAction(Intent.ACTION_UMS_DISCONNECTED);
+        filter.addAction(Usb.ACTION_USB_STATE);
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
@@ -1137,8 +1130,8 @@
     // This is here instead of StatusBarPolicy because it is an important
     // security feature that we don't want people customizing the platform
     // to accidentally lose.
-    private void updateAdbNotification() {
-        if (mAdbEnabled && mUsbConnected) {
+    private void updateAdbNotification(boolean adbEnabled) {
+        if (adbEnabled) {
             if ("0".equals(SystemProperties.get("persist.adb.notify"))) {
                 return;
             }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7130636..c01680e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -80,7 +80,8 @@
                 android.os.Process.THREAD_PRIORITY_FOREGROUND);
 
         BinderInternal.disableBackgroundScheduling(true);
-        
+        android.os.Process.setCanSelfBackground(false);
+
         String factoryTestStr = SystemProperties.get("ro.factorytest");
         int factoryTest = "".equals(factoryTestStr) ? SystemServer.FACTORY_TEST_OFF
                 : Integer.parseInt(factoryTestStr);
diff --git a/services/java/com/android/server/UsbObserver.java b/services/java/com/android/server/UsbObserver.java
index 3993a7f..d08fe9b 100644
--- a/services/java/com/android/server/UsbObserver.java
+++ b/services/java/com/android/server/UsbObserver.java
@@ -159,6 +159,16 @@
     }
 
     private final Handler mHandler = new Handler() {
+        private void addEnabledFunctions(Intent intent) {
+            // include state of all USB functions in our extras
+            for (int i = 0; i < mEnabledFunctions.size(); i++) {
+                intent.putExtra(mEnabledFunctions.get(i), Usb.USB_FUNCTION_ENABLED);
+            }
+            for (int i = 0; i < mDisabledFunctions.size(); i++) {
+                intent.putExtra(mDisabledFunctions.get(i), Usb.USB_FUNCTION_DISABLED);
+            }
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -174,21 +184,21 @@
                         // Send an Intent containing connected/disconnected state
                         // and the enabled/disabled state of all USB functions
                         Intent intent;
-                        if (mUsbConfig != 0) {
+                        boolean usbConnected = (mUsbConfig != 0);
+                        if (usbConnected) {
                             intent = new Intent(Usb.ACTION_USB_CONNECTED);
-
-                            // include state of all USB functions in our extras
-                            for (int i = 0; i < mEnabledFunctions.size(); i++) {
-                                intent.putExtra(mEnabledFunctions.get(i), Usb.USB_FUNCTION_ENABLED);
-                            }
-                            for (int i = 0; i < mDisabledFunctions.size(); i++) {
-                                intent.putExtra(mDisabledFunctions.get(i), Usb.USB_FUNCTION_DISABLED);
-                            }
+                            addEnabledFunctions(intent);
                         } else {
                             intent = new Intent(Usb.ACTION_USB_DISCONNECTED);
                         }
+                        mContext.sendBroadcast(intent);
 
-                        mContext.sendBroadcast(intent, android.Manifest.permission.ACCESS_USB);
+                        // send a sticky broadcast for clients interested in both connect and disconnect
+                        intent = new Intent(Usb.ACTION_USB_STATE);
+                        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+                        intent.putExtra(Usb.USB_CONNECTED, usbConnected);
+                        addEnabledFunctions(intent);
+                        mContext.sendStickyBroadcast(intent);
                     }
                     break;
             }
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index d4133f3..a742093 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -57,20 +57,10 @@
     static final boolean RECORD_KERNEL_THREADS = true;
 
     static final int MONITOR = 2718;
-    static final int GLOBAL_PSS = 2719;
 
     static final int TIME_TO_RESTART = DB ? 15*1000 : 60*1000;
     static final int TIME_TO_WAIT = TIME_TO_RESTART / 2;
 
-    static final int MEMCHECK_DEFAULT_INTERVAL = DB ? 30 : 30*60; // 30 minutes
-    static final int MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL = DB ? 60 : 2*60*60;      // 2 hours
-    static final int MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD = (DB ? 10:16)*1024*1024; // 16MB
-    static final int MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD = (DB ? 14:20)*1024*1024; // 20MB
-    static final int MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD = (DB ? 4:8)*1024*1024;    // 8MB
-    static final int MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD = (DB ? 8:12)*1024*1024;   // 12MB
-
-    static final int MEMCHECK_DEFAULT_EXEC_START_TIME = 1*60*60;           // 1:00am
-    static final int MEMCHECK_DEFAULT_EXEC_END_TIME = 5*60*60;             // 5:00am
     static final int MEMCHECK_DEFAULT_MIN_SCREEN_OFF = DB ? 1*60 : 5*60;   // 5 minutes
     static final int MEMCHECK_DEFAULT_MIN_ALARM = DB ? 1*60 : 3*60;        // 3 minutes
     static final int MEMCHECK_DEFAULT_RECHECK_INTERVAL = DB ? 1*60 : 5*60; // 5 minutes
@@ -79,14 +69,12 @@
     static final int REBOOT_DEFAULT_START_TIME = 3*60*60;                  // 3:00am
     static final int REBOOT_DEFAULT_WINDOW = 60*60;                        // within 1 hour
 
-    static final String CHECKUP_ACTION = "com.android.service.Watchdog.CHECKUP";
     static final String REBOOT_ACTION = "com.android.service.Watchdog.REBOOT";
 
     static Watchdog sWatchdog;
 
     /* This handler will be used to post message back onto the main thread */
     final Handler mHandler;
-    final Runnable mGlobalPssCollected;
     final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
     ContentResolver mResolver;
     BatteryService mBattery;
@@ -97,31 +85,9 @@
     boolean mForceKillSystem;
     Monitor mCurrentMonitor;
 
-    PssRequestor mPhoneReq;
     int mPhonePid;
-    int mPhonePss;
-
-    long mLastMemCheckTime = -(MEMCHECK_DEFAULT_INTERVAL*1000);
-    boolean mHavePss;
-    long mLastMemCheckRealtime = -(MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL*1000);
-    boolean mHaveGlobalPss;
-    final MemMonitor mSystemMemMonitor = new MemMonitor("system",
-            Settings.Secure.MEMCHECK_SYSTEM_ENABLED,
-            Settings.Secure.MEMCHECK_SYSTEM_SOFT_THRESHOLD,
-            MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD,
-            Settings.Secure.MEMCHECK_SYSTEM_HARD_THRESHOLD,
-            MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD);
-    final MemMonitor mPhoneMemMonitor = new MemMonitor("com.android.phone",
-            Settings.Secure.MEMCHECK_PHONE_ENABLED,
-            Settings.Secure.MEMCHECK_PHONE_SOFT_THRESHOLD,
-            MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD,
-            Settings.Secure.MEMCHECK_PHONE_HARD_THRESHOLD,
-            MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD);
 
     final Calendar mCalendar = Calendar.getInstance();
-    long mMemcheckLastTime;
-    long mMemcheckExecStartTime;
-    long mMemcheckExecEndTime;
     int mMinScreenOff = MEMCHECK_DEFAULT_MIN_SCREEN_OFF;
     int mMinAlarm = MEMCHECK_DEFAULT_MIN_ALARM;
     boolean mNeedScheduledCheck;
@@ -140,126 +106,13 @@
     int mReqRecheckInterval= -1;  // >= 0 if a specific recheck interval has been requested
 
     /**
-     * This class monitors the memory in a particular process.
-     */
-    final class MemMonitor {
-        final String mProcessName;
-        final String mEnabledSetting;
-        final String mSoftSetting;
-        final String mHardSetting;
-
-        int mSoftThreshold;
-        int mHardThreshold;
-        boolean mEnabled;
-        long mLastPss;
-
-        static final int STATE_OK = 0;
-        static final int STATE_SOFT = 1;
-        static final int STATE_HARD = 2;
-        int mState;
-
-        MemMonitor(String processName, String enabledSetting,
-                String softSetting, int defSoftThreshold,
-                String hardSetting, int defHardThreshold) {
-            mProcessName = processName;
-            mEnabledSetting = enabledSetting;
-            mSoftSetting = softSetting;
-            mHardSetting = hardSetting;
-            mSoftThreshold = defSoftThreshold;
-            mHardThreshold = defHardThreshold;
-        }
-
-        void retrieveSettings(ContentResolver resolver) {
-            mSoftThreshold = Settings.Secure.getInt(
-                    resolver, mSoftSetting, mSoftThreshold);
-            mHardThreshold = Settings.Secure.getInt(
-                    resolver, mHardSetting, mHardThreshold);
-            mEnabled = Settings.Secure.getInt(
-                    resolver, mEnabledSetting, 0) != 0;
-        }
-
-        boolean checkLocked(long curTime, int pid, int pss) {
-            mLastPss = pss;
-            if (mLastPss < mSoftThreshold) {
-                mState = STATE_OK;
-            } else if (mLastPss < mHardThreshold) {
-                mState = STATE_SOFT;
-            } else {
-                mState = STATE_HARD;
-            }
-            EventLog.writeEvent(EventLogTags.WATCHDOG_PROC_PSS, mProcessName, pid, mLastPss);
-
-            if (mState == STATE_OK) {
-                // Memory is good, don't recover.
-                return false;
-            }
-
-            if (mState == STATE_HARD) {
-                // Memory is really bad, kill right now.
-                EventLog.writeEvent(EventLogTags.WATCHDOG_HARD_RESET, mProcessName, pid,
-                        mHardThreshold, mLastPss);
-                return mEnabled;
-            }
-
-            // It is time to schedule a reset...
-            // Check if we are currently within the time to kill processes due
-            // to memory use.
-            computeMemcheckTimesLocked(curTime);
-            String skipReason = null;
-            if (curTime < mMemcheckExecStartTime || curTime > mMemcheckExecEndTime) {
-                skipReason = "time";
-            } else {
-                skipReason = shouldWeBeBrutalLocked(curTime);
-            }
-            EventLog.writeEvent(EventLogTags.WATCHDOG_SOFT_RESET, mProcessName, pid,
-                    mSoftThreshold, mLastPss, skipReason != null ? skipReason : "");
-            if (skipReason != null) {
-                mNeedScheduledCheck = true;
-                return false;
-            }
-            return mEnabled;
-        }
-
-        void clear() {
-            mLastPss = 0;
-            mState = STATE_OK;
-        }
-    }
-
-    /**
      * Used for scheduling monitor callbacks and checking memory usage.
      */
     final class HeartbeatHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case GLOBAL_PSS: {
-                    if (mHaveGlobalPss) {
-                        // During the last pass we collected pss information, so
-                        // now it is time to report it.
-                        mHaveGlobalPss = false;
-                        if (localLOGV) Slog.v(TAG, "Received global pss, logging.");
-                        logGlobalMemory();
-                    }
-                } break;
-
                 case MONITOR: {
-                    if (mHavePss) {
-                        // During the last pass we collected pss information, so
-                        // now it is time to report it.
-                        mHavePss = false;
-                        if (localLOGV) Slog.v(TAG, "Have pss, checking memory.");
-                        checkMemory();
-                    }
-
-                    if (mHaveGlobalPss) {
-                        // During the last pass we collected pss information, so
-                        // now it is time to report it.
-                        mHaveGlobalPss = false;
-                        if (localLOGV) Slog.v(TAG, "Have global pss, logging.");
-                        logGlobalMemory();
-                    }
-
                     long now = SystemClock.uptimeMillis();
 
                     // See if we should force a reboot.
@@ -274,32 +127,6 @@
                         checkReboot(false);
                     }
 
-                    // See if we should check memory conditions.
-                    long memCheckInterval = Settings.Secure.getLong(
-                            mResolver, Settings.Secure.MEMCHECK_INTERVAL,
-                            MEMCHECK_DEFAULT_INTERVAL) * 1000;
-                    if ((mLastMemCheckTime+memCheckInterval) < now) {
-                        // It is now time to collect pss information.  This
-                        // is async so we won't report it now.  And to keep
-                        // things simple, we will assume that everyone has
-                        // reported back by the next MONITOR message.
-                        mLastMemCheckTime = now;
-                        if (localLOGV) Slog.v(TAG, "Collecting memory usage.");
-                        collectMemory();
-                        mHavePss = true;
-
-                        long memCheckRealtimeInterval = Settings.Secure.getLong(
-                                mResolver, Settings.Secure.MEMCHECK_LOG_REALTIME_INTERVAL,
-                                MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL) * 1000;
-                        long realtimeNow = SystemClock.elapsedRealtime();
-                        if ((mLastMemCheckRealtime+memCheckRealtimeInterval) < realtimeNow) {
-                            mLastMemCheckRealtime = realtimeNow;
-                            if (localLOGV) Slog.v(TAG, "Collecting global memory usage.");
-                            collectGlobalMemory();
-                            mHaveGlobalPss = true;
-                        }
-                    }
-
                     final int size = mMonitors.size();
                     for (int i = 0 ; i < size ; i++) {
                         mCurrentMonitor = mMonitors.get(i);
@@ -315,20 +142,6 @@
         }
     }
 
-    final class GlobalPssCollected implements Runnable {
-        public void run() {
-            mHandler.sendEmptyMessage(GLOBAL_PSS);
-        }
-    }
-
-    final class CheckupReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context c, Intent intent) {
-            if (localLOGV) Slog.v(TAG, "Alarm went off, checking memory.");
-            checkMemory();
-        }
-    }
-
     final class RebootReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context c, Intent intent) {
@@ -359,27 +172,6 @@
         void monitor();
     }
 
-    public interface PssRequestor {
-        void requestPss();
-    }
-
-    public class PssStats {
-        public int mEmptyPss;
-        public int mEmptyCount;
-        public int mBackgroundPss;
-        public int mBackgroundCount;
-        public int mServicePss;
-        public int mServiceCount;
-        public int mVisiblePss;
-        public int mVisibleCount;
-        public int mForegroundPss;
-        public int mForegroundCount;
-
-        public int mNoPssCount;
-
-        public int mProcDeaths[] = new int[10];
-    }
-
     public static Watchdog getInstance() {
         if (sWatchdog == null) {
             sWatchdog = new Watchdog();
@@ -391,7 +183,6 @@
     private Watchdog() {
         super("watchdog");
         mHandler = new HeartbeatHandler();
-        mGlobalPssCollected = new GlobalPssCollected();
     }
 
     public void init(Context context, BatteryService battery,
@@ -403,11 +194,6 @@
         mAlarm = alarm;
         mActivity = activity;
 
-        context.registerReceiver(new CheckupReceiver(),
-                new IntentFilter(CHECKUP_ACTION));
-        mCheckupIntent = PendingIntent.getBroadcast(context,
-                0, new Intent(CHECKUP_ACTION), 0);
-
         context.registerReceiver(new RebootReceiver(),
                 new IntentFilter(REBOOT_ACTION));
         mRebootIntent = PendingIntent.getBroadcast(context,
@@ -420,20 +206,10 @@
         mBootTime = System.currentTimeMillis();
     }
 
-    public void processStarted(PssRequestor req, String name, int pid) {
+    public void processStarted(String name, int pid) {
         synchronized (this) {
             if ("com.android.phone".equals(name)) {
-                mPhoneReq = req;
                 mPhonePid = pid;
-                mPhonePss = 0;
-            }
-        }
-    }
-
-    public void reportPss(PssRequestor req, String name, int pss) {
-        synchronized (this) {
-            if (mPhoneReq == req) {
-                mPhonePss = pss;
             }
         }
     }
@@ -447,152 +223,6 @@
         }
     }
 
-    /**
-     * Retrieve memory usage information from specific processes being
-     * monitored.  This is an async operation, so must be done before doing
-     * memory checks.
-     */
-    void collectMemory() {
-        synchronized (this) {
-            if (mPhoneReq != null) {
-                mPhoneReq.requestPss();
-            }
-        }
-    }
-
-    /**
-     * Retrieve memory usage over all application processes.  This is an
-     * async operation, so must be done before doing memory checks.
-     */
-    void collectGlobalMemory() {
-        mActivity.requestPss(mGlobalPssCollected);
-    }
-
-    /**
-     * Check memory usage in the system, scheduling kills/reboots as needed.
-     * This always runs on the mHandler thread.
-     */
-    void checkMemory() {
-        boolean needScheduledCheck;
-        long curTime;
-        long nextTime = 0;
-
-        long recheckInterval = Settings.Secure.getLong(
-                mResolver, Settings.Secure.MEMCHECK_RECHECK_INTERVAL,
-                MEMCHECK_DEFAULT_RECHECK_INTERVAL) * 1000;
-
-        mSystemMemMonitor.retrieveSettings(mResolver);
-        mPhoneMemMonitor.retrieveSettings(mResolver);
-        retrieveBrutalityAmount();
-
-        synchronized (this) {
-            curTime = System.currentTimeMillis();
-            mNeedScheduledCheck = false;
-
-            // How is the system doing?
-            if (mSystemMemMonitor.checkLocked(curTime, Process.myPid(),
-                    (int)Process.getPss(Process.myPid()))) {
-                // Not good!  Time to suicide.
-                mForceKillSystem = true;
-                notifyAll();
-                return;
-            }
-
-            // How is the phone process doing?
-            if (mPhoneReq != null) {
-                if (mPhoneMemMonitor.checkLocked(curTime, mPhonePid,
-                        mPhonePss)) {
-                    // Just kill the phone process and let it restart.
-                    Slog.i(TAG, "Watchdog is killing the phone process");
-                    Process.killProcess(mPhonePid);
-                }
-            } else {
-                mPhoneMemMonitor.clear();
-            }
-
-            needScheduledCheck = mNeedScheduledCheck;
-            if (needScheduledCheck) {
-                // Something is going bad, but now is not a good time to
-                // tear things down...  schedule an alarm to check again soon.
-                nextTime = curTime + recheckInterval;
-                if (nextTime < mMemcheckExecStartTime) {
-                    nextTime = mMemcheckExecStartTime;
-                } else if (nextTime >= mMemcheckExecEndTime){
-                    // Need to check during next exec time...  so that needs
-                    // to be computed.
-                    if (localLOGV) Slog.v(TAG, "Computing next time range");
-                    computeMemcheckTimesLocked(nextTime);
-                    nextTime = mMemcheckExecStartTime;
-                }
-
-                if (localLOGV) {
-                    mCalendar.setTimeInMillis(nextTime);
-                    Slog.v(TAG, "Next Alarm Time: " + mCalendar);
-                }
-            }
-        }
-
-        if (needScheduledCheck) {
-            if (localLOGV) Slog.v(TAG, "Scheduling next memcheck alarm for "
-                    + ((nextTime-curTime)/1000/60) + "m from now");
-            mAlarm.remove(mCheckupIntent);
-            mAlarm.set(AlarmManager.RTC_WAKEUP, nextTime, mCheckupIntent);
-        } else {
-            if (localLOGV) Slog.v(TAG, "No need to schedule a memcheck alarm!");
-            mAlarm.remove(mCheckupIntent);
-        }
-    }
-
-    final PssStats mPssStats = new PssStats();
-    final String[] mMemInfoFields = new String[] {
-            "MemFree:", "Buffers:", "Cached:",
-            "Active:", "Inactive:",
-            "AnonPages:", "Mapped:", "Slab:",
-            "SReclaimable:", "SUnreclaim:", "PageTables:" };
-    final long[] mMemInfoSizes = new long[mMemInfoFields.length];
-    final String[] mVMStatFields = new String[] {
-            "pgfree ", "pgactivate ", "pgdeactivate ",
-            "pgfault ", "pgmajfault " };
-    final long[] mVMStatSizes = new long[mVMStatFields.length];
-    final long[] mPrevVMStatSizes = new long[mVMStatFields.length];
-    long mLastLogGlobalMemoryTime;
-
-    void logGlobalMemory() {
-        PssStats stats = mPssStats;
-        mActivity.collectPss(stats);
-        EventLog.writeEvent(EventLogTags.WATCHDOG_PSS_STATS,
-                stats.mEmptyPss, stats.mEmptyCount,
-                stats.mBackgroundPss, stats.mBackgroundCount,
-                stats.mServicePss, stats.mServiceCount,
-                stats.mVisiblePss, stats.mVisibleCount,
-                stats.mForegroundPss, stats.mForegroundCount,
-                stats.mNoPssCount);
-        EventLog.writeEvent(EventLogTags.WATCHDOG_PROC_STATS,
-                stats.mProcDeaths[0], stats.mProcDeaths[1], stats.mProcDeaths[2],
-                stats.mProcDeaths[3], stats.mProcDeaths[4]);
-        Process.readProcLines("/proc/meminfo", mMemInfoFields, mMemInfoSizes);
-        for (int i=0; i<mMemInfoSizes.length; i++) {
-            mMemInfoSizes[i] *= 1024;
-        }
-        EventLog.writeEvent(EventLogTags.WATCHDOG_MEMINFO,
-                (int)mMemInfoSizes[0], (int)mMemInfoSizes[1], (int)mMemInfoSizes[2],
-                (int)mMemInfoSizes[3], (int)mMemInfoSizes[4],
-                (int)mMemInfoSizes[5], (int)mMemInfoSizes[6], (int)mMemInfoSizes[7],
-                (int)mMemInfoSizes[8], (int)mMemInfoSizes[9], (int)mMemInfoSizes[10]);
-        long now = SystemClock.uptimeMillis();
-        long dur = now - mLastLogGlobalMemoryTime;
-        mLastLogGlobalMemoryTime = now;
-        Process.readProcLines("/proc/vmstat", mVMStatFields, mVMStatSizes);
-        for (int i=0; i<mVMStatSizes.length; i++) {
-            long v = mVMStatSizes[i];
-            mVMStatSizes[i] -= mPrevVMStatSizes[i];
-            mPrevVMStatSizes[i] = v;
-        }
-        EventLog.writeEvent(EventLogTags.WATCHDOG_VMSTAT, dur,
-                (int)mVMStatSizes[0], (int)mVMStatSizes[1], (int)mVMStatSizes[2],
-                (int)mVMStatSizes[3], (int)mVMStatSizes[4]);
-    }
-
     void checkReboot(boolean fromAlarm) {
         int rebootInterval = mReqRebootInterval >= 0 ? mReqRebootInterval
                 : Settings.Secure.getInt(
@@ -730,47 +360,6 @@
         return null;
     }
 
-    /**
-     * Compute the times during which we next would like to perform process
-     * restarts.
-     *
-     * @param curTime The current system time.
-     */
-    void computeMemcheckTimesLocked(long curTime) {
-        if (mMemcheckLastTime == curTime) {
-            return;
-        }
-
-        mMemcheckLastTime = curTime;
-
-        long memcheckExecStartTime = Settings.Secure.getLong(
-                mResolver, Settings.Secure.MEMCHECK_EXEC_START_TIME,
-                MEMCHECK_DEFAULT_EXEC_START_TIME);
-        long memcheckExecEndTime = Settings.Secure.getLong(
-                mResolver, Settings.Secure.MEMCHECK_EXEC_END_TIME,
-                MEMCHECK_DEFAULT_EXEC_END_TIME);
-
-        mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime,
-                memcheckExecEndTime);
-        if (mMemcheckExecEndTime < curTime) {
-            memcheckExecStartTime += 24*60*60;
-            memcheckExecEndTime += 24*60*60;
-            mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime,
-                    memcheckExecEndTime);
-        }
-        mMemcheckExecStartTime = computeCalendarTime(mCalendar, curTime,
-                memcheckExecStartTime);
-
-        if (localLOGV) {
-            mCalendar.setTimeInMillis(curTime);
-            Slog.v(TAG, "Current Time: " + mCalendar);
-            mCalendar.setTimeInMillis(mMemcheckExecStartTime);
-            Slog.v(TAG, "Start Check Time: " + mCalendar);
-            mCalendar.setTimeInMillis(mMemcheckExecEndTime);
-            Slog.v(TAG, "End Check Time: " + mCalendar);
-        }
-    }
-
     static long computeCalendarTime(Calendar c, long curTime,
             long secondsSinceMidnight) {
 
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 95ab5bc..483f9eb 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -103,7 +103,6 @@
 import android.view.IWindowSession;
 import android.view.InputChannel;
 import android.view.InputQueue;
-import android.view.InputTarget;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.RawInputEvent;
@@ -207,6 +206,9 @@
     // Maximum number of milliseconds to wait for input event injection.
     // FIXME is this value reasonable?
     private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
+    
+    // Default input dispatching timeout in nanoseconds.
+    private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
 
     static final int INJECT_FAILED = 0;
     static final int INJECT_SUCCEEDED = 1;
@@ -570,6 +572,7 @@
                     mHaveInputMethods);
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_DISPLAY);
+            android.os.Process.setCanSelfBackground(false);
 
             synchronized (this) {
                 mService = s;
@@ -605,6 +608,7 @@
             //        Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_FOREGROUND);
+            android.os.Process.setCanSelfBackground(false);
             mPolicy.init(mContext, mService, mPM);
 
             synchronized (this) {
@@ -2061,8 +2065,8 @@
 
             boolean focusChanged = false;
             if (win.canReceiveKeys()) {
-                if ((focusChanged=updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS))
-                        == true) {
+                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS);
+                if (focusChanged) {
                     imMayMove = false;
                 }
             }
@@ -2078,10 +2082,9 @@
             //dump();
 
             if (focusChanged) {
-                if (mCurrentFocus != null) {
-                    mKeyWaiter.handleNewWindowLocked(mCurrentFocus);
-                }
+                finishUpdateFocusedWindowAfterAssignLayersLocked();
             }
+            
             if (localLOGV) Slog.v(
                 TAG, "New client " + client.asBinder()
                 + ": window=" + win);
@@ -2183,10 +2186,14 @@
     }
 
     private void removeWindowInnerLocked(Session session, WindowState win) {
-        mKeyWaiter.finishedKey(session, win.mClient, true,
-                KeyWaiter.RETURN_NOTHING);
-        mKeyWaiter.releasePendingPointerLocked(win.mSession);
-        mKeyWaiter.releasePendingTrackballLocked(win.mSession);
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            mInputMonitor.windowIsBeingRemovedLw(win);
+        } else {
+            mKeyWaiter.finishedKey(session, win.mClient, true,
+                    KeyWaiter.RETURN_NOTHING);
+            mKeyWaiter.releasePendingPointerLocked(win.mSession);
+            mKeyWaiter.releasePendingTrackballLocked(win.mSession);
+        }
 
         win.mRemoved = true;
 
@@ -2554,8 +2561,12 @@
                               applyAnimationLocked(win, transit, false)) {
                             focusMayChange = true;
                             win.mExiting = true;
-                            mKeyWaiter.finishedKey(session, client, true,
-                                    KeyWaiter.RETURN_NOTHING);
+                            if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                                mInputMonitor.windowIsBecomingInvisibleLw(win);
+                            } else {
+                                mKeyWaiter.finishedKey(session, client, true,
+                                        KeyWaiter.RETURN_NOTHING);
+                            }
                         } else if (win.isAnimating()) {
                             // Currently in a hide animation... turn this into
                             // an exit.
@@ -3016,8 +3027,12 @@
                         if (win.isVisibleNow()) {
                             applyAnimationLocked(win,
                                     WindowManagerPolicy.TRANSIT_EXIT, false);
-                            mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
-                                    KeyWaiter.RETURN_NOTHING);
+                            if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                                mInputMonitor.windowIsBeingRemovedLw(win);
+                            } else {
+                                mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
+                                        KeyWaiter.RETURN_NOTHING);
+                            }
                             changed = true;
                         }
                     }
@@ -3048,6 +3063,20 @@
                 "addAppToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
+        
+        // Get the dispatching timeout here while we are not holding any locks so that it
+        // can be cached by the AppWindowToken.  The timeout value is used later by the
+        // input dispatcher in code that does hold locks.  If we did not cache the value
+        // here we would run the chance of introducing a deadlock between the window manager
+        // (which holds locks while updating the input dispatcher state) and the activity manager
+        // (which holds locks while querying the application token).
+        long inputDispatchingTimeoutNanos;
+        try {
+            inputDispatchingTimeoutNanos = token.getKeyDispatchingTimeout() * 1000000L;
+        } catch (RemoteException ex) {
+            Slog.w(TAG, "Could not get dispatching timeout.", ex);
+            inputDispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+        }
 
         synchronized(mWindowMap) {
             AppWindowToken wtoken = findAppWindowToken(token.asBinder());
@@ -3056,6 +3085,7 @@
                 return;
             }
             wtoken = new AppWindowToken(token);
+            wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
             wtoken.groupId = groupId;
             wtoken.appFullscreen = fullscreen;
             wtoken.requestedOrientation = requestedOrientation;
@@ -3319,7 +3349,13 @@
                 if (DEBUG_FOCUS) Slog.v(TAG, "Clearing focused app, was " + mFocusedApp);
                 changed = mFocusedApp != null;
                 mFocusedApp = null;
-                mKeyWaiter.tickle();
+                if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                    if (changed) {
+                        mInputMonitor.setFocusedAppLw(null);
+                    }
+                } else {
+                    mKeyWaiter.tickle();
+                }
             } else {
                 AppWindowToken newFocus = findAppWindowToken(token);
                 if (newFocus == null) {
@@ -3329,7 +3365,13 @@
                 changed = mFocusedApp != newFocus;
                 mFocusedApp = newFocus;
                 if (DEBUG_FOCUS) Slog.v(TAG, "Set focused app to: " + mFocusedApp);
-                mKeyWaiter.tickle();
+                if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                    if (changed) {
+                        mInputMonitor.setFocusedAppLw(newFocus);
+                    }
+                } else {
+                    mKeyWaiter.tickle();
+                }
             }
 
             if (moveFocusNow && changed) {
@@ -3640,8 +3682,12 @@
                         applyAnimationLocked(win,
                                 WindowManagerPolicy.TRANSIT_EXIT, false);
                     }
-                    mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
-                            KeyWaiter.RETURN_NOTHING);
+                    if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                        mInputMonitor.windowIsBecomingInvisibleLw(win);
+                    } else {
+                        mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
+                                KeyWaiter.RETURN_NOTHING);
+                    }
                     changed = true;
                 }
             }
@@ -3926,7 +3972,11 @@
                     if (DEBUG_FOCUS) Slog.v(TAG, "Removing focused app token:" + wtoken);
                     mFocusedApp = null;
                     updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
-                    mKeyWaiter.tickle();
+                    if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                        mInputMonitor.setFocusedAppLw(null);
+                    } else {
+                        mKeyWaiter.tickle();
+                    }
                 }
             } else {
                 Slog.w(TAG, "Attempted to remove non-existing app token: " + token);
@@ -5075,456 +5125,375 @@
         return true;
     }
     
-    /* Notifies the window manager about a broken input channel.
-     * 
-     * Called by the InputManager.
-     */
-    public void notifyInputChannelBroken(InputChannel inputChannel) {
-        synchronized (mWindowMap) {
-            WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
-            if (windowState == null) {
-                return; // irrelevant
-            }
-            
-            Slog.i(TAG, "WINDOW DIED " + windowState);
-            removeWindowLocked(windowState.mSession, windowState);
-        }
-    }
-    
-    /* Notifies the window manager about a broken input channel.
-     * 
-     * Called by the InputManager.
-     */
-    public long notifyInputChannelANR(InputChannel inputChannel) {
-        IApplicationToken appToken;
-        synchronized (mWindowMap) {
-            WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
-            if (windowState == null) {
-                return -2; // irrelevant, abort dispatching (-2)
-            }
-            
-            Slog.i(TAG, "Input event dispatching timed out sending to "
-                    + windowState.mAttrs.getTitle());
-            appToken = windowState.getAppToken();
-        }
-        
-        try {
-            // Notify the activity manager about the timeout and let it decide whether
-            // to abort dispatching or keep waiting.
-            boolean abort = appToken.keyDispatchingTimedOut();
-            if (abort) {
-                return -2; // abort dispatching
-            }
-            
-            // Return new timeout.
-            // We use -1 for infinite timeout to avoid clash with -2 magic number.
-            long newTimeout = appToken.getKeyDispatchingTimeout() * 1000000;
-            return newTimeout < 0 ? -1 : newTimeout;
-        } catch (RemoteException ex) {
-            return -2; // abort dispatching
-        }
-    }
-
-    /* Notifies the window manager about a broken input channel.
-     * 
-     * Called by the InputManager.
-     */
-    public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
-        // Nothing to do just now.
-        // Just wait for the user to dismiss the ANR dialog.
-        
-        // TODO We could try to automatically dismiss the ANR dialog on recovery
-        //      although that might be disorienting.
-    }
-    
-    private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) {
-        int windowCount = mWindows.size();
-        for (int i = 0; i < windowCount; i++) {
-            WindowState windowState = (WindowState) mWindows.get(i);
-            if (windowState.mInputChannel == inputChannel) {
-                return windowState;
-            }
-        }
-        
-        return null;
-    }
-
     // -------------------------------------------------------------
     // Input Events and Focus Management
     // -------------------------------------------------------------
     
-    private boolean checkInjectionPermissionTd(WindowState focus,
-            int injectorPid, int injectorUid) {
-        if (injectorUid > 0 && (focus == null || injectorUid != focus.mSession.mUid)) {
-            if (mContext.checkPermission(
-                    android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
-                    != PackageManager.PERMISSION_GRANTED) {
-                Slog.w(TAG, "Permission denied: injecting key event from pid "
-                        + injectorPid + " uid " + injectorUid + " to window " + focus
-                        + " owned by uid " + focus.mSession.mUid);
-                return false;
+    InputMonitor mInputMonitor = new InputMonitor();
+    
+    /* Tracks the progress of input dispatch and ensures that input dispatch state
+     * is kept in sync with changes in window focus, visibility, registration, and
+     * other relevant Window Manager state transitions. */
+    final class InputMonitor {
+        // Current window with input focus for keys and other non-touch events.  May be null.
+        private WindowState mInputFocus;
+        
+        // When true, prevents input dispatch from proceeding until set to false again.
+        private boolean mInputDispatchFrozen;
+        
+        // When true, input dispatch proceeds normally.  Otherwise all events are dropped.
+        private boolean mInputDispatchEnabled = true;
+
+        // Temporary list of windows information to provide to the input dispatcher.
+        private InputWindowList mTempInputWindows = new InputWindowList();
+        
+        // Temporary input application object to provide to the input dispatcher.
+        private InputApplication mTempInputApplication = new InputApplication();
+        
+        /* Notifies the window manager about a broken input channel.
+         * 
+         * Called by the InputManager.
+         */
+        public void notifyInputChannelBroken(InputChannel inputChannel) {
+            synchronized (mWindowMap) {
+                WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+                if (windowState == null) {
+                    return; // irrelevant
+                }
+                
+                Slog.i(TAG, "WINDOW DIED " + windowState);
+                removeWindowLocked(windowState.mSession, windowState);
             }
         }
-        return true;
-    }
-    
-    /* Gets the input targets for a key event.
-     * 
-     * Called by the InputManager on the InputDispatcher thread.
-     */
-    public int getKeyEventTargetsTd(InputTargetList inputTargets,
-            KeyEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) {
-        if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event);
-
-        // TODO what do we do with mDisplayFrozen?
-        // TODO what do we do with focus.mToken.paused?
         
-        WindowState focus = getFocusedWindow();
-        
-        if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) {
-            return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-        }
-        
-        if (mPolicy.interceptKeyTi(focus, event.getKeyCode(), event.getMetaState(),
-                event.getAction() == KeyEvent.ACTION_DOWN,
-                event.getRepeatCount(), event.getFlags())) {
-            // Policy consumed the event.
-            return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
-        }
-        
-        if (focus == null) {
-            return InputManager.INPUT_EVENT_INJECTION_FAILED;
-        }
-        
-        wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
-        
-        addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC);
-        return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
-    }
-    
-    /* Gets the input targets for a motion event.
-     * 
-     * Called by the InputManager on the InputDispatcher thread.
-     */
-    public int getMotionEventTargetsTd(InputTargetList inputTargets,
-            MotionEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) {
-        switch (nature) {
-            case InputQueue.INPUT_EVENT_NATURE_TRACKBALL:
-                return getMotionEventTargetsForTrackballTd(inputTargets, event, policyFlags,
-                        injectorPid, injectorUid);
+        /* Notifies the window manager about an input channel that is not responding.
+         * The method can either cause dispatching to be aborted by returning -2 or
+         * return a new timeout in nanoseconds.
+         * 
+         * Called by the InputManager.
+         */
+        public long notifyInputChannelANR(InputChannel inputChannel) {
+            AppWindowToken token;
+            synchronized (mWindowMap) {
+                WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+                if (windowState == null) {
+                    return -2; // irrelevant, abort dispatching (-2)
+                }
                 
-            case InputQueue.INPUT_EVENT_NATURE_TOUCH:
-                return getMotionEventTargetsForTouchTd(inputTargets, event, policyFlags,
-                        injectorPid, injectorUid);
-                
-            default:
-                return InputManager.INPUT_EVENT_INJECTION_FAILED;
-        }
-    }
-    
-    /* Gets the input targets for a trackball event.
-     * 
-     * Called by the InputManager on the InputDispatcher thread.
-     */
-    private int getMotionEventTargetsForTrackballTd(InputTargetList inputTargets,
-            MotionEvent event, int policyFlags, int injectorPid, int injectorUid) {
-        WindowState focus = getFocusedWindow();
-        
-        if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) {
-            return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-        }
-        
-        if (focus == null) {
-            return InputManager.INPUT_EVENT_INJECTION_FAILED;
-        }
-        
-        wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
-        
-        addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC);
-        return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
-    }
-    
-    /* Set to true when a fat touch has been detected during the processing of a touch event.
-     * 
-     * Only used by getMotionEventTargetsForTouchTd.
-     * Set to true whenever a fat touch is detected and reset to false on ACTION_UP.
-     */
-    private boolean mFatTouch;
-    
-    /* Set to true when we think the touch event.
-     * 
-     * Only used by getMotionEventTargetsForTouchTd.
-     * Set to true on ACTION_DOWN and set to false on ACTION_UP.
-     */
-    private boolean mTouchDown;
-    
-    /* Current target of Motion events.
-     * 
-     * Only used by getMotionEventTargetsForTouchTd.
-     * Initialized on ACTION_DOWN and cleared on ACTION_UP.
-     */
-    private WindowState mTouchFocus;
-
-    /* Windows above the target that would like to receive an "outside" touch event
-     * for any down events outside of them.
-     * 
-     * Only used by getMotionEventTargetsForTouchTd.
-     * Initialized on ACTION_DOWN and cleared immediately afterwards.
-     */
-    private ArrayList<WindowState> mOutsideTouchTargets = new ArrayList<WindowState>();
-    
-    /* Wallpaper windows that are currently receiving touch events.
-     * 
-     * Only used by getMotionEventTargetsForTouchTd.
-     * Initialized on ACTION_DOWN and cleared on ACTION_UP.
-     */
-    private ArrayList<WindowState> mWallpaperTouchTargets = new ArrayList<WindowState>();
-    
-    /* Gets the input targets for a touch event.
-     * 
-     * Called by the InputManager on the InputDispatcher thread.
-     */
-    private int getMotionEventTargetsForTouchTd(InputTargetList inputTargets,
-            MotionEvent event, int policyFlags, int injectorPid, int injectorUid) {
-        final int action = event.getAction();
-        
-        if (action == MotionEvent.ACTION_DOWN) {
-            updateTouchFocusBeforeDownTd(event, policyFlags);
-        } else {
-            updateTouchFocusBeforeNonDownTd(event, policyFlags);
-        }
-
-        boolean skipDelivery = false;
-        int touchTargetFlags = 0;
-        
-        int injectionResult = InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
-        WindowState focusedTouchTarget = mTouchFocus;
-        if (focusedTouchTarget == null) {
-            // In this case we are either dropping the event, or have received
-            // a move or up without a down.  It is common to receive move
-            // events in such a way, since this means the user is moving the
-            // pointer without actually pressing down.  All other cases should
-            // be atypical, so let's log them.
-            if (action != MotionEvent.ACTION_MOVE) {
-                Slog.w(TAG, "No window to dispatch pointer action " + action);
-                injectionResult = InputManager.INPUT_EVENT_INJECTION_FAILED;
-            }
-        } else {
-            // We have a valid focused touch target.
-            if (! checkInjectionPermissionTd(focusedTouchTarget, injectorPid, injectorUid)) {
-                return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+                Slog.i(TAG, "Input event dispatching timed out sending to "
+                        + windowState.mAttrs.getTitle());
+                token = windowState.mAppToken;
             }
             
-            wakeupIfNeeded(focusedTouchTarget, eventType(event));
-            
-            if ((focusedTouchTarget.mAttrs.flags &
-                    WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
-                // Target wants to ignore fat touch events
-                boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event);
-                
-                if (cheekPress) {
-                    if ((action == MotionEvent.ACTION_DOWN)) {
-                        mFatTouch = true;
-                        skipDelivery = true;
-                    } else {
-                        if (! mFatTouch) {
-                            // cancel the earlier event
-                            touchTargetFlags |= InputTarget.FLAG_CANCEL;
-                            mFatTouch = true;
-                        } else {
-                            skipDelivery = true;
-                        }
+            return notifyANRInternal(token);
+        }
+    
+        /* Notifies the window manager about an input channel spontaneously recovering from ANR
+         * by successfully delivering the event that originally timed out.
+         * 
+         * Called by the InputManager.
+         */
+        public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
+            // Nothing to do just now.
+            // Just wait for the user to dismiss the ANR dialog.
+        }
+        
+        /* Notifies the window manager about an application that is not responding
+         * in general rather than with respect to a particular input channel.
+         * The method can either cause dispatching to be aborted by returning -2 or
+         * return a new timeout in nanoseconds.
+         * 
+         * Called by the InputManager.
+         */
+        public long notifyANR(Object token) {
+            AppWindowToken appWindowToken = (AppWindowToken) token;
+
+            Slog.i(TAG, "Input event dispatching timed out sending to application "
+                    + appWindowToken.stringName);
+            return notifyANRInternal(appWindowToken);
+        }
+        
+        private long notifyANRInternal(AppWindowToken token) {
+            if (token != null && token.appToken != null) {
+                try {
+                    // Notify the activity manager about the timeout and let it decide whether
+                    // to abort dispatching or keep waiting.
+                    boolean abort = token.appToken.keyDispatchingTimedOut();
+                    if (! abort) {
+                        // The activity manager declined to abort dispatching.
+                        // Wait a bit longer and timeout again later.
+                        return token.inputDispatchingTimeoutNanos;
                     }
+                } catch (RemoteException ex) {
                 }
             }
+            return -2; // abort dispatching
         }
         
-        if (! skipDelivery) {
-            int outsideTargetCount = mOutsideTouchTargets.size();
-            for (int i = 0; i < outsideTargetCount; i++) {
-                WindowState outsideTouchTarget = mOutsideTouchTargets.get(i);
-                addInputTargetTd(inputTargets, outsideTouchTarget,
-                        InputTarget.FLAG_OUTSIDE | touchTargetFlags);
+        private WindowState getWindowStateForInputChannel(InputChannel inputChannel) {
+            synchronized (mWindowMap) {
+                return getWindowStateForInputChannelLocked(inputChannel);
+            }
+        }
+        
+        private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) {
+            int windowCount = mWindows.size();
+            for (int i = 0; i < windowCount; i++) {
+                WindowState windowState = (WindowState) mWindows.get(i);
+                if (windowState.mInputChannel == inputChannel) {
+                    return windowState;
+                }
             }
             
-            int wallpaperTargetCount = mWallpaperTouchTargets.size();
-            for (int i = 0; i < wallpaperTargetCount; i++) {
-                WindowState wallpaperTouchTarget = mWallpaperTouchTargets.get(i);
-                addInputTargetTd(inputTargets, wallpaperTouchTarget,
-                        touchTargetFlags);
-            }
-            
-            if (focusedTouchTarget != null) {
-                addInputTargetTd(inputTargets, focusedTouchTarget,
-                        InputTarget.FLAG_SYNC | touchTargetFlags);
-            }
-        }
-
-        if (action == MotionEvent.ACTION_UP) {
-            updateTouchFocusAfterUpTd(event, policyFlags);
+            return null;
         }
         
-        return injectionResult;
-    }
-    
-    private void updateTouchFocusBeforeDownTd(MotionEvent event, int policyFlags) {
-        if (mTouchDown) {
-            // This is weird, we got a down, but we thought it was already down!
-            // XXX: We should probably send an ACTION_UP to the current target.
-            Slog.w(TAG, "Pointer down received while already down in: " + mTouchFocus);
-            updateTouchFocusAfterUpTd(event, policyFlags);
-        }
-        
-        mTouchDown = true;
-        mPowerManager.logPointerDownEvent();
-        
-        final boolean screenWasOff = (policyFlags & WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
-        synchronized (mWindowMap) {
-            final int x = (int) event.getX();
-            final int y = (int) event.getY();
-
+        /* Updates the cached window information provided to the input dispatcher. */
+        public void updateInputWindowsLw() {
+            // Populate the input window list with information about all of the windows that
+            // could potentially receive input.
+            // As an optimization, we could try to prune the list of windows but this turns
+            // out to be difficult because only the native code knows for sure which window
+            // currently has touch focus.
             final ArrayList windows = mWindows;
             final int N = windows.size();
-            WindowState topErrWindow = null;
-            final Rect tmpRect = mTempRect;
-            for (int i= N - 1; i >= 0; i--) {
-                WindowState child = (WindowState) windows.get(i);
-                //Slog.i(TAG, "Checking dispatch to: " + child);
-                
-                final int flags = child.mAttrs.flags;
-                if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
-                    if (topErrWindow == null) {
-                        topErrWindow = child;
-                    }
-                }
-                
-                if (!child.isVisibleLw()) {
-                    //Slog.i(TAG, "Not visible!");
+            for (int i = N - 1; i >= 0; i--) {
+                final WindowState child = (WindowState) windows.get(i);
+                if (child.mInputChannel == null) {
+                    // Skip this window because it cannot possibly receive input.
                     continue;
                 }
                 
-                if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) {
-                    tmpRect.set(child.mFrame);
-                    if (child.mTouchableInsets == ViewTreeObserver
-                                .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
-                        // The touch is inside of the window if it is
-                        // inside the frame, AND the content part of that
-                        // frame that was given by the application.
-                        tmpRect.left += child.mGivenContentInsets.left;
-                        tmpRect.top += child.mGivenContentInsets.top;
-                        tmpRect.right -= child.mGivenContentInsets.right;
-                        tmpRect.bottom -= child.mGivenContentInsets.bottom;
-                    } else if (child.mTouchableInsets == ViewTreeObserver
-                                .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
-                        // The touch is inside of the window if it is
-                        // inside the frame, AND the visible part of that
-                        // frame that was given by the application.
-                        tmpRect.left += child.mGivenVisibleInsets.left;
-                        tmpRect.top += child.mGivenVisibleInsets.top;
-                        tmpRect.right -= child.mGivenVisibleInsets.right;
-                        tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
+                final int flags = child.mAttrs.flags;
+                final int type = child.mAttrs.type;
+                
+                final boolean hasFocus = (child == mInputFocus);
+                final boolean isVisible = child.isVisibleLw();
+                final boolean hasWallpaper = (child == mWallpaperTarget)
+                        && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
+                
+                // Add a window to our list of input windows.
+                final InputWindow inputWindow = mTempInputWindows.add();
+                inputWindow.inputChannel = child.mInputChannel;
+                inputWindow.layoutParamsFlags = flags;
+                inputWindow.layoutParamsType = type;
+                inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
+                inputWindow.visible = isVisible;
+                inputWindow.hasFocus = hasFocus;
+                inputWindow.hasWallpaper = hasWallpaper;
+                inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false;
+                inputWindow.ownerPid = child.mSession.mPid;
+                inputWindow.ownerUid = child.mSession.mUid;
+                
+                final Rect frame = child.mFrame;
+                inputWindow.frameLeft = frame.left;
+                inputWindow.frameTop = frame.top;
+                
+                switch (child.mTouchableInsets) {
+                    default:
+                    case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+                        inputWindow.touchableAreaLeft = frame.left;
+                        inputWindow.touchableAreaTop = frame.top;
+                        inputWindow.touchableAreaRight = frame.right;
+                        inputWindow.touchableAreaBottom = frame.bottom;
+                        break;
+                        
+                    case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
+                        Rect inset = child.mGivenContentInsets;
+                        inputWindow.touchableAreaLeft = frame.left + inset.left;
+                        inputWindow.touchableAreaTop = frame.top + inset.top;
+                        inputWindow.touchableAreaRight = frame.right - inset.right;
+                        inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
+                        break;
                     }
-                    final int touchFlags = flags &
-                        (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
-                    if (tmpRect.contains(x, y) || touchFlags == 0) {
-                        //Slog.i(TAG, "Using this target!");
-                        if (! screenWasOff || (flags &
-                                WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
-                            mTouchFocus = child;
-                        } else {
-                            //Slog.i(TAG, "Waking, skip!");
-                            mTouchFocus = null;
-                        }
+                        
+                    case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
+                        Rect inset = child.mGivenVisibleInsets;
+                        inputWindow.touchableAreaLeft = frame.left + inset.left;
+                        inputWindow.touchableAreaTop = frame.top + inset.top;
+                        inputWindow.touchableAreaRight = frame.right - inset.right;
+                        inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
                         break;
                     }
                 }
-
-                if ((flags & WindowManager.LayoutParams
-                        .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
-                    //Slog.i(TAG, "Adding to outside target list: " + child);
-                    mOutsideTouchTargets.add(child);
-                }
             }
 
-            // If there's an error window but it's not accepting focus (typically because
-            // it is not yet visible) just wait for it -- any other focused window may in fact
-            // be in ANR state.
-            if (topErrWindow != null && mTouchFocus != topErrWindow) {
-                mTouchFocus = null;
-            }
+            // Send windows to native code.
+            mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray());
             
-            // Drop the touch focus if the window is not visible.
-            if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) {
-                mTouchFocus = null;
-            }
+            // Clear the list in preparation for the next round.
+            // Also avoids keeping InputChannel objects referenced unnecessarily.
+            mTempInputWindows.clear();
+        }
+        
+        /* Notifies that an app switch key (BACK / HOME) has just been pressed.
+         * This essentially starts a .5 second timeout for the application to process
+         * subsequent input events while waiting for the app switch to occur.  If it takes longer
+         * than this, the pending events will be dropped.
+         */
+        public void notifyAppSwitchComing() {
+            // TODO Not implemented yet.  Should go in the native side.
+        }
+
+        /* Provides an opportunity for the window manager policy to intercept early key
+         * processing as soon as the key has been read from the device. */
+        public int interceptKeyBeforeQueueing(int deviceId, int type, int scanCode,
+                int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) {
+            RawInputEvent event = new RawInputEvent();
+            event.deviceId = deviceId;
+            event.type = type;
+            event.scancode = scanCode;
+            event.keycode = keyCode;
+            event.flags = policyFlags;
+            event.value = value;
+            event.when = whenNanos / 1000000;
             
-            // Determine wallpaper targets.
-            if (mTouchFocus != null
-                    && mTouchFocus == mWallpaperTarget
-                    && mTouchFocus.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
-                int curTokenIndex = mWallpaperTokens.size();
-                while (curTokenIndex > 0) {
-                    curTokenIndex--;
-                    WindowToken token = mWallpaperTokens.get(curTokenIndex);
-                    
-                    int curWallpaperIndex = token.windows.size();
-                    while (curWallpaperIndex > 0) {
-                        curWallpaperIndex--;
-                        WindowState wallpaper = token.windows.get(curWallpaperIndex);
-                        if ((wallpaper.mAttrs.flags &
-                                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) {
-                            mWallpaperTouchTargets.add(wallpaper);
+            return mPolicy.interceptKeyTq(event, isScreenOn);
+        }
+        
+        /* Provides an opportunity for the window manager policy to process a key before
+         * ordinary dispatch. */
+        public boolean interceptKeyBeforeDispatching(InputChannel focus, int keyCode,
+                int metaState, boolean down, int repeatCount, int policyFlags) {
+            WindowState windowState = getWindowStateForInputChannel(focus);
+            return mPolicy.interceptKeyTi(windowState, keyCode, metaState, down, repeatCount,
+                    policyFlags);
+        }
+        
+        /* Called when the current input focus changes.
+         * Layer assignment is assumed to be complete by the time this is called.
+         */
+        public void setInputFocusLw(WindowState newWindow) {
+            if (DEBUG_INPUT) {
+                Slog.d(TAG, "Input focus has changed to " + newWindow);
+            }
+
+            if (newWindow != mInputFocus) {
+                if (newWindow != null && newWindow.canReceiveKeys()) {
+                    // If the new input focus is an error window or appears above the current
+                    // input focus, preempt any pending synchronous dispatch so that we can
+                    // start delivering events to the new input focus as soon as possible.
+                    if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
+                        if (DEBUG_INPUT) {
+                            Slog.v(TAG, "New SYSTEM_ERROR window; resetting state");
                         }
+                        preemptInputDispatchLw();
+                    } else if (mInputFocus != null && newWindow.mLayer > mInputFocus.mLayer) {
+                        if (DEBUG_INPUT) {
+                            Slog.v(TAG, "Transferring focus to new window at higher layer: "
+                                    + "old win layer=" + mInputFocus.mLayer
+                                    + ", new win layer=" + newWindow.mLayer);
+                        }
+                        preemptInputDispatchLw();
                     }
+                    
+                    // Displaying a window implicitly causes dispatching to be unpaused.
+                    // This is to protect against bugs if someone pauses dispatching but
+                    // forgets to resume.
+                    newWindow.mToken.paused = false;
                 }
-            }
-        }
-    }
-
-    private void updateTouchFocusBeforeNonDownTd(MotionEvent event, int policyFlags) {
-        synchronized (mWindowMap) {
-            // Drop the touch focus if the window is not visible.
-            if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) {
-                mTouchFocus = null;
-                mWallpaperTouchTargets.clear();
-            }
-        }
-    }
-    
-    private void updateTouchFocusAfterUpTd(MotionEvent event, int policyFlags) {
-        mFatTouch = false;
-        mTouchDown = false;
-        mTouchFocus = null;
-        mOutsideTouchTargets.clear();
-        mWallpaperTouchTargets.clear();
-        
-        mPowerManager.logPointerUpEvent();
-    }
-
-    /* Adds a window to a list of input targets.
-     * Do NOT call this method while holding any locks because the call to
-     * appToken.getKeyDispatchingTimeout() can potentially call into the ActivityManager
-     * and create a deadlock hazard.
-     */
-    private void addInputTargetTd(InputTargetList inputTargets, WindowState window, int flags) {
-        if (window.mInputChannel == null) {
-            return;
-        }
-        
-        long timeoutNanos = -1;
-        IApplicationToken appToken = window.getAppToken();
-
-        if (appToken != null) {
-            try {
-                timeoutNanos = appToken.getKeyDispatchingTimeout() * 1000000;
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Could not get key dispatching timeout.", ex);
+            
+                mInputFocus = newWindow;
+                updateInputWindowsLw();
             }
         }
         
-        inputTargets.add(window.mInputChannel, flags, timeoutNanos,
-                - window.mFrame.left, - window.mFrame.top);
+        public void windowIsBecomingInvisibleLw(WindowState window) {
+            // The window is becoming invisible.  Preempt input dispatch in progress
+            // so that the next window below can receive focus.
+            if (window == mInputFocus) {
+                mInputFocus = null;
+                preemptInputDispatchLw();
+            }
+            
+            updateInputWindowsLw();
+        }
+        
+        /* Tells the dispatcher to stop waiting for its current synchronous event targets.
+         * Essentially, just makes those dispatches asynchronous so a new dispatch cycle
+         * can begin.
+         */
+        private void preemptInputDispatchLw() {
+            mInputManager.preemptInputDispatch();
+        }
+        
+        public void setFocusedAppLw(AppWindowToken newApp) {
+            // Focused app has changed.
+            if (newApp == null) {
+                mInputManager.setFocusedApplication(null);
+            } else {
+                mTempInputApplication.name = newApp.toString();
+                mTempInputApplication.dispatchingTimeoutNanos =
+                        newApp.inputDispatchingTimeoutNanos;
+                mTempInputApplication.token = newApp;
+                
+                mInputManager.setFocusedApplication(mTempInputApplication);
+            }
+        }
+        
+        public void windowIsBeingRemovedLw(WindowState window) {
+            // Window is being removed.
+            updateInputWindowsLw();
+        }
+        
+        public void pauseDispatchingLw(WindowToken window) {
+            if (! window.paused) {
+                if (DEBUG_INPUT) {
+                    Slog.v(TAG, "Pausing WindowToken " + window);
+                }
+                
+                window.paused = true;
+                updateInputWindowsLw();
+            }
+        }
+        
+        public void resumeDispatchingLw(WindowToken window) {
+            if (window.paused) {
+                if (DEBUG_INPUT) {
+                    Slog.v(TAG, "Resuming WindowToken " + window);
+                }
+                
+                window.paused = false;
+                updateInputWindowsLw();
+            }
+        }
+        
+        public void freezeInputDispatchingLw() {
+            if (! mInputDispatchFrozen) {
+                if (DEBUG_INPUT) {
+                    Slog.v(TAG, "Freezing input dispatching");
+                }
+                
+                mInputDispatchFrozen = true;
+                updateInputDispatchModeLw();
+            }
+        }
+        
+        public void thawInputDispatchingLw() {
+            if (mInputDispatchFrozen) {
+                if (DEBUG_INPUT) {
+                    Slog.v(TAG, "Thawing input dispatching");
+                }
+                
+                mInputDispatchFrozen = false;
+                updateInputDispatchModeLw();
+            }
+        }
+        
+        public void setEventDispatchingLw(boolean enabled) {
+            if (mInputDispatchEnabled != enabled) {
+                if (DEBUG_INPUT) {
+                    Slog.v(TAG, "Setting event dispatching to " + enabled);
+                }
+                
+                mInputDispatchEnabled = enabled;
+                updateInputDispatchModeLw();
+            }
+        }
+        
+        private void updateInputDispatchModeLw() {
+            mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
+        }
     }
 
     private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
@@ -5582,6 +5551,8 @@
             return OTHER_EVENT;
         }
     }
+    
+    private boolean mFatTouch; // remove me together with dispatchPointer
 
     /**
      * @return Returns true if event was dispatched, false if it was dropped for any reason
@@ -5978,7 +5949,11 @@
         synchronized (mWindowMap) {
             WindowToken token = mTokenMap.get(_token);
             if (token != null) {
-                mKeyWaiter.pauseDispatchingLocked(token);
+                if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                    mInputMonitor.pauseDispatchingLw(token);
+                } else {
+                    mKeyWaiter.pauseDispatchingLocked(token);
+                }
             }
         }
     }
@@ -5992,7 +5967,11 @@
         synchronized (mWindowMap) {
             WindowToken token = mTokenMap.get(_token);
             if (token != null) {
-                mKeyWaiter.resumeDispatchingLocked(token);
+                if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                    mInputMonitor.resumeDispatchingLw(token);
+                } else {
+                    mKeyWaiter.resumeDispatchingLocked(token);
+                }
             }
         }
     }
@@ -6004,7 +5983,11 @@
         }
 
         synchronized (mWindowMap) {
-            mKeyWaiter.setEventDispatchingLocked(enabled);
+            if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                mInputMonitor.setEventDispatchingLw(enabled);
+            } else {
+                mKeyWaiter.setEventDispatchingLocked(enabled);
+            }
         }
     }
 
@@ -7383,6 +7366,9 @@
         public void finishKey(IWindow window) {
             if (localLOGV) Slog.v(
                 TAG, "IWindow finishKey called for " + window);
+            if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                throw new IllegalStateException("Should not be called anymore.");
+            }
             mKeyWaiter.finishedKey(this, window, false,
                     KeyWaiter.RETURN_NOTHING);
         }
@@ -7390,6 +7376,9 @@
         public MotionEvent getPendingPointerMove(IWindow window) {
             if (localLOGV) Slog.v(
                     TAG, "IWindow getPendingMotionEvent called for " + window);
+            if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                throw new IllegalStateException("Should not be called anymore.");
+            }
             return mKeyWaiter.finishedKey(this, window, false,
                     KeyWaiter.RETURN_PENDING_POINTER);
         }
@@ -7397,6 +7386,9 @@
         public MotionEvent getPendingTrackballMove(IWindow window) {
             if (localLOGV) Slog.v(
                     TAG, "IWindow getPendingMotionEvent called for " + window);
+            if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                throw new IllegalStateException("Should not be called anymore.");
+            }
             return mKeyWaiter.finishedKey(this, window, false,
                     KeyWaiter.RETURN_PENDING_TRACKBALL);
         }
@@ -7943,6 +7935,12 @@
         public IApplicationToken getAppToken() {
             return mAppToken != null ? mAppToken.appToken : null;
         }
+        
+        public long getInputDispatchingTimeoutNanos() {
+            return mAppToken != null
+                    ? mAppToken.inputDispatchingTimeoutNanos
+                    : DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+        }
 
         public boolean hasAppShownWindows() {
             return mAppToken != null ? mAppToken.firstWindowDrawn : false;
@@ -8079,10 +8077,12 @@
             // Window is no longer on-screen, so can no longer receive
             // key events...  if we were waiting for it to finish
             // handling a key event, the wait is over!
-            mKeyWaiter.finishedKey(mSession, mClient, true,
-                    KeyWaiter.RETURN_NOTHING);
-            mKeyWaiter.releasePendingPointerLocked(mSession);
-            mKeyWaiter.releasePendingTrackballLocked(mSession);
+            if (! ENABLE_NATIVE_INPUT_DISPATCH) {
+                mKeyWaiter.finishedKey(mSession, mClient, true,
+                        KeyWaiter.RETURN_NOTHING);
+                mKeyWaiter.releasePendingPointerLocked(mSession);
+                mKeyWaiter.releasePendingTrackballLocked(mSession);
+            }
 
             if (mAppToken != null && this == mAppToken.startingWindow) {
                 mAppToken.startingDisplayed = false;
@@ -8098,6 +8098,10 @@
                     i--;
                     WindowState c = (WindowState)mChildWindows.get(i);
                     c.mAttachedHidden = true;
+                    
+                    if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                        mInputMonitor.windowIsBecomingInvisibleLw(c);
+                    }
                 }
 
                 if (mReportDestroySurface) {
@@ -8405,7 +8409,14 @@
                     Slog.w(TAG, "Error hiding surface in " + this, e);
                 }
                 mLastHidden = true;
-                mKeyWaiter.releasePendingPointerLocked(mSession);
+                
+                if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                    for (int i=0; i<N; i++) {
+                        mInputMonitor.windowIsBecomingInvisibleLw((WindowState)mChildWindows.get(i));
+                    }
+                } else {
+                    mKeyWaiter.releasePendingPointerLocked(mSession);
+                }
             }
             mExiting = false;
             if (mRemoveOnExit) {
@@ -9098,6 +9109,9 @@
         int groupId = -1;
         boolean appFullscreen;
         int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        
+        // The input dispatching timeout for this application token in nanoseconds.
+        long inputDispatchingTimeoutNanos;
 
         // These are used for determining when all windows associated with
         // an activity have been drawn, so they can be made visible together
@@ -10158,6 +10172,11 @@
                 }
             }
         }
+        
+        // Window frames may have changed.  Tell the input dispatcher about it.
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            mInputMonitor.updateInputWindowsLw();
+        }
 
         return mPolicy.finishLayoutLw();
     }
@@ -10958,7 +10977,11 @@
                                     Slog.w(TAG, "Exception hiding surface in " + w);
                                 }
                             }
-                            mKeyWaiter.releasePendingPointerLocked(w.mSession);
+                            if (ENABLE_NATIVE_INPUT_DISPATCH) {
+                                mInputMonitor.windowIsBecomingInvisibleLw(w);
+                            } else {
+                                mKeyWaiter.releasePendingPointerLocked(w.mSession);
+                            }
                         }
                         // If we are waiting for this window to handle an
                         // orientation change, well, it is hidden, so
@@ -11548,14 +11571,26 @@
                     assignLayersLocked();
                 }
             }
-
-            if (newFocus != null && mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
-                mKeyWaiter.handleNewWindowLocked(newFocus);
+            
+            if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
+                // If we defer assigning layers, then the caller is responsible for
+                // doing this part.
+                finishUpdateFocusedWindowAfterAssignLayersLocked();
             }
             return true;
         }
         return false;
     }
+    
+    private void finishUpdateFocusedWindowAfterAssignLayersLocked() {
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            mInputMonitor.setInputFocusLw(mCurrentFocus);
+        } else {
+            if (mCurrentFocus != null) {
+                mKeyWaiter.handleNewWindowLocked(mCurrentFocus);
+            }
+        }
+    }
 
     private WindowState computeFocusedWindowLocked() {
         WindowState result = null;
@@ -11634,8 +11669,10 @@
             // still frozen, the events will continue to be blocked while the
             // successive orientation change is processed.  To prevent spurious
             // ANRs, we reset the event dispatch timeout in this case.
-            synchronized (mKeyWaiter) {
-                mKeyWaiter.mWasFrozen = true;
+            if (! ENABLE_NATIVE_INPUT_DISPATCH) {
+                synchronized (mKeyWaiter) {
+                    mKeyWaiter.mWasFrozen = true;
+                }
             }
             return;
         }
@@ -11658,6 +11695,11 @@
         if (DEBUG_FREEZE) Slog.v(TAG, "*** FREEZING DISPLAY", new RuntimeException());
         
         mDisplayFrozen = true;
+        
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            mInputMonitor.freezeInputDispatchingLw();
+        }
+        
         if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
             mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
             mNextAppTransitionPackage = null;
@@ -11691,9 +11733,13 @@
 
         // Reset the key delivery timeout on unfreeze, too.  We force a wakeup here
         // too because regular key delivery processing should resume immediately.
-        synchronized (mKeyWaiter) {
-            mKeyWaiter.mWasFrozen = true;
-            mKeyWaiter.notifyAll();
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            mInputMonitor.thawInputDispatchingLw();
+        } else {
+            synchronized (mKeyWaiter) {
+                mKeyWaiter.mWasFrozen = true;
+                mKeyWaiter.notifyAll();
+            }
         }
 
         // While the display is frozen we don't re-compute the orientation
@@ -11949,20 +11995,25 @@
             }
             pw.print("  DisplayWidth="); pw.print(mDisplay.getWidth());
                     pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight());
-            pw.println("  KeyWaiter state:");
-            pw.print("    mLastWin="); pw.print(mKeyWaiter.mLastWin);
-                    pw.print(" mLastBinder="); pw.println(mKeyWaiter.mLastBinder);
-            pw.print("    mFinished="); pw.print(mKeyWaiter.mFinished);
-                    pw.print(" mGotFirstWindow="); pw.print(mKeyWaiter.mGotFirstWindow);
-                    pw.print(" mEventDispatching="); pw.print(mKeyWaiter.mEventDispatching);
-                    pw.print(" mTimeToSwitch="); pw.println(mKeyWaiter.mTimeToSwitch);
+
+            if (! ENABLE_NATIVE_INPUT_DISPATCH) {
+                pw.println("  KeyWaiter state:");
+                pw.print("    mLastWin="); pw.print(mKeyWaiter.mLastWin);
+                        pw.print(" mLastBinder="); pw.println(mKeyWaiter.mLastBinder);
+                pw.print("    mFinished="); pw.print(mKeyWaiter.mFinished);
+                        pw.print(" mGotFirstWindow="); pw.print(mKeyWaiter.mGotFirstWindow);
+                        pw.print(" mEventDispatching="); pw.print(mKeyWaiter.mEventDispatching);
+                        pw.print(" mTimeToSwitch="); pw.println(mKeyWaiter.mTimeToSwitch);
+            }
         }
     }
 
+    // Called by the heartbeat to ensure locks are not held indefnitely (for deadlock detection).
     public void monitor() {
         synchronized (mWindowMap) { }
         synchronized (mKeyguardTokenWatcher) { }
         synchronized (mKeyWaiter) { }
+        synchronized (mInputMonitor) { }
     }
 
     public void virtualKeyFeedback(KeyEvent event) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2c6806b..93122c4 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -880,7 +880,6 @@
             if (localLOGV) Slog.v(
                 TAG, "Death received in " + this
                 + " for thread " + mAppThread.asBinder());
-            removeRequestedPss(mApp);
             synchronized(ActivityManagerService.this) {
                 appDiedLocked(mApp, mPid, mAppThread);
             }
@@ -1260,6 +1259,7 @@
 
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_FOREGROUND);
+            android.os.Process.setCanSelfBackground(false);
 
             ActivityManagerService m = new ActivityManagerService();
 
@@ -1785,7 +1785,7 @@
                     hostingNameStr != null ? hostingNameStr : "");
             
             if (app.persistent) {
-                Watchdog.getInstance().processStarted(app, app.processName, pid);
+                Watchdog.getInstance().processStarted(app.processName, pid);
             }
             
             StringBuilder buf = mStringBuilder;
@@ -5662,123 +5662,6 @@
         return killed;
     }
     
-    public void reportPss(IApplicationThread caller, int pss) {
-        Watchdog.PssRequestor req;
-        String name;
-        ProcessRecord callerApp;
-        synchronized (this) {
-            if (caller == null) {
-                return;
-            }
-            callerApp = getRecordForAppLocked(caller);
-            if (callerApp == null) {
-                return;
-            }
-            callerApp.lastPss = pss;
-            req = callerApp;
-            name = callerApp.processName;
-        }
-        Watchdog.getInstance().reportPss(req, name, pss);
-        if (!callerApp.persistent) {
-            removeRequestedPss(callerApp);
-        }
-    }
-    
-    public void requestPss(Runnable completeCallback) {
-        ArrayList<ProcessRecord> procs;
-        synchronized (this) {
-            mRequestPssCallback = completeCallback;
-            mRequestPssList.clear();
-            for (int i=mLruProcesses.size()-1; i>=0; i--) {
-                ProcessRecord proc = mLruProcesses.get(i);
-                if (!proc.persistent) {
-                    mRequestPssList.add(proc);
-                }
-            }
-            procs = new ArrayList<ProcessRecord>(mRequestPssList);
-        }
-        
-        int oldPri = Process.getThreadPriority(Process.myTid()); 
-        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-        for (int i=procs.size()-1; i>=0; i--) {
-            ProcessRecord proc = procs.get(i);
-            proc.lastPss = 0;
-            proc.requestPss();
-        }
-        Process.setThreadPriority(oldPri);
-    }
-    
-    void removeRequestedPss(ProcessRecord proc) {
-        Runnable callback = null;
-        synchronized (this) {
-            if (mRequestPssList.remove(proc)) {
-                if (mRequestPssList.size() == 0) {
-                    callback = mRequestPssCallback;
-                    mRequestPssCallback = null;
-                }
-            }
-        }
-        
-        if (callback != null) {
-            callback.run();
-        }
-    }
-    
-    public void collectPss(Watchdog.PssStats stats) {
-        stats.mEmptyPss = 0;
-        stats.mEmptyCount = 0;
-        stats.mBackgroundPss = 0;
-        stats.mBackgroundCount = 0;
-        stats.mServicePss = 0;
-        stats.mServiceCount = 0;
-        stats.mVisiblePss = 0;
-        stats.mVisibleCount = 0;
-        stats.mForegroundPss = 0;
-        stats.mForegroundCount = 0;
-        stats.mNoPssCount = 0;
-        synchronized (this) {
-            int i;
-            int NPD = mProcDeaths.length < stats.mProcDeaths.length
-                    ? mProcDeaths.length : stats.mProcDeaths.length;
-            int aggr = 0;
-            for (i=0; i<NPD; i++) {
-                aggr += mProcDeaths[i];
-                stats.mProcDeaths[i] = aggr;
-            }
-            while (i<stats.mProcDeaths.length) {
-                stats.mProcDeaths[i] = 0;
-                i++;
-            }
-            
-            for (i=mLruProcesses.size()-1; i>=0; i--) {
-                ProcessRecord proc = mLruProcesses.get(i);
-                if (proc.persistent) {
-                    continue;
-                }
-                //Slog.i(TAG, "Proc " + proc + ": pss=" + proc.lastPss);
-                if (proc.lastPss == 0) {
-                    stats.mNoPssCount++;
-                    continue;
-                }
-                if (proc.setAdj >= HIDDEN_APP_MIN_ADJ) {
-                    if (proc.empty) {
-                        stats.mEmptyPss += proc.lastPss;
-                        stats.mEmptyCount++;
-                    } else {
-                        stats.mBackgroundPss += proc.lastPss;
-                        stats.mBackgroundCount++;
-                    }
-                } else if (proc.setAdj >= VISIBLE_APP_ADJ) {
-                    stats.mVisiblePss += proc.lastPss;
-                    stats.mVisibleCount++;
-                } else {
-                    stats.mForegroundPss += proc.lastPss;
-                    stats.mForegroundCount++;
-                }
-            }
-        }
-    }
-    
     public final void startRunning(String pkg, String cls, String action,
             String data) {
         synchronized(this) {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 18fd9d6..18b1acb 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -39,7 +39,7 @@
  * Full information about a particular process that
  * is currently running.
  */
-class ProcessRecord implements Watchdog.PssRequestor {
+class ProcessRecord {
     final BatteryStatsImpl.Uid.Proc batteryStats; // where to collect runtime statistics
     final ApplicationInfo info; // all about the first app in the process
     final String processName;   // name of the process
@@ -264,16 +264,6 @@
         }
     }
     
-    public void requestPss() {
-        IApplicationThread localThread = thread;
-        if (localThread != null) {
-            try {
-                localThread.requestPss();
-            } catch (RemoteException e) {
-            }
-        }
-    }
-    
     public String toShortString() {
         if (shortStringName != null) {
             return shortStringName;
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 1a6119a..d0f856b 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -19,14 +19,17 @@
 //#define LOG_NDEBUG 0
 
 // Log debug messages about InputReaderPolicy
-#define DEBUG_INPUT_READER_POLICY 1
+#define DEBUG_INPUT_READER_POLICY 0
 
 // Log debug messages about InputDispatcherPolicy
-#define DEBUG_INPUT_DISPATCHER_POLICY 1
+#define DEBUG_INPUT_DISPATCHER_POLICY 0
 
+// Log debug messages about input focus tracking
+#define DEBUG_FOCUS 0
 
 #include "JNIHelp.h"
 #include "jni.h"
+#include <limits.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <ui/InputReader.h>
 #include <ui/InputDispatcher.h>
@@ -37,10 +40,94 @@
 #include "../../core/jni/android_view_KeyEvent.h"
 #include "../../core/jni/android_view_MotionEvent.h"
 #include "../../core/jni/android_view_InputChannel.h"
-#include "../../core/jni/android_view_InputTarget.h"
 
 namespace android {
 
+// Window flags from WindowManager.LayoutParams
+enum {
+    FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001,
+    FLAG_DIM_BEHIND        = 0x00000002,
+    FLAG_BLUR_BEHIND        = 0x00000004,
+    FLAG_NOT_FOCUSABLE      = 0x00000008,
+    FLAG_NOT_TOUCHABLE      = 0x00000010,
+    FLAG_NOT_TOUCH_MODAL    = 0x00000020,
+    FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
+    FLAG_KEEP_SCREEN_ON     = 0x00000080,
+    FLAG_LAYOUT_IN_SCREEN   = 0x00000100,
+    FLAG_LAYOUT_NO_LIMITS   = 0x00000200,
+    FLAG_FULLSCREEN      = 0x00000400,
+    FLAG_FORCE_NOT_FULLSCREEN   = 0x00000800,
+    FLAG_DITHER             = 0x00001000,
+    FLAG_SECURE             = 0x00002000,
+    FLAG_SCALED             = 0x00004000,
+    FLAG_IGNORE_CHEEK_PRESSES    = 0x00008000,
+    FLAG_LAYOUT_INSET_DECOR = 0x00010000,
+    FLAG_ALT_FOCUSABLE_IM = 0x00020000,
+    FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
+    FLAG_SHOW_WHEN_LOCKED = 0x00080000,
+    FLAG_SHOW_WALLPAPER = 0x00100000,
+    FLAG_TURN_SCREEN_ON = 0x00200000,
+    FLAG_DISMISS_KEYGUARD = 0x00400000,
+    FLAG_IMMERSIVE = 0x00800000,
+    FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
+    FLAG_COMPATIBLE_WINDOW = 0x20000000,
+    FLAG_SYSTEM_ERROR = 0x40000000,
+};
+
+// Window types from WindowManager.LayoutParams
+enum {
+    FIRST_APPLICATION_WINDOW = 1,
+    TYPE_BASE_APPLICATION   = 1,
+    TYPE_APPLICATION        = 2,
+    TYPE_APPLICATION_STARTING = 3,
+    LAST_APPLICATION_WINDOW = 99,
+    FIRST_SUB_WINDOW        = 1000,
+    TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW,
+    TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1,
+    TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
+    TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
+    TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW+4,
+    LAST_SUB_WINDOW         = 1999,
+    FIRST_SYSTEM_WINDOW     = 2000,
+    TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW,
+    TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1,
+    TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2,
+    TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3,
+    TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4,
+    TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5,
+    TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6,
+    TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7,
+    TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8,
+    TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9,
+    TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10,
+    TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11,
+    TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
+    TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13,
+    TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14,
+    LAST_SYSTEM_WINDOW      = 2999,
+};
+
+enum {
+    POWER_MANAGER_OTHER_EVENT = 0,
+    POWER_MANAGER_CHEEK_EVENT = 1,
+    POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either
+                                   // up events or LONG_TOUCH events.
+    POWER_MANAGER_LONG_TOUCH_EVENT = 3,
+    POWER_MANAGER_TOUCH_UP_EVENT = 4,
+    POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
+};
+
+// Delay between reporting long touch events to the power manager.
+const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms
+
+// Default input dispatching timeout if there is no focused application or paused window
+// from which to determine an appropriate dispatching timeout.
+const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+
+// Minimum amount of time to provide to the input dispatcher for delivery of an event
+// regardless of how long the application window was paused.
+const nsecs_t MIN_INPUT_DISPATCHING_TIMEOUT = 1000 * 1000000LL; // 1 sec
+
 // ----------------------------------------------------------------------------
 
 static struct {
@@ -53,17 +140,18 @@
     jmethodID notifyInputChannelBroken;
     jmethodID notifyInputChannelANR;
     jmethodID notifyInputChannelRecoveredFromANR;
+    jmethodID notifyANR;
     jmethodID virtualKeyFeedback;
-    jmethodID hackInterceptKey;
+    jmethodID interceptKeyBeforeQueueing;
+    jmethodID interceptKeyBeforeDispatching;
+    jmethodID checkInjectEventsPermission;
     jmethodID goToSleep;
-    jmethodID pokeUserActivityForKey;
+    jmethodID pokeUserActivity;
     jmethodID notifyAppSwitchComing;
     jmethodID filterTouchEvents;
     jmethodID filterJumpyTouchEvents;
     jmethodID getVirtualKeyDefinitions;
     jmethodID getExcludedDeviceNames;
-    jmethodID getKeyEventTargets;
-    jmethodID getMotionEventTargets;
 } gCallbacksClassInfo;
 
 static struct {
@@ -79,9 +167,37 @@
 static struct {
     jclass clazz;
 
-    jmethodID ctor;
-    jfieldID mArray;
-} gInputTargetListClassInfo;
+    jfieldID inputChannel;
+    jfieldID layoutParamsFlags;
+    jfieldID layoutParamsType;
+    jfieldID dispatchingTimeoutNanos;
+    jfieldID frameLeft;
+    jfieldID frameTop;
+    jfieldID touchableAreaLeft;
+    jfieldID touchableAreaTop;
+    jfieldID touchableAreaRight;
+    jfieldID touchableAreaBottom;
+    jfieldID visible;
+    jfieldID hasFocus;
+    jfieldID hasWallpaper;
+    jfieldID paused;
+    jfieldID ownerPid;
+    jfieldID ownerUid;
+} gInputWindowClassInfo;
+
+static struct {
+    jclass clazz;
+
+    jfieldID name;
+    jfieldID dispatchingTimeoutNanos;
+    jfieldID token;
+} gInputApplicationClassInfo;
+
+// ----------------------------------------------------------------------------
+
+static inline nsecs_t now() {
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+}
 
 // ----------------------------------------------------------------------------
 
@@ -103,6 +219,11 @@
             jweak inputChannelObjWeak);
     status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
 
+    void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
+    void setFocusedApplication(JNIEnv* env, jobject applicationObj);
+    void setInputDispatchMode(bool enabled, bool frozen);
+    void preemptInputDispatch();
+
     /* --- InputReaderPolicyInterface implementation --- */
 
     virtual bool getDisplayInfo(int32_t displayId,
@@ -130,16 +251,66 @@
             nsecs_t& outNewTimeout);
     virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
     virtual nsecs_t getKeyRepeatTimeout();
-    virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+    virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
-    virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+    virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
 
 private:
+    struct InputWindow {
+        sp<InputChannel> inputChannel;
+        int32_t layoutParamsFlags;
+        int32_t layoutParamsType;
+        nsecs_t dispatchingTimeout;
+        int32_t frameLeft;
+        int32_t frameTop;
+        int32_t touchableAreaLeft;
+        int32_t touchableAreaTop;
+        int32_t touchableAreaRight;
+        int32_t touchableAreaBottom;
+        bool visible;
+        bool hasFocus;
+        bool hasWallpaper;
+        bool paused;
+        int32_t ownerPid;
+        int32_t ownerUid;
+
+        inline bool touchableAreaContainsPoint(int32_t x, int32_t y) {
+            return x >= touchableAreaLeft && x <= touchableAreaRight
+                    && y >= touchableAreaTop && y <= touchableAreaBottom;
+        }
+    };
+
+    struct InputApplication {
+        String8 name;
+        nsecs_t dispatchingTimeout;
+        jweak tokenObjWeak;
+    };
+
+    class ANRTimer {
+        enum Budget {
+            SYSTEM = 0,
+            APPLICATION = 1
+        };
+
+        Budget mBudget;
+        nsecs_t mStartTime;
+        bool mFrozen;
+        InputWindow* mPausedWindow;
+
+    public:
+        ANRTimer();
+
+        void dispatchFrozenBySystem();
+        void dispatchPausedByApplication(InputWindow* pausedWindow);
+        bool waitForDispatchStateChangeLd(NativeInputManager* inputManager);
+
+        nsecs_t getTimeSpentWaitingForApplication() const;
+    };
+
     sp<InputManager> mInputManager;
 
     jobject mCallbacksObj;
-    jobject mReusableInputTargetListObj;
 
     // Cached filtering policies.
     int32_t mFilterTouchEvents;
@@ -160,30 +331,81 @@
 
     jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel);
 
+    // Input target and focus tracking.  (lock mDispatchLock)
+    Mutex mDispatchLock;
+    Condition mDispatchStateChanged;
+
+    bool mDispatchEnabled;
+    bool mDispatchFrozen;
+    bool mWindowsReady;
+    Vector<InputWindow> mWindows;
+    Vector<InputWindow*> mWallpaperWindows;
+
+    // Focus tracking for keys, trackball, etc.
+    InputWindow* mFocusedWindow;
+
+    // Focus tracking for touch.
+    bool mTouchDown;
+    InputWindow* mTouchedWindow;                   // primary target for current down
+    Vector<InputWindow*> mTouchedWallpaperWindows; // wallpaper targets
+
+    Vector<InputWindow*> mTempTouchedOutsideWindows; // temporary outside touch targets
+    Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
+
+    // Focused application.
+    InputApplication* mFocusedApplication;
+    InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
+
+    void dumpDispatchStateLd();
+
+    bool notifyANR(jobject tokenObj, nsecs_t& outNewTimeout);
+    void releaseFocusedApplicationLd(JNIEnv* env);
+
+    int32_t waitForFocusedWindowLd(uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+            Vector<InputTarget>& outTargets, InputWindow*& outFocusedWindow);
+    int32_t waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
+            int32_t injectorPid, int32_t injectorUid,
+            Vector<InputTarget>& outTargets, InputWindow*& outTouchedWindow);
+
+    void releaseTouchedWindowLd();
+
+    int32_t identifyTrackballEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+            int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+    int32_t identifyTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+            int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+
+    bool interceptKeyBeforeDispatching(const InputTarget& target,
+            const KeyEvent* keyEvent, uint32_t policyFlags);
+
+    void pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType);
+    void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
+    bool checkInjectionPermission(const InputWindow* window,
+            int32_t injectorPid, int32_t injectorUid);
+
+    static bool populateWindow(JNIEnv* env, jobject windowObj, InputWindow& outWindow);
+    static void addTarget(const InputWindow* window, int32_t targetFlags,
+            nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets);
+
     static inline JNIEnv* jniEnv() {
         return AndroidRuntime::getJNIEnv();
     }
 
     static bool isAppSwitchKey(int32_t keyCode);
     static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
-    static void populateInputTargets(JNIEnv* env, jobject inputTargetListObj,
-            Vector<InputTarget>& outTargets);
 };
 
 // ----------------------------------------------------------------------------
 
 NativeInputManager::NativeInputManager(jobject callbacksObj) :
     mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
-    mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1) {
+    mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0),
+    mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true),
+    mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
+    mFocusedApplication(NULL) {
     JNIEnv* env = jniEnv();
 
     mCallbacksObj = env->NewGlobalRef(callbacksObj);
 
-    jobject inputTargetListObj = env->NewObject(gInputTargetListClassInfo.clazz,
-            gInputTargetListClassInfo.ctor);
-    mReusableInputTargetListObj = env->NewGlobalRef(inputTargetListObj);
-    env->DeleteLocalRef(inputTargetListObj);
-
     sp<EventHub> eventHub = new EventHub();
     mInputManager = new InputManager(eventHub, this, this);
 }
@@ -192,7 +414,8 @@
     JNIEnv* env = jniEnv();
 
     env->DeleteGlobalRef(mCallbacksObj);
-    env->DeleteGlobalRef(mReusableInputTargetListObj);
+
+    releaseFocusedApplicationLd(env);
 }
 
 bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
@@ -362,7 +585,7 @@
         int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
 #if DEBUG_INPUT_READER_POLICY
     LOGD("interceptKey - when=%lld, deviceId=%d, down=%d, keyCode=%d, scanCode=%d, "
-            "policyFlags=%d",
+            "policyFlags=0x%x",
             when, deviceId, down, keyCode, scanCode, policyFlags);
 #endif
 
@@ -375,9 +598,10 @@
     bool isScreenOn = this->isScreenOn();
     bool isScreenBright = this->isScreenBright();
 
-    jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.hackInterceptKey,
+    jint wmActions = env->CallIntMethod(mCallbacksObj,
+            gCallbacksClassInfo.interceptKeyBeforeQueueing,
             deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn);
-    if (checkAndClearExceptionFromCallback(env, "hackInterceptKey")) {
+    if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
         wmActions = 0;
     }
 
@@ -398,8 +622,7 @@
     }
 
     if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
-        env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivityForKey, when);
-        checkAndClearExceptionFromCallback(env, "pokeUserActivityForKey");
+        pokeUserActivity(when, POWER_MANAGER_BUTTON_EVENT);
     }
 
     if (wmActions & WM_ACTION_PASS_TO_USER) {
@@ -412,6 +635,7 @@
             actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING;
         }
     }
+
     return actions;
 }
 
@@ -423,12 +647,13 @@
     int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
     if (isScreenOn()) {
         // Only dispatch touch events when the device is awake.
+        // Do not wake the device.
         actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
-    }
 
-    if (! isScreenBright()) {
-        // Brighten the screen if dimmed.
-        actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+        if (! isScreenBright()) {
+            // Brighten the screen if dimmed.
+            actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+        }
     }
 
     return actions;
@@ -444,12 +669,13 @@
     int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
     if (isScreenOn()) {
         // Only dispatch trackball events when the device is awake.
+        // Do not wake the device.
         actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
-    }
 
-    if (! isScreenBright()) {
-        // Brighten the screen if dimmed.
-        actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+        if (! isScreenBright()) {
+            // Brighten the screen if dimmed.
+            actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+        }
     }
 
     return actions;
@@ -639,6 +865,27 @@
     }
 }
 
+bool NativeInputManager::notifyANR(jobject tokenObj, nsecs_t& outNewTimeout) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("notifyANR");
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    jlong newTimeout = env->CallLongMethod(mCallbacksObj,
+            gCallbacksClassInfo.notifyANR, tokenObj);
+    if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
+        newTimeout = -2;
+    }
+
+    if (newTimeout == -2) {
+        return false; // abort
+    }
+
+    outNewTimeout = newTimeout;
+    return true; // resume
+}
+
 nsecs_t NativeInputManager::getKeyRepeatTimeout() {
     if (! isScreenOn()) {
         // Disable key repeat when the screen is off.
@@ -649,85 +896,898 @@
     }
 }
 
-int32_t NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
-        int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("getKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
-            policyFlags, injectorPid, injectorUid);
+void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
+#if DEBUG_FOCUS
+    LOGD("setInputWindows");
+#endif
+    { // acquire lock
+        AutoMutex _l(mDispatchLock);
+
+        sp<InputChannel> touchedWindowChannel;
+        if (mTouchedWindow) {
+            touchedWindowChannel = mTouchedWindow->inputChannel;
+            mTouchedWindow = NULL;
+        }
+        size_t numTouchedWallpapers = mTouchedWallpaperWindows.size();
+        if (numTouchedWallpapers != 0) {
+            for (size_t i = 0; i < numTouchedWallpapers; i++) {
+                mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel);
+            }
+            mTouchedWallpaperWindows.clear();
+        }
+
+        mWindows.clear();
+        mFocusedWindow = NULL;
+        mWallpaperWindows.clear();
+
+        if (windowObjArray) {
+            mWindowsReady = true;
+
+            jsize length = env->GetArrayLength(windowObjArray);
+            for (jsize i = 0; i < length; i++) {
+                jobject inputTargetObj = env->GetObjectArrayElement(windowObjArray, i);
+                if (! inputTargetObj) {
+                    break; // found null element indicating end of used portion of the array
+                }
+
+                mWindows.push();
+                InputWindow& window = mWindows.editTop();
+                bool valid = populateWindow(env, inputTargetObj, window);
+                if (! valid) {
+                    mWindows.pop();
+                }
+
+                env->DeleteLocalRef(inputTargetObj);
+            }
+
+            size_t numWindows = mWindows.size();
+            for (size_t i = 0; i < numWindows; i++) {
+                InputWindow* window = & mWindows.editItemAt(i);
+                if (window->hasFocus) {
+                    mFocusedWindow = window;
+                }
+
+                if (window->layoutParamsType == TYPE_WALLPAPER) {
+                    mWallpaperWindows.push(window);
+
+                    for (size_t j = 0; j < numTouchedWallpapers; j++) {
+                        if (window->inputChannel == mTempTouchedWallpaperChannels[i]) {
+                            mTouchedWallpaperWindows.push(window);
+                        }
+                    }
+                }
+
+                if (window->inputChannel == touchedWindowChannel) {
+                    mTouchedWindow = window;
+                }
+            }
+        } else {
+            mWindowsReady = false;
+        }
+
+        mTempTouchedWallpaperChannels.clear();
+
+        mDispatchStateChanged.broadcast();
+
+#if DEBUG_FOCUS
+        dumpDispatchStateLd();
+#endif
+    } // release lock
+}
+
+bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj,
+        InputWindow& outWindow) {
+    bool valid = false;
+
+    jobject inputChannelObj = env->GetObjectField(windowObj,
+            gInputWindowClassInfo.inputChannel);
+    if (inputChannelObj) {
+        sp<InputChannel> inputChannel =
+                android_view_InputChannel_getInputChannel(env, inputChannelObj);
+        if (inputChannel != NULL) {
+            jint layoutParamsFlags = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.layoutParamsFlags);
+            jint layoutParamsType = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.layoutParamsType);
+            jlong dispatchingTimeoutNanos = env->GetLongField(windowObj,
+                    gInputWindowClassInfo.dispatchingTimeoutNanos);
+            jint frameLeft = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.frameLeft);
+            jint frameTop = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.frameTop);
+            jint touchableAreaLeft = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.touchableAreaLeft);
+            jint touchableAreaTop = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.touchableAreaTop);
+            jint touchableAreaRight = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.touchableAreaRight);
+            jint touchableAreaBottom = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.touchableAreaBottom);
+            jboolean visible = env->GetBooleanField(windowObj,
+                    gInputWindowClassInfo.visible);
+            jboolean hasFocus = env->GetBooleanField(windowObj,
+                    gInputWindowClassInfo.hasFocus);
+            jboolean hasWallpaper = env->GetBooleanField(windowObj,
+                    gInputWindowClassInfo.hasWallpaper);
+            jboolean paused = env->GetBooleanField(windowObj,
+                    gInputWindowClassInfo.paused);
+            jint ownerPid = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.ownerPid);
+            jint ownerUid = env->GetIntField(windowObj,
+                    gInputWindowClassInfo.ownerUid);
+
+            outWindow.inputChannel = inputChannel;
+            outWindow.layoutParamsFlags = layoutParamsFlags;
+            outWindow.layoutParamsType = layoutParamsType;
+            outWindow.dispatchingTimeout = dispatchingTimeoutNanos;
+            outWindow.frameLeft = frameLeft;
+            outWindow.frameTop = frameTop;
+            outWindow.touchableAreaLeft = touchableAreaLeft;
+            outWindow.touchableAreaTop = touchableAreaTop;
+            outWindow.touchableAreaRight = touchableAreaRight;
+            outWindow.touchableAreaBottom = touchableAreaBottom;
+            outWindow.visible = visible;
+            outWindow.hasFocus = hasFocus;
+            outWindow.hasWallpaper = hasWallpaper;
+            outWindow.paused = paused;
+            outWindow.ownerPid = ownerPid;
+            outWindow.ownerUid = ownerUid;
+            valid = true;
+        } else {
+            LOGW("Dropping input target because its input channel is not initialized.");
+        }
+
+        env->DeleteLocalRef(inputChannelObj);
+    } else {
+        LOGW("Dropping input target because the input channel object was null.");
+    }
+    return valid;
+}
+
+void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationObj) {
+#if DEBUG_FOCUS
+    LOGD("setFocusedApplication");
+#endif
+    { // acquire lock
+        AutoMutex _l(mDispatchLock);
+
+        releaseFocusedApplicationLd(env);
+
+        if (applicationObj) {
+            jstring nameObj = jstring(env->GetObjectField(applicationObj,
+                    gInputApplicationClassInfo.name));
+            jlong dispatchingTimeoutNanos = env->GetLongField(applicationObj,
+                    gInputApplicationClassInfo.dispatchingTimeoutNanos);
+            jobject tokenObj = env->GetObjectField(applicationObj,
+                    gInputApplicationClassInfo.token);
+            jweak tokenObjWeak = env->NewWeakGlobalRef(tokenObj);
+            if (! tokenObjWeak) {
+                LOGE("Could not create weak reference for application token.");
+                LOGE_EX(env);
+                env->ExceptionClear();
+            }
+            env->DeleteLocalRef(tokenObj);
+
+            mFocusedApplication = & mFocusedApplicationStorage;
+
+            if (nameObj) {
+                const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
+                mFocusedApplication->name.setTo(nameStr);
+                env->ReleaseStringUTFChars(nameObj, nameStr);
+                env->DeleteLocalRef(nameObj);
+            } else {
+                LOGE("InputApplication.name should not be null.");
+                mFocusedApplication->name.setTo("unknown");
+            }
+
+            mFocusedApplication->dispatchingTimeout = dispatchingTimeoutNanos;
+            mFocusedApplication->tokenObjWeak = tokenObjWeak;
+        }
+
+        mDispatchStateChanged.broadcast();
+
+#if DEBUG_FOCUS
+        dumpDispatchStateLd();
+#endif
+    } // release lock
+}
+
+void NativeInputManager::releaseFocusedApplicationLd(JNIEnv* env) {
+    if (mFocusedApplication) {
+        env->DeleteWeakGlobalRef(mFocusedApplication->tokenObjWeak);
+        mFocusedApplication = NULL;
+    }
+}
+
+void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) {
+#if DEBUG_FOCUS
+    LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
 #endif
 
-    JNIEnv* env = jniEnv();
+    { // acquire lock
+        AutoMutex _l(mDispatchLock);
 
-    jint injectionResult;
-    jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
-    if (! keyEventObj) {
-        LOGE("Could not obtain DVM KeyEvent object to get key event targets.");
-        injectionResult = INPUT_EVENT_INJECTION_FAILED;
-    } else {
-        jint injectionResult = env->CallIntMethod(mCallbacksObj,
-                gCallbacksClassInfo.getKeyEventTargets, mReusableInputTargetListObj,
-                keyEventObj, jint(keyEvent->getNature()), jint(policyFlags),
-                jint(injectorPid), jint(injectorUid));
-        if (checkAndClearExceptionFromCallback(env, "getKeyEventTargets")) {
-            injectionResult = INPUT_EVENT_INJECTION_FAILED;
-        } else {
-            populateInputTargets(env, mReusableInputTargetListObj, outTargets);
+        if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
+            mDispatchEnabled = enabled;
+            mDispatchFrozen = frozen;
+
+            mDispatchStateChanged.broadcast();
         }
-        env->DeleteLocalRef(keyEventObj);
+
+#if DEBUG_FOCUS
+        dumpDispatchStateLd();
+#endif
+    } // release lock
+}
+
+void NativeInputManager::preemptInputDispatch() {
+#if DEBUG_FOCUS
+    LOGD("preemptInputDispatch");
+#endif
+
+    mInputManager->preemptInputDispatch();
+}
+
+int32_t NativeInputManager::waitForFocusedWindowLd(uint32_t policyFlags,
+        int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
+        InputWindow*& outFocusedWindow) {
+
+    int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+    bool firstIteration = true;
+    ANRTimer anrTimer;
+    for (;;) {
+        if (firstIteration) {
+            firstIteration = false;
+        } else {
+            if (! anrTimer.waitForDispatchStateChangeLd(this)) {
+                LOGW("Dropping event because the dispatcher timed out waiting to identify "
+                        "the window that should receive it.");
+                injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+                break;
+            }
+        }
+
+        // If dispatch is not enabled then fail.
+        if (! mDispatchEnabled) {
+            LOGI("Dropping event because input dispatch is disabled.");
+            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+            break;
+        }
+
+        // If dispatch is frozen or we don't have valid window data yet then wait.
+        if (mDispatchFrozen || ! mWindowsReady) {
+#if DEBUG_FOCUS
+            LOGD("Waiting because dispatch is frozen or windows are not ready.");
+#endif
+            anrTimer.dispatchFrozenBySystem();
+            continue;
+        }
+
+        // If there is no currently focused window and no focused application
+        // then drop the event.
+        if (! mFocusedWindow) {
+            if (mFocusedApplication) {
+#if DEBUG_FOCUS
+                LOGD("Waiting because there is no focused window but there is a "
+                        "focused application that may yet introduce a new target: '%s'.",
+                        mFocusedApplication->name.string());
+#endif
+                continue;
+            }
+
+            LOGI("Dropping event because there is no focused window or focused application.");
+            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+            break;
+        }
+
+        // Check permissions.
+        if (! checkInjectionPermission(mFocusedWindow, injectorPid, injectorUid)) {
+            injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+            break;
+        }
+
+        // If the currently focused window is paused then keep waiting.
+        if (mFocusedWindow->paused) {
+#if DEBUG_FOCUS
+            LOGD("Waiting because focused window is paused.");
+#endif
+            anrTimer.dispatchPausedByApplication(mFocusedWindow);
+            continue;
+        }
+
+        // Success!
+        break; // done waiting, exit loop
     }
+
+    // Output targets.
+    if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+        addTarget(mFocusedWindow, InputTarget::FLAG_SYNC,
+                anrTimer.getTimeSpentWaitingForApplication(), outTargets);
+
+        outFocusedWindow = mFocusedWindow;
+    } else {
+        outFocusedWindow = NULL;
+    }
+
+#if DEBUG_FOCUS
+    LOGD("waitForFocusedWindow finished: injectionResult=%d",
+            injectionResult);
+    dumpDispatchStateLd();
+#endif
     return injectionResult;
 }
 
-int32_t NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
-        int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("getMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
-            policyFlags, injectorPid, injectorUid);
-#endif
+int32_t NativeInputManager::waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
+        int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
+        InputWindow*& outTouchedWindow) {
+    nsecs_t startTime = now();
 
-    JNIEnv* env = jniEnv();
+    int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+    int32_t action = motionEvent->getAction();
 
-    jint injectionResult;
-    jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent);
-    if (! motionEventObj) {
-        LOGE("Could not obtain DVM MotionEvent object to get key event targets.");
-        injectionResult = INPUT_EVENT_INJECTION_FAILED;
-    } else {
-        jint injectionResult = env->CallIntMethod(mCallbacksObj,
-                gCallbacksClassInfo.getMotionEventTargets, mReusableInputTargetListObj,
-                motionEventObj, jint(motionEvent->getNature()), jint(policyFlags),
-                jint(injectorPid), jint(injectorUid));
-        if (checkAndClearExceptionFromCallback(env, "getMotionEventTargets")) {
-            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+    // For security reasons, we defer updating the touch state until we are sure that
+    // event injection will be allowed.
+    //
+    // FIXME In the original code, screenWasOff could never be set to true.
+    //       The reason is that the POLICY_FLAG_WOKE_HERE
+    //       and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
+    //       EV_KEY, EV_REL and EV_ABS events.  As it happens, the touch event was
+    //       actually enqueued using the policyFlags that appeared in the final EV_SYN
+    //       events upon which no preprocessing took place.  So policyFlags was always 0.
+    //       In the new native input dispatcher we're a bit more careful about event
+    //       preprocessing so the touches we receive can actually have non-zero policyFlags.
+    //       Unfortunately we obtain undesirable behavior.
+    //
+    //       Here's what happens:
+    //
+    //       When the device dims in anticipation of going to sleep, touches
+    //       in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
+    //       the device to brighten and reset the user activity timer.
+    //       Touches on other windows (such as the launcher window)
+    //       are dropped.  Then after a moment, the device goes to sleep.  Oops.
+    //
+    //       Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
+    //       instead of POLICY_FLAG_WOKE_HERE...
+    //
+    bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
+    bool firstIteration = true;
+    ANRTimer anrTimer;
+    for (;;) {
+        if (firstIteration) {
+            firstIteration = false;
         } else {
-            populateInputTargets(env, mReusableInputTargetListObj, outTargets);
+            if (! anrTimer.waitForDispatchStateChangeLd(this)) {
+                LOGW("Dropping event because the dispatcher timed out waiting to identify "
+                        "the window that should receive it.");
+                injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+                break;
+            }
         }
-        android_view_MotionEvent_recycle(env, motionEventObj);
-        env->DeleteLocalRef(motionEventObj);
+
+        // If dispatch is not enabled then fail.
+        if (! mDispatchEnabled) {
+            LOGI("Dropping event because input dispatch is disabled.");
+            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+            break; // failed, exit wait loop
+        }
+
+        // If dispatch is frozen or we don't have valid window data yet then wait.
+        if (mDispatchFrozen || ! mWindowsReady) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+            LOGD("Waiting because dispatch is frozen or windows are not ready.");
+#endif
+            anrTimer.dispatchFrozenBySystem();
+            continue;
+        }
+
+        // Update the touch state as needed based on the properties of the touch event.
+        if (action == MOTION_EVENT_ACTION_DOWN) {
+            InputWindow* newTouchedWindow = NULL;
+            mTempTouchedOutsideWindows.clear();
+
+            int32_t x = int32_t(motionEvent->getX(0));
+            int32_t y = int32_t(motionEvent->getY(0));
+            InputWindow* topErrorWindow = NULL;
+
+            // Traverse windows from front to back to find touched window and outside targets.
+            size_t numWindows = mWindows.size();
+            for (size_t i = 0; i < numWindows; i++) {
+                InputWindow* window = & mWindows.editItemAt(i);
+                int32_t flags = window->layoutParamsFlags;
+
+                if (flags & FLAG_SYSTEM_ERROR) {
+                    if (! topErrorWindow) {
+                        topErrorWindow = window;
+                    }
+                }
+
+                if (window->visible) {
+                    if (! (flags & FLAG_NOT_TOUCHABLE)) {
+                        bool isTouchModal = (flags &
+                                (FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL)) == 0;
+                        if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
+                            if (! screenWasOff || flags & FLAG_TOUCHABLE_WHEN_WAKING) {
+                                newTouchedWindow = window;
+                            }
+                            break; // found touched window, exit window loop
+                        }
+                    }
+
+                    if (flags & FLAG_WATCH_OUTSIDE_TOUCH) {
+                        mTempTouchedOutsideWindows.push(window);
+                    }
+                }
+            }
+
+            // If there is an error window but it is not taking focus (typically because
+            // it is invisible) then wait for it.  Any other focused window may in
+            // fact be in ANR state.
+            if (topErrorWindow && newTouchedWindow != topErrorWindow) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+                LOGD("Waiting because system error window is pending.");
+#endif
+                anrTimer.dispatchFrozenBySystem();
+                continue; // wait some more
+            }
+
+            // If we did not find a touched window then fail.
+            if (! newTouchedWindow) {
+                if (mFocusedApplication) {
+#if DEBUG_FOCUS
+                    LOGD("Waiting because there is no focused window but there is a "
+                            "focused application that may yet introduce a new target: '%s'.",
+                            mFocusedApplication->name.string());
+#endif
+                    continue;
+                }
+
+                LOGI("Dropping event because there is no touched window or focused application.");
+                injectionResult = INPUT_EVENT_INJECTION_FAILED;
+                break; // failed, exit wait loop
+            }
+
+            // Check permissions.
+            if (! checkInjectionPermission(newTouchedWindow, injectorPid, injectorUid)) {
+                injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+                break; // failed, exit wait loop
+            }
+
+            // If the touched window is paused then keep waiting.
+            if (newTouchedWindow->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+                LOGD("Waiting because touched window is paused.");
+#endif
+                anrTimer.dispatchPausedByApplication(newTouchedWindow);
+                continue; // wait some more
+            }
+
+            // Success!  Update the touch dispatch state for real.
+            releaseTouchedWindowLd();
+
+            mTouchedWindow = newTouchedWindow;
+
+            if (newTouchedWindow->hasWallpaper) {
+                mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
+            }
+            break; // done
+        } else {
+            // Check permissions.
+            if (! checkInjectionPermission(mTouchedWindow, injectorPid, injectorUid)) {
+                injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+                break; // failed, exit wait loop
+            }
+
+            // If there is no currently touched window then fail.
+            if (! mTouchedWindow || ! mTouchDown) {
+                LOGI("Dropping event because touched window is no longer valid.");
+                injectionResult = INPUT_EVENT_INJECTION_FAILED;
+                break; // failed, exit wait loop
+            }
+
+            // If the touched window is paused then keep waiting.
+            if (mTouchedWindow->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+                LOGD("Waiting because touched window is paused.");
+#endif
+                anrTimer.dispatchPausedByApplication(mTouchedWindow);
+                continue; // wait some more
+            }
+
+            // Success!
+            break; // done
+        }
     }
+
+    // Output targets.
+    bool havePermission;
+    if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+        // Injection succeeded so the injector must have permission.
+        havePermission = true;
+
+        size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
+        for (size_t i = 0; i < numWallpaperWindows; i++) {
+            addTarget(mTouchedWallpaperWindows[i], 0, 0, outTargets);
+        }
+
+        size_t numOutsideWindows = mTempTouchedOutsideWindows.size();
+        for (size_t i = 0; i < numOutsideWindows; i++) {
+            addTarget(mTempTouchedOutsideWindows[i], InputTarget::FLAG_OUTSIDE, 0, outTargets);
+        }
+
+        addTarget(mTouchedWindow, InputTarget::FLAG_SYNC,
+                anrTimer.getTimeSpentWaitingForApplication(), outTargets);
+        outTouchedWindow = mTouchedWindow;
+    } else {
+        if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED
+                && checkInjectionPermission(NULL, injectorPid, injectorUid)) {
+            // Injection failed but the injector does have permission to inject events.
+            // While we might not have found a valid target for the injected event, we
+            // still want to update the dispatch state to take it into account.
+            havePermission = true;
+        } else {
+            // Injector does not have permission to inject events.
+            // We make sure to leave the dispatch state unchanged.
+            havePermission = false;
+        }
+        outTouchedWindow = NULL;
+    }
+    mTempTouchedOutsideWindows.clear();
+
+    // Update final pieces of touch state now that we know for sure whether the injector
+    // had permission to perform the injection.
+    if (havePermission) {
+        if (action == MOTION_EVENT_ACTION_DOWN) {
+            if (mTouchDown) {
+                // This is weird.  We got a down but we thought it was already down!
+                LOGW("Pointer down received while already down.");
+            } else {
+                mTouchDown = true;
+            }
+
+            if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+                // Since we failed to identify a target for this touch down, we may still
+                // be holding on to an earlier target from a previous touch down.  Release it.
+                releaseTouchedWindowLd();
+            }
+        } else if (action == MOTION_EVENT_ACTION_UP) {
+            mTouchDown = false;
+            releaseTouchedWindowLd();
+        }
+    }
+
+#if DEBUG_FOCUS
+    LOGD("waitForTouchedWindow finished: injectionResult=%d",
+            injectionResult);
+    dumpDispatchStateLd();
+#endif
     return injectionResult;
 }
 
-void NativeInputManager::populateInputTargets(JNIEnv* env, jobject inputTargetListObj,
+void NativeInputManager::releaseTouchedWindowLd() {
+    mTouchedWindow = NULL;
+    mTouchedWallpaperWindows.clear();
+}
+
+void NativeInputManager::addTarget(const InputWindow* window, int32_t targetFlags,
+        nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets) {
+    nsecs_t timeout = window->dispatchingTimeout - timeSpentWaitingForApplication;
+    if (timeout < MIN_INPUT_DISPATCHING_TIMEOUT) {
+        timeout = MIN_INPUT_DISPATCHING_TIMEOUT;
+    }
+
+    outTargets.push();
+
+    InputTarget& target = outTargets.editTop();
+    target.inputChannel = window->inputChannel;
+    target.flags = targetFlags;
+    target.timeout = timeout;
+    target.xOffset = - window->frameLeft;
+    target.yOffset = - window->frameTop;
+}
+
+bool NativeInputManager::checkInjectionPermission(const InputWindow* window,
+        int32_t injectorPid, int32_t injectorUid) {
+    if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
+        JNIEnv* env = jniEnv();
+        jboolean result = env->CallBooleanMethod(mCallbacksObj,
+                gCallbacksClassInfo.checkInjectEventsPermission, injectorPid, injectorUid);
+        checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission");
+
+        if (! result) {
+            if (window) {
+                LOGW("Permission denied: injecting event from pid %d uid %d to window "
+                        "with input channel %s owned by uid %d",
+                        injectorPid, injectorUid, window->inputChannel->getName().string(),
+                        window->ownerUid);
+            } else {
+                LOGW("Permission denied: injecting event from pid %d uid %d",
+                        injectorPid, injectorUid);
+            }
+            return false;
+        }
+    }
+
+    return true;
+}
+
+int32_t NativeInputManager::waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+        int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("waitForKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+            policyFlags, injectorPid, injectorUid);
+#endif
+
+    int32_t windowType;
+    { // acquire lock
+        AutoMutex _l(mDispatchLock);
+
+        InputWindow* focusedWindow;
+        int32_t injectionResult = waitForFocusedWindowLd(policyFlags,
+                injectorPid, injectorUid, outTargets, /*out*/ focusedWindow);
+        if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+            return injectionResult;
+        }
+
+        windowType = focusedWindow->layoutParamsType;
+    } // release lock
+
+    const InputTarget& target = outTargets.top();
+    bool consumed = interceptKeyBeforeDispatching(target, keyEvent, policyFlags);
+    if (consumed) {
+        outTargets.clear();
+        return INPUT_EVENT_INJECTION_SUCCEEDED;
+    }
+
+    pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
+    return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+int32_t NativeInputManager::waitForMotionEventTargets(MotionEvent* motionEvent,
+        uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
         Vector<InputTarget>& outTargets) {
-    jobjectArray inputTargetArray = jobjectArray(env->GetObjectField(
-            inputTargetListObj, gInputTargetListClassInfo.mArray));
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("waitForMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+            policyFlags, injectorPid, injectorUid);
+#endif
 
-    jsize length = env->GetArrayLength(inputTargetArray);
-    for (jsize i = 0; i < length; i++) {
-        jobject item = env->GetObjectArrayElement(inputTargetArray, i);
-        if (! item) {
-            break; // found null element indicating end of used portion of the array
-        }
+    switch (motionEvent->getNature()) {
+    case INPUT_EVENT_NATURE_TRACKBALL:
+        return identifyTrackballEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+                outTargets);
 
-        outTargets.add();
-        android_view_InputTarget_toNative(env, item, & outTargets.editTop());
+    case INPUT_EVENT_NATURE_TOUCH:
+        return identifyTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+                outTargets);
 
-        env->DeleteLocalRef(item);
+    default:
+        assert(false);
+        return INPUT_EVENT_INJECTION_FAILED;
     }
-    env->DeleteLocalRef(inputTargetArray);
 }
 
+int32_t NativeInputManager::identifyTrackballEventTargets(MotionEvent* motionEvent,
+        uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+        Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("identifyTrackballEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+            policyFlags, injectorPid, injectorUid);
+#endif
+
+    int32_t windowType;
+    { // acquire lock
+        AutoMutex _l(mDispatchLock);
+
+        InputWindow* focusedWindow;
+        int32_t injectionResult = waitForFocusedWindowLd(policyFlags,
+                injectorPid, injectorUid, outTargets, /*out*/ focusedWindow);
+        if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+            return injectionResult;
+        }
+
+        windowType = focusedWindow->layoutParamsType;
+    } // release lock
+
+    pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
+    return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+int32_t NativeInputManager::identifyTouchEventTargets(MotionEvent* motionEvent,
+        uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+        Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("identifyTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+            policyFlags, injectorPid, injectorUid);
+#endif
+
+    int32_t windowType;
+    { // acquire lock
+        AutoMutex _l(mDispatchLock);
+
+        InputWindow* touchedWindow;
+        int32_t injectionResult = waitForTouchedWindowLd(motionEvent, policyFlags,
+                injectorPid, injectorUid, outTargets, /*out*/ touchedWindow);
+        if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+            return injectionResult;
+        }
+
+        windowType = touchedWindow->layoutParamsType;
+    } // release lock
+
+    int32_t eventType;
+    switch (motionEvent->getAction()) {
+    case MOTION_EVENT_ACTION_DOWN:
+        eventType = POWER_MANAGER_TOUCH_EVENT;
+        break;
+    case MOTION_EVENT_ACTION_UP:
+        eventType = POWER_MANAGER_TOUCH_UP_EVENT;
+        break;
+    default:
+        if (motionEvent->getEventTime() - motionEvent->getDownTime()
+                >= EVENT_IGNORE_DURATION) {
+            eventType = POWER_MANAGER_TOUCH_EVENT;
+        } else {
+            eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+        }
+        break;
+    }
+    pokeUserActivityIfNeeded(windowType, eventType);
+    return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+bool NativeInputManager::interceptKeyBeforeDispatching(const InputTarget& target,
+        const KeyEvent* keyEvent, uint32_t policyFlags) {
+    JNIEnv* env = jniEnv();
+
+    jobject inputChannelObj = getInputChannelObjLocal(env, target.inputChannel);
+    if (inputChannelObj) {
+        jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
+                gCallbacksClassInfo.interceptKeyBeforeDispatching,
+                inputChannelObj, keyEvent->getKeyCode(), keyEvent->getMetaState(),
+                keyEvent->getAction() == KEY_EVENT_ACTION_DOWN,
+                keyEvent->getRepeatCount(), policyFlags);
+        bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
+
+        env->DeleteLocalRef(inputChannelObj);
+
+        return consumed && ! error;
+    } else {
+        LOGW("Could not apply key dispatch policy because input channel '%s' is "
+                "no longer valid.", target.inputChannel->getName().string());
+        return false;
+    }
+}
+
+void NativeInputManager::pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType) {
+    if (windowType != TYPE_KEYGUARD) {
+        nsecs_t eventTime = now();
+        pokeUserActivity(eventTime, eventType);
+    }
+}
+
+void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
+    JNIEnv* env = jniEnv();
+    env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivity,
+            eventTime, eventType);
+    checkAndClearExceptionFromCallback(env, "pokeUserActivity");
+}
+
+void NativeInputManager::dumpDispatchStateLd() {
+#if DEBUG_FOCUS
+    LOGD("  dispatcherState: dispatchEnabled=%d, dispatchFrozen=%d, windowsReady=%d",
+            mDispatchEnabled, mDispatchFrozen, mWindowsReady);
+    if (mFocusedApplication) {
+        LOGD("  focusedApplication: name='%s', dispatchingTimeout=%0.3fms",
+                mFocusedApplication->name.string(),
+                mFocusedApplication->dispatchingTimeout / 1000000.0);
+    } else {
+        LOGD("  focusedApplication: <null>");
+    }
+    LOGD("  focusedWindow: '%s'",
+            mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>");
+    LOGD("  touchedWindow: '%s', touchDown=%d",
+            mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>",
+            mTouchDown);
+    for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
+        LOGD("  touchedWallpaperWindows[%d]: '%s'",
+                i, mTouchedWallpaperWindows[i]->inputChannel->getName().string());
+    }
+    for (size_t i = 0; i < mWindows.size(); i++) {
+        LOGD("  windows[%d]: '%s', paused=%d, hasFocus=%d, hasWallpaper=%d, visible=%d, "
+                "flags=0x%08x, type=0x%08x, "
+                "frame=[%d,%d], touchableArea=[%d,%d][%d,%d], "
+                "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms",
+                i, mWindows[i].inputChannel->getName().string(),
+                mWindows[i].paused, mWindows[i].hasFocus, mWindows[i].hasWallpaper,
+                mWindows[i].visible, mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
+                mWindows[i].frameLeft, mWindows[i].frameTop,
+                mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop,
+                mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom,
+                mWindows[i].ownerPid, mWindows[i].ownerUid,
+                mWindows[i].dispatchingTimeout / 1000000.0);
+    }
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+NativeInputManager::ANRTimer::ANRTimer() :
+        mBudget(APPLICATION), mStartTime(now()), mFrozen(false), mPausedWindow(NULL) {
+}
+
+void NativeInputManager::ANRTimer::dispatchFrozenBySystem() {
+    mFrozen = true;
+}
+
+void NativeInputManager::ANRTimer::dispatchPausedByApplication(InputWindow* pausedWindow) {
+    mPausedWindow = pausedWindow;
+}
+
+bool NativeInputManager::ANRTimer::waitForDispatchStateChangeLd(NativeInputManager* inputManager) {
+    nsecs_t currentTime = now();
+
+    Budget newBudget;
+    nsecs_t dispatchingTimeout;
+    sp<InputChannel> pausedChannel = NULL;
+    jobject tokenObj = NULL;
+    if (mFrozen) {
+        newBudget = SYSTEM;
+        dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+        mFrozen = false;
+    } else if (mPausedWindow) {
+        newBudget = APPLICATION;
+        dispatchingTimeout = mPausedWindow->dispatchingTimeout;
+        pausedChannel = mPausedWindow->inputChannel;
+        mPausedWindow = NULL;
+    } else if (inputManager->mFocusedApplication) {
+        newBudget = APPLICATION;
+        dispatchingTimeout = inputManager->mFocusedApplication->dispatchingTimeout;
+        tokenObj = jniEnv()->NewLocalRef(inputManager->mFocusedApplication->tokenObjWeak);
+    } else {
+        newBudget = APPLICATION;
+        dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+    }
+
+    if (mBudget != newBudget) {
+        mBudget = newBudget;
+        mStartTime = currentTime;
+    }
+
+    bool result = false;
+    nsecs_t timeoutRemaining = mStartTime + dispatchingTimeout - currentTime;
+    if (timeoutRemaining > 0
+            && inputManager->mDispatchStateChanged.waitRelative(inputManager->mDispatchLock,
+                    timeoutRemaining) == OK) {
+        result = true;
+    } else {
+        if (pausedChannel != NULL || tokenObj != NULL) {
+            bool resumed;
+            nsecs_t newTimeout = 0;
+
+            inputManager->mDispatchLock.unlock(); // release lock
+            if (pausedChannel != NULL) {
+                resumed = inputManager->notifyInputChannelANR(pausedChannel, /*out*/ newTimeout);
+            } else {
+                resumed = inputManager->notifyANR(tokenObj, /*out*/ newTimeout);
+            }
+            inputManager->mDispatchLock.lock(); // re-acquire lock
+
+            if (resumed) {
+                mStartTime = now() - dispatchingTimeout + newTimeout;
+                result = true;
+            }
+        }
+    }
+
+    if (tokenObj) {
+        jniEnv()->DeleteLocalRef(tokenObj);
+    }
+
+    return result;
+}
+
+nsecs_t NativeInputManager::ANRTimer::getTimeSpentWaitingForApplication() const {
+    return mBudget == APPLICATION ? now() - mStartTime : 0;
+}
 
 // ----------------------------------------------------------------------------
 
@@ -926,6 +1986,42 @@
             injectorPid, injectorUid, sync, timeoutMillis);
 }
 
+static void android_server_InputManager_nativeSetInputWindows(JNIEnv* env, jclass clazz,
+        jobjectArray windowObjArray) {
+    if (checkInputManagerUnitialized(env)) {
+        return;
+    }
+
+    gNativeInputManager->setInputWindows(env, windowObjArray);
+}
+
+static void android_server_InputManager_nativeSetFocusedApplication(JNIEnv* env, jclass clazz,
+        jobject applicationObj) {
+    if (checkInputManagerUnitialized(env)) {
+        return;
+    }
+
+    gNativeInputManager->setFocusedApplication(env, applicationObj);
+}
+
+static void android_server_InputManager_nativeSetInputDispatchMode(JNIEnv* env,
+        jclass clazz, jboolean enabled, jboolean frozen) {
+    if (checkInputManagerUnitialized(env)) {
+        return;
+    }
+
+    gNativeInputManager->setInputDispatchMode(enabled, frozen);
+}
+
+static void android_server_InputManager_nativePreemptInputDispatch(JNIEnv* env,
+        jclass clazz) {
+    if (checkInputManagerUnitialized(env)) {
+        return;
+    }
+
+    gNativeInputManager->preemptInputDispatch();
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gInputManagerMethods[] = {
@@ -953,7 +2049,15 @@
     { "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIIZI)I",
             (void*) android_server_InputManager_nativeInjectKeyEvent },
     { "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIIZI)I",
-            (void*) android_server_InputManager_nativeInjectMotionEvent }
+            (void*) android_server_InputManager_nativeInjectMotionEvent },
+    { "nativeSetInputWindows", "([Lcom/android/server/InputWindow;)V",
+            (void*) android_server_InputManager_nativeSetInputWindows },
+    { "nativeSetFocusedApplication", "(Lcom/android/server/InputApplication;)V",
+            (void*) android_server_InputManager_nativeSetFocusedApplication },
+    { "nativeSetInputDispatchMode", "(ZZ)V",
+            (void*) android_server_InputManager_nativeSetInputDispatchMode },
+    { "nativePreemptInputDispatch", "()V",
+            (void*) android_server_InputManager_nativePreemptInputDispatch }
 };
 
 #define FIND_CLASS(var, className) \
@@ -999,17 +2103,26 @@
     GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, gCallbacksClassInfo.clazz,
             "notifyInputChannelRecoveredFromANR", "(Landroid/view/InputChannel;)V");
 
+    GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz,
+            "notifyANR", "(Ljava/lang/Object;)J");
+
     GET_METHOD_ID(gCallbacksClassInfo.virtualKeyFeedback, gCallbacksClassInfo.clazz,
             "virtualKeyFeedback", "(JIIIIIIJ)V");
 
-    GET_METHOD_ID(gCallbacksClassInfo.hackInterceptKey, gCallbacksClassInfo.clazz,
-            "hackInterceptKey", "(IIIIIIJZ)I");
+    GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz,
+            "interceptKeyBeforeQueueing", "(IIIIIIJZ)I");
+
+    GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz,
+            "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIZII)Z");
+
+    GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
+            "checkInjectEventsPermission", "(II)Z");
 
     GET_METHOD_ID(gCallbacksClassInfo.goToSleep, gCallbacksClassInfo.clazz,
             "goToSleep", "(J)V");
 
-    GET_METHOD_ID(gCallbacksClassInfo.pokeUserActivityForKey, gCallbacksClassInfo.clazz,
-            "pokeUserActivityForKey", "(J)V");
+    GET_METHOD_ID(gCallbacksClassInfo.pokeUserActivity, gCallbacksClassInfo.clazz,
+            "pokeUserActivity", "(JI)V");
 
     GET_METHOD_ID(gCallbacksClassInfo.notifyAppSwitchComing, gCallbacksClassInfo.clazz,
             "notifyAppSwitchComing", "()V");
@@ -1027,14 +2140,6 @@
     GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
             "getExcludedDeviceNames", "()[Ljava/lang/String;");
 
-    GET_METHOD_ID(gCallbacksClassInfo.getKeyEventTargets, gCallbacksClassInfo.clazz,
-            "getKeyEventTargets",
-            "(Lcom/android/server/InputTargetList;Landroid/view/KeyEvent;IIII)I");
-
-    GET_METHOD_ID(gCallbacksClassInfo.getMotionEventTargets, gCallbacksClassInfo.clazz,
-            "getMotionEventTargets",
-            "(Lcom/android/server/InputTargetList;Landroid/view/MotionEvent;IIII)I");
-
     // VirtualKeyDefinition
 
     FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,
@@ -1055,15 +2160,71 @@
     GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz,
             "height", "I");
 
-    // InputTargetList
+    // InputWindow
 
-    FIND_CLASS(gInputTargetListClassInfo.clazz, "com/android/server/InputTargetList");
+    FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow");
 
-    GET_METHOD_ID(gInputTargetListClassInfo.ctor, gInputTargetListClassInfo.clazz,
-            "<init>", "()V");
+    GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz,
+            "inputChannel", "Landroid/view/InputChannel;");
 
-    GET_FIELD_ID(gInputTargetListClassInfo.mArray, gInputTargetListClassInfo.clazz,
-            "mArray", "[Landroid/view/InputTarget;");
+    GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, gInputWindowClassInfo.clazz,
+            "layoutParamsFlags", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.layoutParamsType, gInputWindowClassInfo.clazz,
+            "layoutParamsType", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.dispatchingTimeoutNanos, gInputWindowClassInfo.clazz,
+            "dispatchingTimeoutNanos", "J");
+
+    GET_FIELD_ID(gInputWindowClassInfo.frameLeft, gInputWindowClassInfo.clazz,
+            "frameLeft", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.frameTop, gInputWindowClassInfo.clazz,
+            "frameTop", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.touchableAreaLeft, gInputWindowClassInfo.clazz,
+            "touchableAreaLeft", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.touchableAreaTop, gInputWindowClassInfo.clazz,
+            "touchableAreaTop", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.touchableAreaRight, gInputWindowClassInfo.clazz,
+            "touchableAreaRight", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.touchableAreaBottom, gInputWindowClassInfo.clazz,
+            "touchableAreaBottom", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.visible, gInputWindowClassInfo.clazz,
+            "visible", "Z");
+
+    GET_FIELD_ID(gInputWindowClassInfo.hasFocus, gInputWindowClassInfo.clazz,
+            "hasFocus", "Z");
+
+    GET_FIELD_ID(gInputWindowClassInfo.hasWallpaper, gInputWindowClassInfo.clazz,
+            "hasWallpaper", "Z");
+
+    GET_FIELD_ID(gInputWindowClassInfo.paused, gInputWindowClassInfo.clazz,
+            "paused", "Z");
+
+    GET_FIELD_ID(gInputWindowClassInfo.ownerPid, gInputWindowClassInfo.clazz,
+            "ownerPid", "I");
+
+    GET_FIELD_ID(gInputWindowClassInfo.ownerUid, gInputWindowClassInfo.clazz,
+            "ownerUid", "I");
+
+    // InputApplication
+
+    FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/InputApplication");
+
+    GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz,
+            "name", "Ljava/lang/String;");
+
+    GET_FIELD_ID(gInputApplicationClassInfo.dispatchingTimeoutNanos,
+            gInputApplicationClassInfo.clazz,
+            "dispatchingTimeoutNanos", "J");
+
+    GET_FIELD_ID(gInputApplicationClassInfo.token, gInputApplicationClassInfo.clazz,
+            "token", "Ljava/lang/Object;");
 
     return 0;
 }
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
index 0b41dd8..59d7cde 100755
--- a/services/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "GpsLocationProvider"
 
-//#define LOG_NDDEBUG 0
+//#define LOG_NDEBUG 0
 
 #include "JNIHelp.h"
 #include "jni.h"
@@ -66,7 +66,6 @@
 
 static void location_callback(GpsLocation* location)
 {
-    LOGD("location_callback\n");
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(mCallbacksObj, method_reportLocation, location->flags,
             (jdouble)location->latitude, (jdouble)location->longitude,
@@ -78,16 +77,13 @@
 
 static void status_callback(GpsStatus* status)
 {
-    LOGD("status_callback\n");
     JNIEnv* env = AndroidRuntime::getJNIEnv();
-    LOGD("env: %p obj: %p\n", env, mCallbacksObj);
     env->CallVoidMethod(mCallbacksObj, method_reportStatus, status->status);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
 }
 
 static void sv_status_callback(GpsSvStatus* sv_status)
 {
-    LOGD("sv_status_callback\n");
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     memcpy(&sGpsSvStatus, sv_status, sizeof(sGpsSvStatus));
     env->CallVoidMethod(mCallbacksObj, method_reportSvStatus);
@@ -96,7 +92,6 @@
 
 static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length)
 {
-    LOGD("nmea_callback\n");
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     // The Java code will call back to read these values
     // We do this to avoid creating unnecessary String objects
@@ -108,7 +103,6 @@
 
 static void set_capabilities_callback(uint32_t capabilities)
 {
-    LOGD("set_capabilities_callback\n");
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(mCallbacksObj, method_setEngineCapabilities, capabilities);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -116,19 +110,16 @@
 
 static void acquire_wakelock_callback()
 {
-    LOGD("acquire_wakelock_callback\n");
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
 }
 
 static void release_wakelock_callback()
 {
-    LOGD("release_wakelock_callback\n");
     release_wake_lock(WAKE_LOCK_NAME);
 }
 
 static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg)
 {
-    LOGD("create_thread_callback\n");
     return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);
 }
 
@@ -146,7 +137,6 @@
 
 static void xtra_download_request_callback()
 {
-    LOGD("xtra_download_request_callback\n");
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(mCallbacksObj, method_xtraDownloadRequest);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -159,7 +149,6 @@
 
 static void agps_status_callback(AGpsStatus* agps_status)
 {
-    LOGD("agps_status_callback\n");
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus,
                         agps_status->type, agps_status->status);
@@ -241,8 +230,6 @@
 
 static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
 {
-    LOGD("android_location_GpsLocationProvider_init obj: %p\n", obj);
-
     // this must be set before calling into the HAL library
     if (!mCallbacksObj)
         mCallbacksObj = env->NewGlobalRef(obj);
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 8c343b5..c6a4134 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -104,6 +104,8 @@
                     0,
                     fullScreenIntent,
                     PendingIntent.FLAG_CANCEL_CURRENT);
+                // if you tap on it you should get the original alert box
+                not.contentIntent = not.fullScreenIntent;
                 mNotificationManager.notify(id, not);
             }
         },