Merge "Added missing comments in Instrumentation#sendStringSync"
diff --git a/api/current.txt b/api/current.txt
index 37b888e..1bd2c09 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10696,6 +10696,7 @@
     field public static final int METADATA_KEY_GENRE = 6; // 0x6
     field public static final int METADATA_KEY_HAS_AUDIO = 16; // 0x10
     field public static final int METADATA_KEY_HAS_VIDEO = 17; // 0x11
+    field public static final int METADATA_KEY_LOCATION = 23; // 0x17
     field public static final int METADATA_KEY_MIMETYPE = 12; // 0xc
     field public static final int METADATA_KEY_NUM_TRACKS = 10; // 0xa
     field public static final int METADATA_KEY_TITLE = 7; // 0x7
@@ -18892,7 +18893,8 @@
     method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
     method public deprecated int setEngineByPackageName(java.lang.String);
     method public int setLanguage(java.util.Locale);
-    method public int setOnUtteranceCompletedListener(android.speech.tts.TextToSpeech.OnUtteranceCompletedListener);
+    method public deprecated int setOnUtteranceCompletedListener(android.speech.tts.TextToSpeech.OnUtteranceCompletedListener);
+    method public int setOnUtteranceProgressListener(android.speech.tts.UtteranceProgressListener);
     method public int setPitch(float);
     method public int setSpeechRate(float);
     method public void shutdown();
@@ -18965,6 +18967,13 @@
     method protected abstract void onSynthesizeText(android.speech.tts.SynthesisRequest, android.speech.tts.SynthesisCallback);
   }
 
+  public abstract class UtteranceProgressListener {
+    ctor public UtteranceProgressListener();
+    method public abstract void onDone(java.lang.String);
+    method public abstract void onError(java.lang.String);
+    method public abstract void onStart(java.lang.String);
+  }
+
 }
 
 package android.telephony {
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index d816e7c..154dbb8 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -42,6 +42,7 @@
 #include <surfaceflinger/ISurfaceComposerClient.h>
 
 #include <core/SkBitmap.h>
+#include <core/SkStream.h>
 #include <images/SkImageDecoder.h>
 
 #include <GLES/gl.h>
@@ -150,9 +151,15 @@
     //StopWatch watch("blah");
 
     SkBitmap bitmap;
-    SkImageDecoder::DecodeMemory(buffer, len,
-            &bitmap, SkBitmap::kRGB_565_Config,
-            SkImageDecoder::kDecodePixels_Mode);
+    SkMemoryStream  stream(buffer, len);
+    SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
+    codec->setDitherImage(false);
+    if (codec) {
+        codec->decode(&stream, &bitmap,
+                SkBitmap::kRGB_565_Config,
+                SkImageDecoder::kDecodePixels_Mode);
+        delete codec;
+    }
 
     // ensure we can call getPixels(). No need to call unlock, since the
     // bitmap will go out of scope when we return from this method.
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index ca66a4e..1e746cd 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -63,6 +63,7 @@
 
     printf("\n");
     printf("Build: %s\n", build);
+    printf("Build fingerprint: '%s'\n", fingerprint); /* format is important for other tools */
     printf("Bootloader: %s\n", bootloader);
     printf("Radio: %s\n", radio);
     printf("Network: %s\n", network);
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index f383af9..7f0ea99 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -657,6 +657,15 @@
      */
     private void setupChangeAnimation(final ViewGroup parent, final int changeReason,
             Animator baseAnimator, final long duration, final View child) {
+
+        // If we already have a listener for this child, then we've already set up the
+        // changing animation we need. Multiple calls for a child may occur when several
+        // add/remove operations are run at once on a container; each one will trigger
+        // changes for the existing children in the container.
+        if (layoutChangeListenerMap.get(child) != null) {
+            return;
+        }
+
         // Make a copy of the appropriate animation
         final Animator anim = baseAnimator.clone();
 
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 522f477..0c6baeb 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -901,6 +901,7 @@
         private RuntimeException mUnbindLocation;
 
         private boolean mDied;
+        private boolean mForgotten;
 
         private static class ConnectionInfo {
             IBinder binder;
@@ -959,6 +960,7 @@
                     ci.binder.unlinkToDeath(ci.deathMonitor, 0);
                 }
                 mActiveConnections.clear();
+                mForgotten = true;
             }
         }
 
@@ -1020,6 +1022,11 @@
             ServiceDispatcher.ConnectionInfo info;
 
             synchronized (this) {
+                if (mForgotten) {
+                    // We unbound before receiving the connection; ignore
+                    // any connection received.
+                    return;
+                }
                 old = mActiveConnections.get(name);
                 if (old != null && old.binder == service) {
                     // Huh, already have this one.  Oh well!
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index f81ea81..91398bc 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -212,7 +212,11 @@
              */
             mHandler.sendEmptyMessage(MSG_CLEAR_WALLPAPER);
         }
-        
+
+        public Handler getHandler() {
+            return mHandler;
+        }
+
         public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) {
             synchronized (this) {
                 if (mWallpaper != null) {
@@ -604,7 +608,7 @@
             // Ignore
         }
     }
-    
+
     /**
      * Set the position of the current wallpaper within any larger space, when
      * that wallpaper is visible behind the given window.  The X and Y offsets
@@ -619,16 +623,23 @@
      * @param yOffset The offset along the Y dimension, from 0 to 1.
      */
     public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
-        try {
-            //Log.v(TAG, "Sending new wallpaper offsets from app...");
-            ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
-                    windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep);
-            //Log.v(TAG, "...app returning after sending offsets!");
-        } catch (RemoteException e) {
-            // Ignore.
-        }
+        final IBinder fWindowToken = windowToken;
+        final float fXOffset = xOffset;
+        final float fYOffset = yOffset;
+        sGlobals.getHandler().post(new Runnable() {
+            public void run() {
+                try {
+                    //Log.v(TAG, "Sending new wallpaper offsets from app...");
+                    ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+                            fWindowToken, fXOffset, fYOffset, mWallpaperXStep, mWallpaperYStep);
+                    //Log.v(TAG, "...app returning after sending offsets!");
+                } catch (RemoteException e) {
+                    // Ignore.
+                }
+            }
+        });
     }
-    
+
     /**
      * For applications that use multiple virtual screens showing a wallpaper,
      * specify the step size between virtual screens. For example, if the
diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index 7addd4a..b1d0070 100644
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -86,7 +86,7 @@
     private static final int CONNECTION_ACCESS_REQUEST_REPLY = 104;
     private static final int CONNECTION_ACCESS_REQUEST_EXPIRY = 105;
 
-    private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs
+    public static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs
     private static final int CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT = 7000; // 7 secs
     private static final int CONNECTION_ACCESS_UNDEFINED = -1;
     private static final long INIT_INCOMING_REJECT_TIMER = 1000; // 1 sec
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index fefeb93..deea2b8 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -90,7 +90,7 @@
 
     boolean connectHeadset(String address);
     boolean disconnectHeadset(String address);
-    boolean notifyIncomingConnection(String address);
+    boolean notifyIncomingConnection(String address, boolean rejected);
 
     // HID profile APIs
     boolean connectInputDevice(in BluetoothDevice device);
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
index ad1bfb2..bea6529 100644
--- a/core/java/android/content/res/XmlBlock.java
+++ b/core/java/android/content/res/XmlBlock.java
@@ -484,7 +484,7 @@
 
     private final AssetManager mAssets;
     private final int mNative;
-    private final StringBlock mStrings;
+    /*package*/ final StringBlock mStrings;
     private boolean mOpen = true;
     private int mOpenCount = 1;
 
@@ -494,9 +494,9 @@
     private static final native int nativeGetStringBlock(int obj);
 
     private static final native int nativeCreateParseState(int obj);
-    private static final native int nativeNext(int state);
+    /*package*/ static final native int nativeNext(int state);
     private static final native int nativeGetNamespace(int state);
-    private static final native int nativeGetName(int state);
+    /*package*/ static final native int nativeGetName(int state);
     private static final native int nativeGetText(int state);
     private static final native int nativeGetLineNumber(int state);
     private static final native int nativeGetAttributeCount(int state);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 0052dd0..a569317 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -360,6 +360,11 @@
         }
     }
 
+    /**
+     * Gets you info about the current data network.
+     * Call {@link NetworkInfo#isConnected()} on the returned {@link NetworkInfo}
+     * to check if the device has a data connection.
+    */
     public NetworkInfo getActiveNetworkInfo() {
         try {
             return mService.getActiveNetworkInfo();
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 537750a..7286f0d 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -22,8 +22,9 @@
 import java.util.EnumMap;
 
 /**
- * Describes the status of a network interface of a given type
- * (currently either Mobile or Wifi).
+ * Describes the status of a network interface.
+ * <p>Use {@link ConnectivityManager#getActiveNetworkInfo()} to get an instance that represents
+ * the current network connection.
  */
 public class NetworkInfo implements Parcelable {
 
@@ -38,7 +39,7 @@
      * <tr><td><code>SCANNING</code></td><td><code>CONNECTING</code></td></tr>
      * <tr><td><code>CONNECTING</code></td><td><code>CONNECTING</code></td></tr>
      * <tr><td><code>AUTHENTICATING</code></td><td><code>CONNECTING</code></td></tr>
-     * <tr><td><code>CONNECTED</code></td><td<code>CONNECTED</code></td></tr>
+     * <tr><td><code>CONNECTED</code></td><td><code>CONNECTED</code></td></tr>
      * <tr><td><code>DISCONNECTING</code></td><td><code>DISCONNECTING</code></td></tr>
      * <tr><td><code>DISCONNECTED</code></td><td><code>DISCONNECTED</code></td></tr>
      * <tr><td><code>UNAVAILABLE</code></td><td><code>DISCONNECTED</code></td></tr>
@@ -159,9 +160,12 @@
     }
 
     /**
-     * Reports the type of network (currently mobile or Wi-Fi) to which the
-     * info in this object pertains.
-     * @return the network type
+     * Reports the type of network to which the
+     * info in this {@code NetworkInfo} pertains.
+     * @return one of {@link ConnectivityManager#TYPE_MOBILE}, {@link
+     * ConnectivityManager#TYPE_WIFI}, {@link ConnectivityManager#TYPE_WIMAX}, {@link
+     * ConnectivityManager#TYPE_ETHERNET},  {@link ConnectivityManager#TYPE_BLUETOOTH}, or other
+     * types defined by {@link ConnectivityManager}
      */
     public int getType() {
         synchronized (this) {
@@ -226,6 +230,7 @@
     /**
      * Indicates whether network connectivity exists and it is possible to establish
      * connections and pass data.
+     * <p>Always call this before attempting to perform data transactions.
      * @return {@code true} if network connectivity exists, {@code false} otherwise.
      */
     public boolean isConnected() {
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 3605652..f6e627c 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -464,6 +464,19 @@
      * time, and that none of them have disappeared.
      */
     public NetworkStats subtract(NetworkStats value) throws NonMonotonicException {
+        return subtract(value, false);
+    }
+
+    /**
+     * Subtract the given {@link NetworkStats}, effectively leaving the delta
+     * between two snapshots in time. Assumes that statistics rows collect over
+     * time, and that none of them have disappeared.
+     *
+     * @param clampNonMonotonic When non-monotonic stats are found, just clamp
+     *            to 0 instead of throwing {@link NonMonotonicException}.
+     */
+    public NetworkStats subtract(NetworkStats value, boolean clampNonMonotonic)
+            throws NonMonotonicException {
         final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime;
         if (deltaRealtime < 0) {
             throw new NonMonotonicException(this, value);
@@ -497,7 +510,15 @@
 
                 if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
                         || entry.txPackets < 0 || entry.operations < 0) {
-                    throw new NonMonotonicException(this, i, value, j);
+                    if (clampNonMonotonic) {
+                        entry.rxBytes = Math.max(entry.rxBytes, 0);
+                        entry.rxPackets = Math.max(entry.rxPackets, 0);
+                        entry.txBytes = Math.max(entry.txBytes, 0);
+                        entry.txPackets = Math.max(entry.txPackets, 0);
+                        entry.operations = Math.max(entry.operations, 0);
+                    } else {
+                        throw new NonMonotonicException(this, i, value, j);
+                    }
                 }
             }
 
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 3a2bbec..0fb49bc 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1645,6 +1645,9 @@
     /**
      * Searches the query string for the first value with the given key.
      *
+     * <p><strong>Warning:</strong> Prior to Ice Cream Sandwich, this decoded
+     * the '+' character as '+' rather than ' '.
+     *
      * @param key which will be encoded
      * @throws UnsupportedOperationException if this isn't a hierarchical URI
      * @throws NullPointerException if key is null
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 016af58..0b93ad0 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -32,7 +32,7 @@
 interface INfcAdapter
 {
     INfcTag getNfcTagInterface();
-    INfcAdapterExtras getNfcAdapterExtrasInterface();
+    INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg);
 
     int getState();
     boolean disable();
diff --git a/core/java/android/nfc/INfcAdapterExtras.aidl b/core/java/android/nfc/INfcAdapterExtras.aidl
index 0c2a2fd..2b9d4f0 100644
--- a/core/java/android/nfc/INfcAdapterExtras.aidl
+++ b/core/java/android/nfc/INfcAdapterExtras.aidl
@@ -23,10 +23,10 @@
  * {@hide}
  */
 interface INfcAdapterExtras {
-    Bundle open(IBinder b);
-    Bundle close();
-    Bundle transceive(in byte[] data_in);
-    int getCardEmulationRoute();
-    void setCardEmulationRoute(int route);
-    void authenticate(in byte[] token);
+    Bundle open(in String pkg, IBinder b);
+    Bundle close(in String pkg, IBinder b);
+    Bundle transceive(in String pkg, in byte[] data_in);
+    int getCardEmulationRoute(in String pkg);
+    void setCardEmulationRoute(in String pkg, int route);
+    void authenticate(in String pkg, in byte[] token);
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index f2bccaa..dec262b 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -16,6 +16,8 @@
 
 package android.nfc;
 
+import java.util.HashMap;
+
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.app.Activity;
@@ -197,15 +199,21 @@
     static INfcTag sTagService;
 
     /**
-     * NfcAdapter is currently a singleton, and does not require a context.
-     * However all the public API's are future-proofed to require a context.
-     * If we start using that then we'll need to keep a HashMap of
-     * Context.getApplicationContext() -> NfcAdapter, such that NfcAdapter
-     * is a singleton within each application context.
+     * The NfcAdapter object for each application context.
+     * There is a 1-1 relationship between application context and
+     * NfcAdapter object.
      */
-    static NfcAdapter sSingleton;  // protected by NfcAdapter.class
+    static HashMap<Context, NfcAdapter> sNfcAdapters = new HashMap(); //guard by NfcAdapter.class
+
+    /**
+     * NfcAdapter used with a null context. This ctor was deprecated but we have
+     * to support it for backwards compatibility. New methods that require context
+     * might throw when called on the null-context NfcAdapter.
+     */
+    static NfcAdapter sNullContextNfcAdapter;  // protected by NfcAdapter.class
 
     final NfcActivityManager mNfcActivityManager;
+    final Context mContext;
 
     /**
      * A callback to be invoked when the system successfully delivers your {@link NdefMessage}
@@ -280,12 +288,12 @@
     }
 
     /**
-     * Returns the singleton, or throws if NFC is not available.
+     * Returns the NfcAdapter for application context,
+     * or throws if NFC is not available.
+     * @hide
      */
-    static synchronized NfcAdapter getSingleton() {
+    public static synchronized NfcAdapter getNfcAdapter(Context context) {
         if (!sIsInitialized) {
-            sIsInitialized = true;
-
             /* is this device meant to have NFC */
             if (!hasNfcFeature()) {
                 Log.v(TAG, "this device does not have NFC support");
@@ -303,12 +311,21 @@
                 Log.e(TAG, "could not retrieve NFC Tag service");
                 throw new UnsupportedOperationException();
             }
-            sSingleton = new NfcAdapter();
+
+            sIsInitialized = true;
         }
-        if (sSingleton == null) {
-            throw new UnsupportedOperationException();
+        if (context == null) {
+            if (sNullContextNfcAdapter == null) {
+                sNullContextNfcAdapter = new NfcAdapter(null);
+            }
+            return sNullContextNfcAdapter;
         }
-        return sSingleton;
+        NfcAdapter adapter = sNfcAdapters.get(context);
+        if (adapter == null) {
+            adapter = new NfcAdapter(context);
+            sNfcAdapters.put(context, adapter);
+        }
+        return adapter;
     }
 
     /** get handle to NFC service interface */
@@ -336,6 +353,10 @@
      * @return the default NFC adapter, or null if no NFC adapter exists
      */
     public static NfcAdapter getDefaultAdapter(Context context) {
+        if (context == null) {
+            throw new IllegalArgumentException("context cannot be null");
+        }
+        context = context.getApplicationContext();
         /* use getSystemService() instead of just instantiating to take
          * advantage of the context's cached NfcManager & NfcAdapter */
         NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
@@ -343,25 +364,30 @@
     }
 
     /**
-     * Get a handle to the default NFC Adapter on this Android device.
-     * <p>
-     * Most Android devices will only have one NFC Adapter (NFC Controller).
-     *
-     * @return the default NFC adapter, or null if no NFC adapter exists
+     * Legacy NfcAdapter getter, always use {@link #getDefaultAdapter(Context)} instead.<p>
+     * This method was deprecated at API level 10 (Gingerbread MR1) because a context is required
+     * for many NFC API methods. Those methods will fail when called on an NfcAdapter
+     * object created from this method.<p>
      * @deprecated use {@link #getDefaultAdapter(Context)}
      */
     @Deprecated
     public static NfcAdapter getDefaultAdapter() {
         Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " +
                 "NfcAdapter.getDefaultAdapter(Context) instead", new Exception());
-        return getSingleton();
+
+        return NfcAdapter.getNfcAdapter(null);
+    }
+
+    NfcAdapter(Context context) {
+        mContext = context;
+        mNfcActivityManager = new NfcActivityManager(this);
     }
 
     /**
-     * Does not currently need a context.
+     * @hide
      */
-    NfcAdapter() {
-        mNfcActivityManager = new NfcActivityManager(this);
+    public Context getContext() {
+        return mContext;
     }
 
     /**
@@ -834,8 +860,12 @@
      * @hide
      */
     public INfcAdapterExtras getNfcAdapterExtrasInterface() {
+        if (mContext == null) {
+            throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
+                    + " NFC extras APIs");
+        }
         try {
-            return sService.getNfcAdapterExtrasInterface();
+            return sService.getNfcAdapterExtrasInterface(mContext.getPackageName());
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
             return null;
diff --git a/core/java/android/nfc/NfcManager.java b/core/java/android/nfc/NfcManager.java
index 300ab45..6ec2e21 100644
--- a/core/java/android/nfc/NfcManager.java
+++ b/core/java/android/nfc/NfcManager.java
@@ -39,8 +39,9 @@
      */
     public NfcManager(Context context) {
         NfcAdapter adapter;
+        context = context.getApplicationContext();
         try {
-            adapter = NfcAdapter.getSingleton();
+            adapter = NfcAdapter.getNfcAdapter(context);
         } catch (UnsupportedOperationException e) {
             adapter = null;
         }
diff --git a/core/java/android/server/BluetoothAdapterStateMachine.java b/core/java/android/server/BluetoothAdapterStateMachine.java
index da7c489..c59a05a 100644
--- a/core/java/android/server/BluetoothAdapterStateMachine.java
+++ b/core/java/android/server/BluetoothAdapterStateMachine.java
@@ -62,6 +62,17 @@
  * m1 = TURN_HOT
  * m2 = Transition to HotOff when number of process wanting BT on is 0.
  *      POWER_STATE_CHANGED will make the transition.
+ * Note:
+ * The diagram above shows all the states and messages that trigger normal state changes.
+ * The diagram above does not capture everything:
+ *   The diagram does not capture following messages.
+ *   - messages that do not trigger state changes
+ *     For example, PER_PROCESS_TURN_ON received in BluetoothOn state
+ *   - unhandled messages
+ *     For example, USER_TURN_ON received in BluetoothOn state
+ *   - timeout messages
+ *   The diagram does not capture error conditions and state recoveries.
+ *   - For example POWER_STATE_CHANGED received in BluetoothOn state
  */
 final class BluetoothAdapterStateMachine extends StateMachine {
     private static final String TAG = "BluetoothAdapterStateMachine";
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 1b473ec..aa62cd7 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -784,11 +784,12 @@
                 // machine.  We don't handle AVCTP signals currently. We only send
                 // intents for AVDTP state changes. We need to handle both of them in
                 // some cases. For now, just don't move to incoming state in this case.
-                mBluetoothService.notifyIncomingA2dpConnection(address);
+                mBluetoothService.notifyIncomingA2dpConnection(address, true);
             } else {
                 Log.i(TAG, "" + authorized +
                       "Incoming A2DP / AVRCP connection from " + address);
                 mA2dp.allowIncomingConnect(device, authorized);
+                mBluetoothService.notifyIncomingA2dpConnection(address, false);
             }
         } else if (BluetoothUuid.isInputDevice(uuid)) {
             // We can have more than 1 input device connected.
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 9ca5847..d604a01 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -89,7 +89,7 @@
 
     private int mNativeData;
     private BluetoothEventLoop mEventLoop;
-    private BluetoothHeadset mBluetoothHeadset;
+    private BluetoothHeadset mHeadsetProxy;
     private BluetoothInputDevice mInputDevice;
     private BluetoothPan mPan;
     private boolean mIsAirplaneSensitive;
@@ -605,6 +605,7 @@
         }
         mBondState.initBondState();
         initProfileState();
+        getProfileProxy();
     }
 
     /**
@@ -1766,8 +1767,8 @@
 
     private void dumpHeadsetService(PrintWriter pw) {
         pw.println("\n--Headset Service--");
-        if (mBluetoothHeadset != null) {
-            List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
+        if (mHeadsetProxy != null) {
+            List<BluetoothDevice> deviceList = mHeadsetProxy.getConnectedDevices();
             if (deviceList.size() == 0) {
                 pw.println("No headsets connected");
             } else {
@@ -1775,21 +1776,20 @@
                 pw.println("\ngetConnectedDevices[0] = " + device);
                 dumpHeadsetConnectionState(pw, device);
                 pw.println("getBatteryUsageHint() = " +
-                             mBluetoothHeadset.getBatteryUsageHint(device));
+                             mHeadsetProxy.getBatteryUsageHint(device));
             }
 
             deviceList.clear();
-            deviceList = mBluetoothHeadset.getDevicesMatchingConnectionStates(new int[] {
+            deviceList = mHeadsetProxy.getDevicesMatchingConnectionStates(new int[] {
                      BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
             pw.println("--Connected and Disconnected Headsets");
             for (BluetoothDevice device: deviceList) {
                 pw.println(device);
-                if (mBluetoothHeadset.isAudioConnected(device)) {
+                if (mHeadsetProxy.isAudioConnected(device)) {
                     pw.println("SCO audio connected to device:" + device);
                 }
             }
         }
-        mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
     }
 
     private void dumpInputDeviceProfile(PrintWriter pw) {
@@ -1824,7 +1824,6 @@
                 pw.println(device);
             }
         }
-        mAdapter.closeProfileProxy(BluetoothProfile.INPUT_DEVICE, mBluetoothHeadset);
     }
 
     private void dumpPanProfile(PrintWriter pw) {
@@ -1862,7 +1861,7 @@
 
     private void dumpHeadsetConnectionState(PrintWriter pw,
             BluetoothDevice device) {
-        switch (mBluetoothHeadset.getConnectionState(device)) {
+        switch (mHeadsetProxy.getConnectionState(device)) {
             case BluetoothHeadset.STATE_CONNECTING:
                 pw.println("getConnectionState() = STATE_CONNECTING");
                 break;
@@ -1884,7 +1883,6 @@
             Integer pid = mServiceRecordToPid.get(handle).first;
             pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
         }
-        mAdapter.closeProfileProxy(BluetoothProfile.PAN, mBluetoothHeadset);
     }
 
     private void dumpAclConnectedDevices(PrintWriter pw) {
@@ -1927,11 +1925,16 @@
         }
     }
 
+    private void getProfileProxy() {
+        mAdapter.getProfileProxy(mContext,
+                                 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
+    }
+
     private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
         new BluetoothProfile.ServiceListener() {
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
             if (profile == BluetoothProfile.HEADSET) {
-                mBluetoothHeadset = (BluetoothHeadset) proxy;
+                mHeadsetProxy = (BluetoothHeadset) proxy;
             } else if (profile == BluetoothProfile.INPUT_DEVICE) {
                 mInputDevice = (BluetoothInputDevice) proxy;
             } else if (profile == BluetoothProfile.PAN) {
@@ -1940,7 +1943,7 @@
         }
         public void onServiceDisconnected(int profile) {
             if (profile == BluetoothProfile.HEADSET) {
-                mBluetoothHeadset = null;
+                mHeadsetProxy = null;
             } else if (profile == BluetoothProfile.INPUT_DEVICE) {
                 mInputDevice = null;
             } else if (profile == BluetoothProfile.PAN) {
@@ -2424,25 +2427,43 @@
         }
     }
 
-    public boolean notifyIncomingConnection(String address) {
-        BluetoothDeviceProfileState state =
-             mDeviceProfileState.get(address);
+    public boolean notifyIncomingConnection(String address, boolean rejected) {
+        BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
         if (state != null) {
             Message msg = new Message();
-            msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
-            state.sendMessage(msg);
+            if (rejected) {
+                if (mA2dpService.getPriority(getRemoteDevice(address)) >=
+                    BluetoothProfile.PRIORITY_ON) {
+                    msg.what = BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES;
+                    msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
+                    state.sendMessageDelayed(msg,
+                        BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES_DELAY);
+                }
+            } else {
+                msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
+                state.sendMessage(msg);
+            }
             return true;
         }
         return false;
     }
 
-    /*package*/ boolean notifyIncomingA2dpConnection(String address) {
-       BluetoothDeviceProfileState state =
-            mDeviceProfileState.get(address);
+    /*package*/ boolean notifyIncomingA2dpConnection(String address, boolean rejected) {
+       BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
        if (state != null) {
            Message msg = new Message();
-           msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
-           state.sendMessage(msg);
+           if (rejected) {
+               if (mHeadsetProxy.getPriority(getRemoteDevice(address)) >=
+                   BluetoothProfile.PRIORITY_ON) {
+                   msg.what = BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES;
+                   msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
+                   state.sendMessageDelayed(msg,
+                             BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES_DELAY);
+               }
+           } else {
+               msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
+               state.sendMessage(msg);
+           }
            return true;
        }
        return false;
diff --git a/core/java/android/speech/tts/AudioMessageParams.java b/core/java/android/speech/tts/AudioMessageParams.java
index 68d8738..29b4367 100644
--- a/core/java/android/speech/tts/AudioMessageParams.java
+++ b/core/java/android/speech/tts/AudioMessageParams.java
@@ -15,12 +15,12 @@
  */
 package android.speech.tts;
 
-import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
 
 class AudioMessageParams extends MessageParams {
     private final BlockingMediaPlayer mPlayer;
 
-    AudioMessageParams(UtteranceCompletedDispatcher dispatcher,
+    AudioMessageParams(UtteranceProgressDispatcher dispatcher,
             String callingApp, BlockingMediaPlayer player) {
         super(dispatcher, callingApp);
         mPlayer = player;
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
index d970ae6..0194240 100644
--- a/core/java/android/speech/tts/AudioPlaybackHandler.java
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -312,10 +312,11 @@
     private void handleSilence(MessageParams msg) {
         if (DBG) Log.d(TAG, "handleSilence()");
         SilenceMessageParams params = (SilenceMessageParams) msg;
+        params.getDispatcher().dispatchOnStart();
         if (params.getSilenceDurationMs() > 0) {
             params.getConditionVariable().block(params.getSilenceDurationMs());
         }
-        params.getDispatcher().dispatchUtteranceCompleted();
+        params.getDispatcher().dispatchOnDone();
         if (DBG) Log.d(TAG, "handleSilence() done.");
     }
 
@@ -323,11 +324,12 @@
     private void handleAudio(MessageParams msg) {
         if (DBG) Log.d(TAG, "handleAudio()");
         AudioMessageParams params = (AudioMessageParams) msg;
+        params.getDispatcher().dispatchOnStart();
         // Note that the BlockingMediaPlayer spawns a separate thread.
         //
         // TODO: This can be avoided.
         params.getPlayer().startAndWait();
-        params.getDispatcher().dispatchUtteranceCompleted();
+        params.getDispatcher().dispatchOnDone();
         if (DBG) Log.d(TAG, "handleAudio() done.");
     }
 
@@ -361,6 +363,7 @@
         if (DBG) Log.d(TAG, "Created audio track [" + audioTrack.hashCode() + "]");
 
         param.setAudioTrack(audioTrack);
+        msg.getDispatcher().dispatchOnStart();
     }
 
     // More data available to be flushed to the audio track.
@@ -411,6 +414,7 @@
         final AudioTrack audioTrack = params.getAudioTrack();
 
         if (audioTrack == null) {
+            params.getDispatcher().dispatchOnError();
             return;
         }
 
@@ -439,7 +443,7 @@
             audioTrack.release();
             params.setAudioTrack(null);
         }
-        params.getDispatcher().dispatchUtteranceCompleted();
+        params.getDispatcher().dispatchOnDone();
         mLastSynthesisRequest = null;
         params.mLogger.onWriteData();
     }
diff --git a/core/java/android/speech/tts/ITextToSpeechCallback.aidl b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
index 40902ae..f0287d4 100755
--- a/core/java/android/speech/tts/ITextToSpeechCallback.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
@@ -21,5 +21,7 @@
  * {@hide}
  */
 oneway interface ITextToSpeechCallback {
-    void utteranceCompleted(String utteranceId);
+    void onStart(String utteranceId);
+    void onDone(String utteranceId);
+    void onError(String utteranceId);
 }
diff --git a/core/java/android/speech/tts/MessageParams.java b/core/java/android/speech/tts/MessageParams.java
index e7d6da3..de9cc07 100644
--- a/core/java/android/speech/tts/MessageParams.java
+++ b/core/java/android/speech/tts/MessageParams.java
@@ -15,22 +15,22 @@
  */
 package android.speech.tts;
 
-import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
 
 abstract class MessageParams {
     static final int TYPE_SYNTHESIS = 1;
     static final int TYPE_AUDIO = 2;
     static final int TYPE_SILENCE = 3;
 
-    private final UtteranceCompletedDispatcher mDispatcher;
+    private final UtteranceProgressDispatcher mDispatcher;
     private final String mCallingApp;
 
-    MessageParams(UtteranceCompletedDispatcher dispatcher, String callingApp) {
+    MessageParams(UtteranceProgressDispatcher dispatcher, String callingApp) {
         mDispatcher = dispatcher;
         mCallingApp = callingApp;
     }
 
-    UtteranceCompletedDispatcher getDispatcher() {
+    UtteranceProgressDispatcher getDispatcher() {
         return mDispatcher;
     }
 
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
index 0cca06a..ce3522b 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -15,7 +15,7 @@
  */
 package android.speech.tts;
 
-import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
 import android.util.Log;
 
 /**
@@ -62,12 +62,12 @@
 
     private volatile boolean mDone = false;
 
-    private final UtteranceCompletedDispatcher mDispatcher;
+    private final UtteranceProgressDispatcher mDispatcher;
     private final String mCallingApp;
     private final EventLogger mLogger;
 
     PlaybackSynthesisCallback(int streamType, float volume, float pan,
-            AudioPlaybackHandler audioTrackHandler, UtteranceCompletedDispatcher dispatcher,
+            AudioPlaybackHandler audioTrackHandler, UtteranceProgressDispatcher dispatcher,
             String callingApp, EventLogger logger) {
         mStreamType = streamType;
         mVolume = volume;
diff --git a/core/java/android/speech/tts/SilenceMessageParams.java b/core/java/android/speech/tts/SilenceMessageParams.java
index 7a4ff1c..9909126 100644
--- a/core/java/android/speech/tts/SilenceMessageParams.java
+++ b/core/java/android/speech/tts/SilenceMessageParams.java
@@ -16,13 +16,13 @@
 package android.speech.tts;
 
 import android.os.ConditionVariable;
-import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
 
 class SilenceMessageParams extends MessageParams {
     private final ConditionVariable mCondVar = new ConditionVariable();
     private final long mSilenceDurationMs;
 
-    SilenceMessageParams(UtteranceCompletedDispatcher dispatcher,
+    SilenceMessageParams(UtteranceProgressDispatcher dispatcher,
             String callingApp, long silenceDurationMs) {
         super(dispatcher, callingApp);
         mSilenceDurationMs = silenceDurationMs;
diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java
index 779721e..0c0f033 100644
--- a/core/java/android/speech/tts/SynthesisMessageParams.java
+++ b/core/java/android/speech/tts/SynthesisMessageParams.java
@@ -17,7 +17,7 @@
 
 import android.media.AudioFormat;
 import android.media.AudioTrack;
-import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
 
 import java.util.LinkedList;
 
@@ -56,7 +56,7 @@
 
     SynthesisMessageParams(int streamType, int sampleRate,
             int audioFormat, int channelCount,
-            float volume, float pan, UtteranceCompletedDispatcher dispatcher,
+            float volume, float pan, UtteranceProgressDispatcher dispatcher,
             String callingApp, EventLogger logger) {
         super(dispatcher, callingApp);
 
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index fdc2570..38699ea 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -482,7 +482,7 @@
     private OnInitListener mInitListener;
     // Written from an unspecified application thread, read from
     // a binder thread.
-    private volatile OnUtteranceCompletedListener mUtteranceCompletedListener;
+    private volatile UtteranceProgressListener mUtteranceProgressListener;
     private final Object mStartLock = new Object();
 
     private String mRequestedEngine;
@@ -1146,9 +1146,28 @@
      * @param listener The listener to use.
      *
      * @return {@link #ERROR} or {@link #SUCCESS}.
+     *
+     * @deprecated Use {@link #setOnUtteranceProgressListener(UtteranceProgressListener)}
+     *        instead.
      */
+    @Deprecated
     public int setOnUtteranceCompletedListener(final OnUtteranceCompletedListener listener) {
-        mUtteranceCompletedListener = listener;
+        mUtteranceProgressListener = UtteranceProgressListener.from(listener);
+        return TextToSpeech.SUCCESS;
+    }
+
+    /**
+     * Sets the listener that will be notified of various events related to the
+     * synthesis of a given utterance.
+     *
+     * See {@link UtteranceProgressListener} and
+     * {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}.
+     *
+     * @param listener the listener to use.
+     * @return {@link #ERROR} or {@link #SUCCESS}
+     */
+    public int setOnUtteranceProgressListener(UtteranceProgressListener listener) {
+        mUtteranceProgressListener = listener;
         return TextToSpeech.SUCCESS;
     }
 
@@ -1204,10 +1223,26 @@
         private ITextToSpeechService mService;
         private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() {
             @Override
-            public void utteranceCompleted(String utteranceId) {
-                OnUtteranceCompletedListener listener = mUtteranceCompletedListener;
+            public void onDone(String utteranceId) {
+                UtteranceProgressListener listener = mUtteranceProgressListener;
                 if (listener != null) {
-                    listener.onUtteranceCompleted(utteranceId);
+                    listener.onDone(utteranceId);
+                }
+            }
+
+            @Override
+            public void onError(String utteranceId) {
+                UtteranceProgressListener listener = mUtteranceProgressListener;
+                if (listener != null) {
+                    listener.onError(utteranceId);
+                }
+            }
+
+            @Override
+            public void onStart(String utteranceId) {
+                UtteranceProgressListener listener = mUtteranceProgressListener;
+                if (listener != null) {
+                    listener.onStart(utteranceId);
                 }
             }
         };
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 83b6d4c..39922da 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -306,6 +306,7 @@
          */
         public int enqueueSpeechItem(int queueMode, final SpeechItem speechItem) {
             if (!speechItem.isValid()) {
+                speechItem.dispatchOnError();
                 return TextToSpeech.ERROR;
             }
 
@@ -332,6 +333,7 @@
                 return TextToSpeech.SUCCESS;
             } else {
                 Log.w(TAG, "SynthThread has quit");
+                speechItem.dispatchOnError();
                 return TextToSpeech.ERROR;
             }
         }
@@ -381,14 +383,16 @@
         }
     }
 
-    interface UtteranceCompletedDispatcher {
-        public void dispatchUtteranceCompleted();
+    interface UtteranceProgressDispatcher {
+        public void dispatchOnDone();
+        public void dispatchOnStart();
+        public void dispatchOnError();
     }
 
     /**
      * An item in the synth thread queue.
      */
-    private abstract class SpeechItem implements UtteranceCompletedDispatcher {
+    private abstract class SpeechItem implements UtteranceProgressDispatcher {
         private final String mCallingApp;
         protected final Bundle mParams;
         private boolean mStarted = false;
@@ -443,10 +447,27 @@
             stopImpl();
         }
 
-        public void dispatchUtteranceCompleted() {
+        @Override
+        public void dispatchOnDone() {
             final String utteranceId = getUtteranceId();
             if (!TextUtils.isEmpty(utteranceId)) {
-                mCallbacks.dispatchUtteranceCompleted(getCallingApp(), utteranceId);
+                mCallbacks.dispatchOnDone(getCallingApp(), utteranceId);
+            }
+        }
+
+        @Override
+        public void dispatchOnStart() {
+            final String utteranceId = getUtteranceId();
+            if (!TextUtils.isEmpty(utteranceId)) {
+                mCallbacks.dispatchOnStart(getCallingApp(), utteranceId);
+            }
+        }
+
+        @Override
+        public void dispatchOnError() {
+            final String utteranceId = getUtteranceId();
+            if (!TextUtils.isEmpty(utteranceId)) {
+                mCallbacks.dispatchOnError(getCallingApp(), utteranceId);
             }
         }
 
@@ -617,9 +638,12 @@
 
         @Override
         protected int playImpl() {
+            dispatchOnStart();
             int status = super.playImpl();
             if (status == TextToSpeech.SUCCESS) {
-                dispatchUtteranceCompleted();
+                dispatchOnDone();
+            } else {
+                dispatchOnError();
             }
             return status;
         }
@@ -856,16 +880,34 @@
             }
         }
 
-        public void dispatchUtteranceCompleted(String packageName, String utteranceId) {
-            ITextToSpeechCallback cb;
-            synchronized (mAppToCallback) {
-                cb = mAppToCallback.get(packageName);
-            }
+        public void dispatchOnDone(String packageName, String utteranceId) {
+            ITextToSpeechCallback cb = getCallbackFor(packageName);
             if (cb == null) return;
             try {
-                cb.utteranceCompleted(utteranceId);
+                cb.onDone(utteranceId);
             } catch (RemoteException e) {
-                Log.e(TAG, "Callback failed: " + e);
+                Log.e(TAG, "Callback onDone failed: " + e);
+            }
+        }
+
+        public void dispatchOnStart(String packageName, String utteranceId) {
+            ITextToSpeechCallback cb = getCallbackFor(packageName);
+            if (cb == null) return;
+            try {
+                cb.onStart(utteranceId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Callback onStart failed: " + e);
+            }
+
+        }
+
+        public void dispatchOnError(String packageName, String utteranceId) {
+            ITextToSpeechCallback cb = getCallbackFor(packageName);
+            if (cb == null) return;
+            try {
+                cb.onError(utteranceId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Callback onError failed: " + e);
             }
         }
 
@@ -886,6 +928,15 @@
             }
         }
 
+        private ITextToSpeechCallback getCallbackFor(String packageName) {
+            ITextToSpeechCallback cb;
+            synchronized (mAppToCallback) {
+                cb = mAppToCallback.get(packageName);
+            }
+
+            return cb;
+        }
+
     }
 
 }
diff --git a/core/java/android/speech/tts/UtteranceProgressListener.java b/core/java/android/speech/tts/UtteranceProgressListener.java
new file mode 100644
index 0000000..a04458a
--- /dev/null
+++ b/core/java/android/speech/tts/UtteranceProgressListener.java
@@ -0,0 +1,68 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package android.speech.tts;
+
+/**
+ * Listener for events relating to the progress of an utterance through
+ * the synthesis queue. Each utterance is associated with a call to
+ * {@link TextToSpeech#speak} or {@link TextToSpeech#synthesizeToFile} with an
+ * associated utterance identifier, as per {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}.
+ *
+ * The callbacks specified in this method can be called from multiple threads.
+ */
+public abstract class UtteranceProgressListener {
+    /**
+     * Called when an utterance "starts" as perceived by the caller. This will
+     * be soon before audio is played back in the case of a {@link TextToSpeech#speak}
+     * or before the first bytes of a file are written to storage in the case
+     * of {@link TextToSpeech#synthesizeToFile}.
+     *
+     * @param utteranceId the utterance ID of the utterance.
+     */
+    public abstract void onStart(String utteranceId);
+
+    /**
+     * Called when an utterance has successfully completed processing.
+     * All audio will have been played back by this point for audible output, and all
+     * output will have been written to disk for file synthesis requests.
+     *
+     * This request is guaranteed to be called after {@link #onStart(String)}.
+     *
+     * @param utteranceId the utterance ID of the utterance.
+     */
+    public abstract void onDone(String utteranceId);
+
+    /**
+     * Called when an error has occurred during processing. This can be called
+     * at any point in the synthesis process. Note that there might be calls
+     * to {@link #onStart(String)} for specified utteranceId but there will never
+     * be a call to both {@link #onDone(String)} and {@link #onError(String)} for
+     * the same utterance.
+     *
+     * @param utteranceId the utterance ID of the utterance.
+     */
+    public abstract void onError(String utteranceId);
+
+    /**
+     * Wraps an old deprecated OnUtteranceCompletedListener with a shiny new
+     * progress listener.
+     *
+     * @hide
+     */
+    static UtteranceProgressListener from(
+            final TextToSpeech.OnUtteranceCompletedListener listener) {
+        return new UtteranceProgressListener() {
+            @Override
+            public synchronized void onDone(String utteranceId) {
+                listener.onUtteranceCompleted(utteranceId);
+            }
+
+            // The following methods are left unimplemented.
+            @Override
+            public void onStart(String utteranceId) { }
+
+            @Override
+            public void onError(String utteranceId) { }
+        };
+    }
+}
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 353b628..22a7528 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -136,12 +136,12 @@
     private static java.text.DateFormat sStatusTimeFormat;
     private static String sElapsedFormatMMSS;
     private static String sElapsedFormatHMMSS;
-    
+
     private static final String FAST_FORMAT_HMMSS = "%1$d:%2$02d:%3$02d";
     private static final String FAST_FORMAT_MMSS = "%1$02d:%2$02d";
     private static final char TIME_PADDING = '0';
     private static final char TIME_SEPARATOR = ':';
-    
+
 
     public static final long SECOND_IN_MILLIS = 1000;
     public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
@@ -201,7 +201,7 @@
     public static final String YEAR_FORMAT_TWO_DIGITS = "%g";
     public static final String WEEKDAY_FORMAT = "%A";
     public static final String ABBREV_WEEKDAY_FORMAT = "%a";
-    
+
     // This table is used to lookup the resource string id of a format string
     // used for formatting a start and end date that fall in the same year.
     // The index is constructed from a bit-wise OR of the boolean values:
@@ -227,7 +227,7 @@
         com.android.internal.R.string.numeric_mdy1_time1_mdy2_time2,
         com.android.internal.R.string.numeric_wday1_mdy1_time1_wday2_mdy2_time2,
     };
-    
+
     // This table is used to lookup the resource string id of a format string
     // used for formatting a start and end date that fall in the same month.
     // The index is constructed from a bit-wise OR of the boolean values:
@@ -256,7 +256,7 @@
     /**
      * Request the full spelled-out name. For use with the 'abbrev' parameter of
      * {@link #getDayOfWeekString} and {@link #getMonthString}.
-     * 
+     *
      * @more <p>
      *       e.g. "Sunday" or "January"
      */
@@ -265,7 +265,7 @@
     /**
      * Request an abbreviated version of the name. For use with the 'abbrev'
      * parameter of {@link #getDayOfWeekString} and {@link #getMonthString}.
-     * 
+     *
      * @more <p>
      *       e.g. "Sun" or "Jan"
      */
@@ -347,7 +347,7 @@
      * @return Localized month of the year.
      */
     public static String getMonthString(int month, int abbrev) {
-        // Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER. 
+        // Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER.
         // This is a shortcut to not spam the translators with too many variations
         // of the same string.  If we find that in a language the distinction
         // is necessary, we can can add more without changing this API.
@@ -380,7 +380,7 @@
      * @hide Pending API council approval
      */
     public static String getStandaloneMonthString(int month, int abbrev) {
-        // Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER. 
+        // Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER.
         // This is a shortcut to not spam the translators with too many variations
         // of the same string.  If we find that in a language the distinction
         // is necessary, we can can add more without changing this API.
@@ -434,7 +434,7 @@
      * <p>
      * Can use {@link #FORMAT_ABBREV_RELATIVE} flag to use abbreviated relative
      * times, like "42 mins ago".
-     * 
+     *
      * @param time the time to describe, in milliseconds
      * @param now the current time in milliseconds
      * @param minResolution the minimum timespan to report. For example, a time
@@ -450,7 +450,7 @@
             int flags) {
         Resources r = Resources.getSystem();
         boolean abbrevRelative = (flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0;
-        
+
         boolean past = (now >= time);
         long duration = Math.abs(now - time);
 
@@ -525,7 +525,7 @@
         String format = r.getQuantityString(resId, (int) count);
         return String.format(format, count);
     }
-    
+
     /**
      * Returns the number of days passed between two dates.
      *
@@ -555,7 +555,7 @@
      * <li>Dec 12, 4:12 AM</li>
      * <li>11/14/2007, 8:20 AM</li>
      * </ul>
-     * 
+     *
      * @param time some time in the past.
      * @param minResolution the minimum elapsed time (in milliseconds) to report
      *            when showing relative times. For example, a time 3 seconds in
@@ -584,7 +584,7 @@
         }
 
         CharSequence timeClause = formatDateRange(c, time, time, FORMAT_SHOW_TIME);
-        
+
         String result;
         if (duration < transitionResolution) {
             CharSequence relativeClause = getRelativeTimeSpanString(time, now, minResolution, flags);
@@ -601,7 +601,7 @@
      * Returns a string describing a day relative to the current day. For example if the day is
      * today this function returns "Today", if the day was a week ago it returns "7 days ago", and
      * if the day is in 2 weeks it returns "in 14 days".
-     * 
+     *
      * @param r the resources to get the strings from
      * @param day the relative day to describe in UTC milliseconds
      * @param today the current time in UTC milliseconds
@@ -618,7 +618,7 @@
 
         int days = Math.abs(currentDay - startDay);
         boolean past = (today > day);
-        
+
         if (days == 1) {
             if (past) {
                 return r.getString(com.android.internal.R.string.yesterday);
@@ -635,7 +635,7 @@
         } else {
             resId = com.android.internal.R.plurals.in_num_days;
         }
-        
+
         String format = r.getQuantityString(resId, days);
         return String.format(format, days);
     }
@@ -677,11 +677,11 @@
     public static String formatElapsedTime(long elapsedSeconds) {
         return formatElapsedTime(null, elapsedSeconds);
     }
-    
+
     /**
      * Formats an elapsed time in the form "MM:SS" or "H:MM:SS"
      * for display on the call-in-progress screen.
-     * 
+     *
      * @param recycle {@link StringBuilder} to recycle, if possible
      * @param elapsedSeconds the elapsed time in seconds.
      */
@@ -724,7 +724,7 @@
             }
             sb.append(hours);
             sb.append(TIME_SEPARATOR);
-            if (minutes < 10) { 
+            if (minutes < 10) {
                 sb.append(TIME_PADDING);
             } else {
                 sb.append(toDigitChar(minutes / 10));
@@ -755,7 +755,7 @@
             } else {
                 sb.setLength(0);
             }
-            if (minutes < 10) { 
+            if (minutes < 10) {
                 sb.append(TIME_PADDING);
             } else {
                 sb.append(toDigitChar(minutes / 10));
@@ -777,11 +777,11 @@
     private static char toDigitChar(long digit) {
         return (char) (digit + '0');
     }
-    
+
     /**
      * Format a date / time such that if the then is on the same day as now, it shows
      * just the time and if it's a different day, it shows just the date.
-     * 
+     *
      * <p>The parameters dateFormat and timeFormat should each be one of
      * {@link java.text.DateFormat#DEFAULT},
      * {@link java.text.DateFormat#FULL},
@@ -833,14 +833,14 @@
     public static boolean isToday(long when) {
         Time time = new Time();
         time.set(when);
-        
+
         int thenYear = time.year;
         int thenMonth = time.month;
         int thenMonthDay = time.monthDay;
 
         time.set(System.currentTimeMillis());
         return (thenYear == time.year)
-                && (thenMonth == time.month) 
+                && (thenMonth == time.month)
                 && (thenMonthDay == time.monthDay);
     }
 
@@ -914,7 +914,7 @@
     public static String writeDateTime(Calendar cal, StringBuilder sb)
     {
         int n;
-       
+
         n = cal.get(Calendar.YEAR);
         sb.setCharAt(3, (char)('0'+n%10));
         n /= 10;
@@ -1015,7 +1015,7 @@
 
     /**
      * Formats a date or a time range according to the local conventions.
-     * 
+     *
      * <p>
      * Example output strings (date formats in these examples are shown using
      * the US date format convention but that may change depending on the
@@ -1036,10 +1036,10 @@
      *   <li>Oct 9, 8:00am - Oct 10, 5:00pm</li>
      *   <li>12/31/2007 - 01/01/2008</li>
      * </ul>
-     * 
+     *
      * <p>
      * The flags argument is a bitmask of options from the following list:
-     * 
+     *
      * <ul>
      *   <li>FORMAT_SHOW_TIME</li>
      *   <li>FORMAT_SHOW_WEEKDAY</li>
@@ -1061,15 +1061,15 @@
      *   <li>FORMAT_ABBREV_ALL</li>
      *   <li>FORMAT_NUMERIC_DATE</li>
      * </ul>
-     * 
+     *
      * <p>
      * If FORMAT_SHOW_TIME is set, the time is shown as part of the date range.
      * If the start and end time are the same, then just the start time is
      * shown.
-     * 
+     *
      * <p>
      * If FORMAT_SHOW_WEEKDAY is set, then the weekday is shown.
-     * 
+     *
      * <p>
      * If FORMAT_SHOW_YEAR is set, then the year is always shown.
      * If FORMAT_NO_YEAR is set, then the year is not shown.
@@ -1082,80 +1082,91 @@
      * Normally the date is shown unless the start and end day are the same.
      * If FORMAT_SHOW_DATE is set, then the date is always shown, even for
      * same day ranges.
-     * 
+     *
      * <p>
      * If FORMAT_NO_MONTH_DAY is set, then if the date is shown, just the
      * month name will be shown, not the day of the month.  For example,
      * "January, 2008" instead of "January 6 - 12, 2008".
-     * 
+     *
      * <p>
      * If FORMAT_CAP_AMPM is set and 12-hour time is used, then the "AM"
      * and "PM" are capitalized.  You should not use this flag
      * because in some locales these terms cannot be capitalized, and in
      * many others it doesn't make sense to do so even though it is possible.
-     * 
+     *
      * <p>
      * If FORMAT_NO_NOON is set and 12-hour time is used, then "12pm" is
      * shown instead of "noon".
-     * 
+     *
      * <p>
      * If FORMAT_CAP_NOON is set and 12-hour time is used, then "Noon" is
      * shown instead of "noon".  You should probably not use this flag
      * because in many locales it will not make sense to capitalize
      * the term.
-     * 
+     *
      * <p>
      * If FORMAT_NO_MIDNIGHT is set and 12-hour time is used, then "12am" is
      * shown instead of "midnight".
-     * 
+     *
      * <p>
      * If FORMAT_CAP_MIDNIGHT is set and 12-hour time is used, then "Midnight"
      * is shown instead of "midnight".  You should probably not use this
      * flag because in many locales it will not make sense to capitalize
      * the term.
-     * 
+     *
      * <p>
      * If FORMAT_12HOUR is set and the time is shown, then the time is
      * shown in the 12-hour time format. You should not normally set this.
      * Instead, let the time format be chosen automatically according to the
      * system settings. If both FORMAT_12HOUR and FORMAT_24HOUR are set, then
      * FORMAT_24HOUR takes precedence.
-     * 
+     *
      * <p>
      * If FORMAT_24HOUR is set and the time is shown, then the time is
      * shown in the 24-hour time format. You should not normally set this.
      * Instead, let the time format be chosen automatically according to the
      * system settings. If both FORMAT_12HOUR and FORMAT_24HOUR are set, then
      * FORMAT_24HOUR takes precedence.
-     * 
+     *
      * <p>
      * If FORMAT_UTC is set, then the UTC time zone is used for the start
      * and end milliseconds unless a time zone is specified. If a time zone
      * is specified it will be used regardless of the FORMAT_UTC flag.
-     * 
+     *
      * <p>
      * If FORMAT_ABBREV_TIME is set and 12-hour time format is used, then the
      * start and end times (if shown) are abbreviated by not showing the minutes
      * if they are zero.  For example, instead of "3:00pm" the time would be
      * abbreviated to "3pm".
-     * 
+     *
      * <p>
      * If FORMAT_ABBREV_WEEKDAY is set, then the weekday (if shown) is
      * abbreviated to a 3-letter string.
-     * 
+     *
      * <p>
      * If FORMAT_ABBREV_MONTH is set, then the month (if shown) is abbreviated
      * to a 3-letter string.
-     * 
+     *
      * <p>
      * If FORMAT_ABBREV_ALL is set, then the weekday and the month (if shown)
      * are abbreviated to 3-letter strings.
-     * 
+     *
      * <p>
      * If FORMAT_NUMERIC_DATE is set, then the date is shown in numeric format
      * instead of using the name of the month.  For example, "12/31/2008"
      * instead of "December 31, 2008".
-     * 
+     *
+     * <p>
+     * If the end date ends at 12:00am at the beginning of a day, it is
+     * formatted as the end of the previous day in two scenarios:
+     * <ul>
+     *   <li>For single day events. This results in "8pm - midnight" instead of
+     *       "Nov 10, 8pm - Nov 11, 12am".</li>
+     *   <li>When the time is not displayed. This results in "Nov 10 - 11" for
+     *       an event with a start date of Nov 10 and an end date of Nov 12 at
+     *       00:00.</li>
+     * </ul>
+     *
      * @param context the context is required only if the time is shown
      * @param formatter the Formatter used for formatting the date range.
      * Note: be sure to call setLength(0) on StringBuilder passed to
@@ -1165,7 +1176,7 @@
      * @param flags a bit mask of options
      * @param timeZone the time zone to compute the string in. Use null for local
      * or if the FORMAT_UTC flag is being used.
-     *  
+     *
      * @return the formatter with the formatted date/time range appended to the string buffer.
      */
     public static Formatter formatDateRange(Context context, Formatter formatter, long startMillis,
@@ -1215,20 +1226,6 @@
             dayDistance = endJulianDay - startJulianDay;
         }
 
-        // If the end date ends at 12am at the beginning of a day,
-        // then modify it to make it look like it ends at midnight on
-        // the previous day.  This will allow us to display "8pm - midnight",
-        // for example, instead of "Nov 10, 8pm - Nov 11, 12am". But we only do
-        // this if it is midnight of the same day as the start date because
-        // for multiple-day events, an end time of "midnight on Nov 11" is
-        // ambiguous and confusing (is that midnight the start of Nov 11, or
-        // the end of Nov 11?).
-        // If we are not showing the time then also adjust the end date
-        // for multiple-day events.  This is to allow us to display, for
-        // example, "Nov 10 -11" for an event with a start date of Nov 10
-        // and an end date of Nov 12 at 00:00.
-        // If the start and end time are the same, then skip this and don't
-        // adjust the date.
         if (!isInstant
             && (endDate.hour | endDate.minute | endDate.second) == 0
             && (!showTime || dayDistance <= 1)) {
@@ -1592,7 +1589,7 @@
      *   <li>Wed, October 31</li>
      *   <li>10/31/2007</li>
      * </ul>
-     * 
+     *
      * @param context the context is required only if the time is shown
      * @param millis a point in time in UTC milliseconds
      * @param flags a bit mask of formatting options
@@ -1607,13 +1604,13 @@
      * are counted starting at midnight, which means that assuming that the current
      * time is March 31st, 0:30:
      * <ul>
-     *   <li>"millis=0:10 today" will be displayed as "0:10"</li> 
+     *   <li>"millis=0:10 today" will be displayed as "0:10"</li>
      *   <li>"millis=11:30pm the day before" will be displayed as "Mar 30"</li>
      * </ul>
      * If the given millis is in a different year, then the full date is
      * returned in numeric format (e.g., "10/12/2008").
-     * 
-     * @param withPreposition If true, the string returned will include the correct 
+     *
+     * @param withPreposition If true, the string returned will include the correct
      * preposition ("at 9:20am", "on 10/12/2008" or "on May 29").
      */
     public static CharSequence getRelativeTimeSpanString(Context c, long millis,
@@ -1661,9 +1658,9 @@
         }
         return result;
     }
-    
+
     /**
-     * Convenience function to return relative time string without preposition. 
+     * Convenience function to return relative time string without preposition.
      * @param c context for resources
      * @param millis time in milliseconds
      * @return {@link CharSequence} containing relative time.
@@ -1672,7 +1669,7 @@
     public static CharSequence getRelativeTimeSpanString(Context c, long millis) {
         return getRelativeTimeSpanString(c, millis, false /* no preposition */);
     }
-    
+
     private static Time sNowTime;
     private static Time sThenTime;
 }
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 9a39345..67d9033 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -315,6 +315,27 @@
 
     private static native void nFlushCaches(int level);
 
+    /**
+     * Release all resources associated with the underlying caches. This should
+     * only be called after a full flushCaches().
+     * 
+     * @hide
+     */
+    public static void terminateCaches() {
+        nTerminateCaches();
+    }
+
+    private static native void nTerminateCaches();
+
+    /**
+     * @hide
+     */
+    public static void initCaches() {
+        nInitCaches();
+    }
+
+    private static native void nInitCaches();
+    
     ///////////////////////////////////////////////////////////////////////////
     // Display list
     ///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index c2ac79d..e0167d8 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -25,6 +25,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.util.Log;
+import com.google.android.gles_jni.EGLImpl;
 
 import javax.microedition.khronos.egl.EGL10;
 import javax.microedition.khronos.egl.EGL11;
@@ -344,6 +345,15 @@
     }
 
     /**
+     * Invoke this method when the system needs to clean up all resources
+     * associated with hardware rendering.
+     */
+    static void terminate() {
+        Log.d(LOG_TAG, "Terminating hardware rendering");
+        Gl20Renderer.terminate();
+    }    
+    
+    /**
      * Indicates whether hardware acceleration is currently enabled.
      * 
      * @return True if hardware acceleration is in use, false otherwise.
@@ -651,6 +661,8 @@
                 throw new Surface.OutOfResourcesException("eglMakeCurrent failed "
                         + GLUtils.getEGLErrorString(sEgl.eglGetError()));
             }
+            
+            initCaches();
 
             // If mDirtyRegions is set, this means we have an EGL configuration
             // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
@@ -671,6 +683,8 @@
             return mEglContext.getGL();
         }
 
+        abstract void initCaches();
+
         EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
             int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE };
 
@@ -914,6 +928,11 @@
                     EGL_NONE
             };
         }
+        
+        @Override
+        void initCaches() {
+            GLES20Canvas.initCaches();
+        }
 
         @Override
         boolean canDraw() {
@@ -1006,16 +1025,7 @@
             if (eglContext == null) {
                 return;
             } else {
-                synchronized (sPbufferLock) {
-                    // Create a temporary 1x1 pbuffer so we have a context
-                    // to clear our OpenGL objects
-                    if (sPbuffer == null) {
-                        sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
-                                EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
-                        });
-                    }
-                }
-                sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
+                usePbufferSurface(eglContext);
             }
 
             switch (level) {
@@ -1029,5 +1039,46 @@
                     break;
             }
         }
+
+        private static void usePbufferSurface(EGLContext eglContext) {
+            synchronized (sPbufferLock) {
+                // Create a temporary 1x1 pbuffer so we have a context
+                // to clear our OpenGL objects
+                if (sPbuffer == null) {
+                    sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
+                            EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
+                    });
+                }
+            }
+            sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
+        }
+
+        static void terminate() {
+            synchronized (sEglLock) {
+                if (sEgl == null) return;
+    
+                if (EGLImpl.getInitCount(sEglDisplay) == 1) {
+                    EGLContext eglContext = sEglContextStorage.get();
+                    if (eglContext == null) return;
+
+                    usePbufferSurface(eglContext);
+                    GLES20Canvas.terminateCaches();
+
+                    sEgl.eglDestroyContext(sEglDisplay, eglContext);
+                    sEglContextStorage.remove();
+        
+                    sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
+                    sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+                    sEgl.eglReleaseThread();
+                    sEgl.eglTerminate(sEglDisplay);
+                    
+                    sEgl = null;
+                    sEglDisplay = null;
+                    sEglConfig = null;
+                    sPbuffer = null;
+                }
+            }
+        }
     }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ec25b64..723846a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -568,7 +568,7 @@
         }
     }
 
-    private void destroyHardwareResources() {
+    void destroyHardwareResources() {
         if (mAttachInfo.mHardwareRenderer != null) {
             if (mAttachInfo.mHardwareRenderer.isEnabled()) {
                 mAttachInfo.mHardwareRenderer.destroyLayers(mView);
@@ -881,12 +881,10 @@
                 || mNewSurfaceNeeded;
 
         WindowManager.LayoutParams params = null;
-        int windowAttributesChanges = 0;
         if (mWindowAttributesChanged) {
             mWindowAttributesChanged = false;
             surfaceChanged = true;
             params = lp;
-            windowAttributesChanges = mWindowAttributesChangesFlag;
         }
         CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
         if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 5ef4f3e..d89bc36 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.app.ActivityManager;
+import android.content.ComponentCallbacks2;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.PixelFormat;
@@ -409,7 +411,30 @@
      */
     public void trimMemory(int level) {
         if (HardwareRenderer.isAvailable()) {
-            HardwareRenderer.trimMemory(level);
+            switch (level) {
+                case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
+                case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
+                    // On low and medium end gfx devices
+                    if (!ActivityManager.isHighEndGfx(getDefaultDisplay())) {
+                        // Force a full memory flush
+                        HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
+                        // Destroy all hardware surfaces and resources associated to
+                        // known windows
+                        synchronized (this) {
+                            if (mViews == null) return;
+                            int count = mViews.length;
+                            for (int i = 0; i < count; i++) {
+                                mRoots[i].destroyHardwareResources();
+                            }
+                        }
+                        // Terminate the hardware renderer to free all resources
+                        HardwareRenderer.terminate();                        
+                        break;
+                    }
+                    // high end gfx devices fall through to next case
+                default:
+                    HardwareRenderer.trimMemory(level);
+            }
         }
     }
 
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index b020cbc..596cd10 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2861,8 +2861,8 @@
     }
 
     // Used to avoid sending many visible rect messages.
-    private Rect mLastVisibleRectSent;
-    private Rect mLastGlobalRect;
+    private Rect mLastVisibleRectSent = new Rect();
+    private Rect mLastGlobalRect = new Rect();
     private Rect mVisibleRect = new Rect();
     private Rect mGlobalVisibleRect = new Rect();
     private Point mScrollOffset = new Point();
@@ -2878,7 +2878,7 @@
                 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
                         nativeMoveGeneration(), mSendScrollEvent ? 1 : 0, mScrollOffset);
             }
-            mLastVisibleRectSent = mVisibleRect;
+            mLastVisibleRectSent.set(mVisibleRect);
             mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
         }
         if (getGlobalVisibleRect(mGlobalVisibleRect)
@@ -2894,7 +2894,7 @@
             if (!mBlockWebkitViewMessages) {
                 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, mGlobalVisibleRect);
             }
-            mLastGlobalRect = mGlobalVisibleRect;
+            mLastGlobalRect.set(mGlobalVisibleRect);
         }
         return mVisibleRect;
     }
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index fd2abc2..326587e 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -129,7 +129,7 @@
         mEdge = res.getDrawable(R.drawable.overscroll_edge);
         mGlow = res.getDrawable(R.drawable.overscroll_glow);
 
-        mMinWidth = (int) (context.getResources().getDisplayMetrics().density * MIN_WIDTH + 0.5f);
+        mMinWidth = (int) (res.getDisplayMetrics().density * MIN_WIDTH + 0.5f);
         mInterpolator = new DecelerateInterpolator();
     }
 
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index c1e36ed..73e1273 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -1036,6 +1036,7 @@
         }
     }
 
+    @RemotableViewMethod
     @Override
     public void setVisibility(int visibility) {
         super.setVisibility(visibility);
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 8ba7bee..5fa4ad0 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1248,6 +1248,8 @@
      */
     public void dismiss() {
         if (isShowing() && mPopupView != null) {
+            mIsShowing = false;
+
             unregisterForScrollChanged();
 
             try {
@@ -1257,7 +1259,6 @@
                     ((ViewGroup) mPopupView).removeView(mContentView);
                 }
                 mPopupView = null;
-                mIsShowing = false;
     
                 if (mOnDismissListener != null) {
                     mOnDismissListener.onDismiss();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6638ea6..0dd55bf 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -449,18 +450,19 @@
         super(context, attrs, defStyle);
         mText = "";
 
+        final Resources res = getResources();
+        final CompatibilityInfo compat = res.getCompatibilityInfo();
+
         mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
-        mTextPaint.density = getResources().getDisplayMetrics().density;
-        mTextPaint.setCompatibilityScaling(
-                getResources().getCompatibilityInfo().applicationScale);
+        mTextPaint.density = res.getDisplayMetrics().density;
+        mTextPaint.setCompatibilityScaling(compat.applicationScale);
 
         // If we get the paint from the skin, we should set it to left, since
         // the layout always wants it to be left.
         // mTextPaint.setTextAlign(Paint.Align.LEFT);
 
         mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mHighlightPaint.setCompatibilityScaling(
-                getResources().getCompatibilityInfo().applicationScale);
+        mHighlightPaint.setCompatibilityScaling(compat.applicationScale);
 
         mMovement = getDefaultMovementMethod();
         mTransformation = null;
@@ -6097,18 +6099,13 @@
         int physicalWidth = width;
 
         if (mHorizontallyScrolling) {
-            width = getTextWidth();
+            width = VERY_WIDE;
         }
 
         makeNewLayout(width, physicalWidth, UNKNOWN_BORING, UNKNOWN_BORING,
                       physicalWidth, false);
     }
 
-    private int getTextWidth() {
-        final int length = mText.length();
-        return (length == 0) ? 0 : (int) (getPaint().measureText(mText, 0, length) + 0.5f);
-    }
-
     @Override
     protected void resetResolvedLayoutDirection() {
         super.resetResolvedLayoutDirection();
@@ -6554,7 +6551,7 @@
         int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight();
         int unpaddedWidth = want;
 
-        if (mHorizontallyScrolling) want = getTextWidth();
+        if (mHorizontallyScrolling) want = VERY_WIDE;
 
         int hintWant = want;
         int hintWidth = (mHintLayout == null) ? hintWant : mHintLayout.getWidth();
@@ -11544,6 +11541,9 @@
     private boolean                 mHighlightPathBogus = true;
     private static final RectF      sTempRect = new RectF();
 
+    // XXX should be much larger
+    private static final int        VERY_WIDE = 1024*1024;
+
     private static final int        BLINK = 500;
 
     private static final int ANIMATED_SCROLL_GAP = 250;
diff --git a/core/jni/android_nfc_NdefRecord.cpp b/core/jni/android_nfc_NdefRecord.cpp
index e8cc4c6..67907b6 100644
--- a/core/jni/android_nfc_NdefRecord.cpp
+++ b/core/jni/android_nfc_NdefRecord.cpp
@@ -149,7 +149,7 @@
 
     /* Set flags field */
     mFlags = e->GetFieldID(record_cls, "mFlags", "B");
-    e->SetIntField(o, mFlags, record.Flags);
+    e->SetByteField(o, mFlags, record.Flags);
 
     ret = 0;
 
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index e79de2d..4f75fad 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -134,6 +134,18 @@
     }
 }
 
+static void android_view_GLES20Canvas_initCaches(JNIEnv* env, jobject clazz) {
+    if (Caches::hasInstance()) {
+        Caches::getInstance().init();
+    }
+}
+
+static void android_view_GLES20Canvas_terminateCaches(JNIEnv* env, jobject clazz) {
+    if (Caches::hasInstance()) {
+        Caches::getInstance().terminate();
+    }
+}
+
 // ----------------------------------------------------------------------------
 // Constructors
 // ----------------------------------------------------------------------------
@@ -756,6 +768,8 @@
     { "nPreserveBackBuffer",    "()Z",         (void*) android_view_GLES20Canvas_preserveBackBuffer },
     { "nDisableVsync",          "()V",         (void*) android_view_GLES20Canvas_disableVsync },
     { "nFlushCaches",           "(I)V",        (void*) android_view_GLES20Canvas_flushCaches },
+    { "nInitCaches",            "()V",         (void*) android_view_GLES20Canvas_initCaches },
+    { "nTerminateCaches",       "()V",         (void*) android_view_GLES20Canvas_terminateCaches },
 
     { "nCreateRenderer",    "()I",             (void*) android_view_GLES20Canvas_createRenderer },
     { "nDestroyRenderer",   "(I)V",            (void*) android_view_GLES20Canvas_destroyRenderer },
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 02974f9a..4fe7600 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -24,6 +24,8 @@
 #include <EGL/egl.h>
 #include <GLES/gl.h>
 
+#include <EGL/egl_display.h>
+
 #include <surfaceflinger/Surface.h>
 #include <SkBitmap.h>
 #include <SkPixelRef.h>
@@ -173,6 +175,16 @@
     return success;
 }
 
+static jint jni_getInitCount(JNIEnv *_env, jobject _clazz, jobject display) {
+    EGLDisplay dpy = getDisplay(_env, display);
+    egl_display_t* eglDisplay = get_display(dpy); 
+    return eglDisplay ? eglDisplay->getRefsCount() : 0;
+}
+
+static jboolean jni_eglReleaseThread(JNIEnv *_env, jobject _this) {
+    return eglReleaseThread();
+}
+
 static jboolean jni_eglChooseConfig(JNIEnv *_env, jobject _this, jobject display,
         jintArray attrib_list, jobjectArray configs, jint config_size, jintArray num_config) {
     if (display == NULL
@@ -526,6 +538,8 @@
 {"eglInitialize",   "(" DISPLAY "[I)Z", (void*)jni_eglInitialize },
 {"eglQueryContext", "(" DISPLAY CONTEXT "I[I)Z", (void*)jni_eglQueryContext },
 {"eglQuerySurface", "(" DISPLAY SURFACE "I[I)Z", (void*)jni_eglQuerySurface },
+{"eglReleaseThread","()Z", (void*)jni_eglReleaseThread },
+{"getInitCount",    "(" DISPLAY ")I", (void*)jni_getInitCount },
 {"eglChooseConfig", "(" DISPLAY "[I[" CONFIG "I[I)Z", (void*)jni_eglChooseConfig },
 {"_eglCreateContext","(" DISPLAY CONFIG CONTEXT "[I)I", (void*)jni_eglCreateContext },
 {"eglGetConfigs",   "(" DISPLAY "[" CONFIG "I[I)Z", (void*)jni_eglGetConfigs },
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 79acd55..380b3d8 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -604,10 +604,13 @@
         }
         Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true);
         if (config == Config.ARGB_8888 && !hasAlpha) {
-            bm.eraseColor(0xff000000);
+            nativeErase(bm.mNativeBitmap, 0xff000000);
             nativeSetHasAlpha(bm.mNativeBitmap, hasAlpha);
         } else {
-            bm.eraseColor(0);
+            // No need to initialize it to zeroes; it is backed by a VM byte array
+            // which is by definition preinitialized to all zeroes.
+            //
+            //nativeErase(bm.mNativeBitmap, 0);
         }
         return bm;
     }
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index 9aa6700..534afce 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -54,6 +54,7 @@
     METADATA_KEY_BITRATE         = 20,
     METADATA_KEY_TIMED_TEXT_LANGUAGES      = 21,
     METADATA_KEY_IS_DRM          = 22,
+    METADATA_KEY_LOCATION        = 23,
 
     // Add more here...
 };
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 57f678c..4cdee17 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -85,6 +85,7 @@
     kKeyDate              = 'date',  // cstring
     kKeyWriter            = 'writ',  // cstring
     kKeyCompilation       = 'cpil',  // cstring
+    kKeyLocation          = 'loc ',  // cstring
     kKeyTimeScale         = 'tmsl',  // int32_t
 
     // video profile and level
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 75b07de..f293cba 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -46,22 +46,16 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-Caches::Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO),
-        lastDstMode(GL_ZERO), currentProgram(NULL) {
+Caches::Caches(): Singleton<Caches>(), mInitialized(false) {
     GLint maxTextureUnits;
     glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
     if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
         LOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
     }
 
-    glGenBuffers(1, &meshBuffer);
-    glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
-
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
 
-    mCurrentBuffer = meshBuffer;
-    mRegionMesh = NULL;
+    init();
 
     mDebugLevel = readDebugLevel();
     LOGD("Enabling debug mode %d", mDebugLevel);
@@ -71,8 +65,40 @@
 #endif
 }
 
-Caches::~Caches() {
+void Caches::init() {
+    if (mInitialized) return;
+
+    glGenBuffers(1, &meshBuffer);
+    glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
+    glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
+
+    mCurrentBuffer = meshBuffer;
+    mRegionMesh = NULL;
+
+    blend = false;
+    lastSrcMode = GL_ZERO;
+    lastDstMode = GL_ZERO;
+    currentProgram = NULL;
+
+    mInitialized = true;
+}
+
+void Caches::terminate() {
+    if (!mInitialized) return;
+
+    glDeleteBuffers(1, &meshBuffer);
+    mCurrentBuffer = 0;
+
+    glDeleteBuffers(1, &mRegionMeshIndices);
     delete[] mRegionMesh;
+    mRegionMesh = NULL;
+
+    fboCache.clear();
+
+    programCache.clear();
+    currentProgram = NULL;
+
+    mInitialized = false;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 9b0d7c6..5e58a9e 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -86,7 +86,6 @@
 
 class ANDROID_API Caches: public Singleton<Caches> {
     Caches();
-    ~Caches();
 
     friend class Singleton<Caches>;
 
@@ -109,6 +108,11 @@
     };
 
     /**
+     * Initializes the cache.
+     */
+    void init();
+
+    /**
      * Flush the cache.
      *
      * @param mode Indicates how much of the cache should be flushed
@@ -116,6 +120,12 @@
     void flush(FlushMode mode);
 
     /**
+     * Destroys all resources associated with this cache. This should
+     * be called after a flush(kFlushMode_Full).
+     */
+    void terminate();
+
+    /**
      * Indicates whether the renderer is in debug mode.
      * This debug mode provides limited information to app developers.
      */
@@ -194,6 +204,7 @@
 
 private:
     DebugLevel mDebugLevel;
+    bool mInitialized;
 }; // class Caches
 
 }; // namespace uirenderer
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 10694c3..11ecd1f 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -458,5 +458,12 @@
      * @hide
      */
     public static final int METADATA_KEY_IS_DRM          = 22;
+    /**
+     * This key retrieves the location information, if available.
+     * The location should be specified according to ISO-6709 standard, under
+     * a mp4/3gp box "@xyz". Location with longitude of -90 degrees and latitude
+     * of 180 degrees will be retrieved as "-90.0000+180.0000", for instance.
+     */
+    public static final int METADATA_KEY_LOCATION        = 23;
     // Add more here...
 }
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 6db87fe..e9b8042 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -47,17 +47,22 @@
     VISUALIZER_STATE_ACTIVE,
 };
 
+// maximum number of reads from same buffer before resetting capture buffer. This means
+// that the framework has stopped playing audio and we must start returning silence
+#define MAX_STALL_COUNT 10
+
 struct VisualizerContext {
     const struct effect_interface_s *mItfe;
     effect_config_t mConfig;
-    uint32_t mState;
     uint32_t mCaptureIdx;
     uint32_t mCaptureSize;
-    uint32_t mCurrentBuf;
+    uint8_t mState;
+    uint8_t mCurrentBuf;
+    uint8_t mLastBuf;
+    uint8_t mStallCount;
     uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX];
 };
 
-
 //
 //--- Local functions
 //
@@ -66,6 +71,8 @@
 {
     pContext->mCaptureIdx = 0;
     pContext->mCurrentBuf = 0;
+    pContext->mLastBuf = 1;
+    pContext->mStallCount = 0;
     memset(pContext->mCaptureBuf[0], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
     memset(pContext->mCaptureBuf[1], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
 }
@@ -417,9 +424,24 @@
             memcpy(pReplyData,
                    pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
                    pContext->mCaptureSize);
+            // if audio framework has stopped playing audio although the effect is still
+            // active we must clear the capture buffer to return silence
+            if (pContext->mLastBuf == pContext->mCurrentBuf) {
+                if (pContext->mStallCount < MAX_STALL_COUNT) {
+                    if (++pContext->mStallCount == MAX_STALL_COUNT) {
+                        memset(pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
+                                0x80,
+                                pContext->mCaptureSize);
+                    }
+                }
+            } else {
+                pContext->mStallCount = 0;
+            }
+            pContext->mLastBuf = pContext->mCurrentBuf;
         } else {
             memset(pReplyData, 0x80, pContext->mCaptureSize);
         }
+
         break;
 
     default:
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 1b286fa..7b6fa38 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1136,6 +1136,41 @@
             break;
         }
 
+        // @xyz
+        case FOURCC('\xA9', 'x', 'y', 'z'):
+        {
+            // Best case the total data length inside "@xyz" box
+            // would be 8, for instance "@xyz" + "\x00\x04\x15\xc7" + "0+0/",
+            // where "\x00\x04" is the text string length with value = 4,
+            // "\0x15\xc7" is the language code = en, and "0+0" is a
+            // location (string) value with longitude = 0 and latitude = 0.
+            if (chunk_data_size < 8) {
+                return ERROR_MALFORMED;
+            }
+
+            // Worst case the location string length would be 18,
+            // for instance +90.0000-180.0000, without the trailing "/" and
+            // the string length + language code.
+            char buffer[18];
+
+            // Substracting 5 from the data size is because the text string length +
+            // language code takes 4 bytes, and the trailing slash "/" takes 1 byte.
+            off64_t location_length = chunk_data_size - 5;
+            if (location_length >= (off64_t) sizeof(buffer)) {
+                return ERROR_MALFORMED;
+            }
+
+            if (mDataSource->readAt(
+                        data_offset + 4, buffer, location_length) < location_length) {
+                return ERROR_IO;
+            }
+
+            buffer[location_length] = '\0';
+            mFileMetaData->setCString(kKeyLocation, buffer);
+            *offset += chunk_size;
+            break;
+        }
+
         case FOURCC('e', 's', 'd', 's'):
         {
             if (chunk_data_size < 4) {
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 022b169..2634da0 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -418,6 +418,7 @@
         { kKeyYear, METADATA_KEY_YEAR },
         { kKeyWriter, METADATA_KEY_WRITER },
         { kKeyCompilation, METADATA_KEY_COMPILATION },
+        { kKeyLocation, METADATA_KEY_LOCATION },
     };
     static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
 
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index c450158..0fbbb9e 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -187,10 +187,13 @@
     return true;
 }
 
-static void MakeSocketBlocking(int s, bool blocking) {
+static status_t MakeSocketBlocking(int s, bool blocking) {
     // Make socket non-blocking.
     int flags = fcntl(s, F_GETFL, 0);
-    CHECK_NE(flags, -1);
+
+    if (flags == -1) {
+        return UNKNOWN_ERROR;
+    }
 
     if (blocking) {
         flags &= ~O_NONBLOCK;
@@ -198,7 +201,9 @@
         flags |= O_NONBLOCK;
     }
 
-    CHECK_NE(fcntl(s, F_SETFL, flags), -1);
+    flags = fcntl(s, F_SETFL, flags);
+
+    return flags == -1 ? UNKNOWN_ERROR : OK;
 }
 
 void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
@@ -302,27 +307,32 @@
     reply->post();
 }
 
+void ARTSPConnection::performDisconnect() {
+    if (mUIDValid) {
+        HTTPBase::UnRegisterSocketUserTag(mSocket);
+    }
+    close(mSocket);
+    mSocket = -1;
+
+    flushPendingRequests();
+
+    mUser.clear();
+    mPass.clear();
+    mAuthType = NONE;
+    mNonce.clear();
+
+    mState = DISCONNECTED;
+}
+
 void ARTSPConnection::onDisconnect(const sp<AMessage> &msg) {
     if (mState == CONNECTED || mState == CONNECTING) {
-        if (mUIDValid) {
-            HTTPBase::UnRegisterSocketUserTag(mSocket);
-        }
-        close(mSocket);
-        mSocket = -1;
-
-        flushPendingRequests();
+        performDisconnect();
     }
 
     sp<AMessage> reply;
     CHECK(msg->findMessage("reply", &reply));
 
     reply->setInt32("result", OK);
-    mState = DISCONNECTED;
-
-    mUser.clear();
-    mPass.clear();
-    mAuthType = NONE;
-    mNonce.clear();
 
     reply->post();
 }
@@ -427,21 +437,25 @@
             send(mSocket, request.c_str() + numBytesSent,
                  request.size() - numBytesSent, 0);
 
-        if (n == 0) {
-            // Server closed the connection.
-            LOGE("Server unexpectedly closed the connection.");
+        if (n < 0 && errno == EINTR) {
+            continue;
+        }
 
-            reply->setInt32("result", ERROR_IO);
-            reply->post();
-            return;
-        } else if (n < 0) {
-            if (errno == EINTR) {
-                continue;
+        if (n <= 0) {
+            performDisconnect();
+
+            if (n == 0) {
+                // Server closed the connection.
+                LOGE("Server unexpectedly closed the connection.");
+
+                reply->setInt32("result", ERROR_IO);
+                reply->post();
+            } else {
+                LOGE("Error sending rtsp request. (%s)", strerror(errno));
+                reply->setInt32("result", -errno);
+                reply->post();
             }
 
-            LOGE("Error sending rtsp request.");
-            reply->setInt32("result", -errno);
-            reply->post();
             return;
         }
 
@@ -512,17 +526,22 @@
     size_t offset = 0;
     while (offset < size) {
         ssize_t n = recv(mSocket, (uint8_t *)data + offset, size - offset, 0);
-        if (n == 0) {
-            // Server closed the connection.
-            LOGE("Server unexpectedly closed the connection.");
-            return ERROR_IO;
-        } else if (n < 0) {
-            if (errno == EINTR) {
-                continue;
-            }
 
-            LOGE("Error reading rtsp response.");
-            return -errno;
+        if (n < 0 && errno == EINTR) {
+            continue;
+        }
+
+        if (n <= 0) {
+            performDisconnect();
+
+            if (n == 0) {
+                // Server closed the connection.
+                LOGE("Server unexpectedly closed the connection.");
+                return ERROR_IO;
+            } else {
+                LOGE("Error reading rtsp response. (%s)", strerror(errno));
+                return -errno;
+            }
         }
 
         offset += (size_t)n;
@@ -681,24 +700,8 @@
     if (contentLength > 0) {
         response->mContent = new ABuffer(contentLength);
 
-        size_t numBytesRead = 0;
-        while (numBytesRead < contentLength) {
-            ssize_t n = recv(
-                    mSocket, response->mContent->data() + numBytesRead,
-                    contentLength - numBytesRead, 0);
-
-            if (n == 0) {
-                // Server closed the connection.
-                TRESPASS();
-            } else if (n < 0) {
-                if (errno == EINTR) {
-                    continue;
-                }
-
-                TRESPASS();
-            }
-
-            numBytesRead += (size_t)n;
+        if (receive(response->mContent->data(), contentLength) != OK) {
+            return false;
         }
     }
 
@@ -765,17 +768,20 @@
             send(mSocket, response.c_str() + numBytesSent,
                  response.size() - numBytesSent, 0);
 
-        if (n == 0) {
-            // Server closed the connection.
-            LOGE("Server unexpectedly closed the connection.");
+        if (n < 0 && errno == EINTR) {
+            continue;
+        }
 
-            return false;
-        } else if (n < 0) {
-            if (errno == EINTR) {
-                continue;
+        if (n <= 0) {
+            if (n == 0) {
+                // Server closed the connection.
+                LOGE("Server unexpectedly closed the connection.");
+            } else {
+                LOGE("Error sending rtsp response (%s).", strerror(errno));
             }
 
-            LOGE("Error sending rtsp response.");
+            performDisconnect();
+
             return false;
         }
 
diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h
index 5cb84fd..68f2d59 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.h
+++ b/media/libstagefright/rtsp/ARTSPConnection.h
@@ -91,6 +91,8 @@
 
     AString mUserAgent;
 
+    void performDisconnect();
+
     void onConnect(const sp<AMessage> &msg);
     void onDisconnect(const sp<AMessage> &msg);
     void onCompleteConnection(const sp<AMessage> &msg);
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 3789b8d..dd049c2 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -463,8 +463,17 @@
                                 mBaseURL = tmp;
                             }
 
-                            CHECK_GT(mSessionDesc->countTracks(), 1u);
-                            setupTrack(1);
+                            if (mSessionDesc->countTracks() < 2) {
+                                // There's no actual tracks in this session.
+                                // The first "track" is merely session meta
+                                // data.
+
+                                LOGW("Session doesn't contain any playable "
+                                     "tracks. Aborting.");
+                                result = ERROR_UNSUPPORTED;
+                            } else {
+                                setupTrack(1);
+                            }
                         }
                     }
                 }
@@ -783,9 +792,13 @@
                 }
 
                 if (mNumAccessUnitsReceived == 0) {
+#if 1
                     LOGI("stream ended? aborting.");
                     (new AMessage('abor', id()))->post();
                     break;
+#else
+                    LOGI("haven't seen an AU in a looong time.");
+#endif
                 }
 
                 mNumAccessUnitsReceived = 0;
diff --git a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
index 99cbb86..62213de 100644
--- a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
+++ b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
@@ -16,8 +16,7 @@
 
 package com.android.nfc_extras;
 
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
 import android.nfc.INfcAdapterExtras;
 import android.nfc.NfcAdapter;
 import android.os.RemoteException;
@@ -60,10 +59,14 @@
     // best effort recovery
     private static NfcAdapter sAdapter;
     private static INfcAdapterExtras sService;
-    private static NfcAdapterExtras sSingleton;
-    private static NfcExecutionEnvironment sEmbeddedEe;
-    private static CardEmulationRoute sRouteOff;
-    private static CardEmulationRoute sRouteOnWhenScreenOn;
+    private static final CardEmulationRoute ROUTE_OFF =
+            new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null);
+
+    private final NfcExecutionEnvironment mEmbeddedEe;
+    private final CardEmulationRoute mRouteOnWhenScreenOn;
+
+    final Context mContext;
+    final String mPackageName;
 
     /** get service handles */
     private static void initService() {
@@ -84,31 +87,35 @@
      * @return the {@link NfcAdapterExtras} object for the given {@link NfcAdapter}
      */
     public static NfcAdapterExtras get(NfcAdapter adapter) {
-        synchronized(NfcAdapterExtras.class) {
-            if (sSingleton == null) {
+        Context context = adapter.getContext();
+        if (context == null) {
+            throw new UnsupportedOperationException(
+                    "You must pass a context to your NfcAdapter to use the NFC extras APIs");
+        }
+
+        synchronized (NfcAdapterExtras.class) {
+            if (sService == null) {
                 try {
                     sAdapter = adapter;
-                    sSingleton = new NfcAdapterExtras();
-                    sEmbeddedEe = new NfcExecutionEnvironment(sSingleton);
-                    sRouteOff = new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null);
-                    sRouteOnWhenScreenOn = new CardEmulationRoute(
-                            CardEmulationRoute.ROUTE_ON_WHEN_SCREEN_ON, sEmbeddedEe);
                     initService();
                 } finally {
                     if (sService == null) {
-                        sRouteOnWhenScreenOn = null;
-                        sRouteOff = null;
-                        sEmbeddedEe = null;
-                        sSingleton = null;
                         sAdapter = null;
                     }
                 }
             }
-            return sSingleton;
         }
+
+        return new NfcAdapterExtras(context);
     }
 
-    private NfcAdapterExtras() {}
+    private NfcAdapterExtras(Context context) {
+        mContext = context.getApplicationContext();
+        mPackageName = context.getPackageName();
+        mEmbeddedEe = new NfcExecutionEnvironment(this);
+        mRouteOnWhenScreenOn = new CardEmulationRoute(CardEmulationRoute.ROUTE_ON_WHEN_SCREEN_ON,
+                mEmbeddedEe);
+    }
 
     /**
      * Immutable data class that describes a card emulation route.
@@ -166,18 +173,16 @@
      *
      * <p class="note">
      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
-     *
-     * @return
      */
     public CardEmulationRoute getCardEmulationRoute() {
         try {
-            int route = sService.getCardEmulationRoute();
+            int route = sService.getCardEmulationRoute(mPackageName);
             return route == CardEmulationRoute.ROUTE_OFF ?
-                    sRouteOff :
-                    sRouteOnWhenScreenOn;
+                    ROUTE_OFF :
+                    mRouteOnWhenScreenOn;
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
-            return sRouteOff;
+            return ROUTE_OFF;
         }
     }
 
@@ -189,11 +194,11 @@
      * <p class="note">
      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
      *
-     * @param route a {@link #CardEmulationRoute}
+     * @param route a {@link CardEmulationRoute}
      */
     public void setCardEmulationRoute(CardEmulationRoute route) {
         try {
-            sService.setCardEmulationRoute(route.route);
+            sService.setCardEmulationRoute(mPackageName, route.route);
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
         }
@@ -201,7 +206,7 @@
 
     /**
      * Get the {@link NfcExecutionEnvironment} that is embedded with the
-     * {@link NFcAdapter}.
+     * {@link NfcAdapter}.
      *
      * <p class="note">
      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
@@ -209,7 +214,7 @@
      * @return a {@link NfcExecutionEnvironment}, or null if there is no embedded NFC-EE
      */
     public NfcExecutionEnvironment getEmbeddedExecutionEnvironment() {
-        return sEmbeddedEe;
+        return mEmbeddedEe;
     }
 
     /**
@@ -218,12 +223,12 @@
      * Some implementations of NFC Adapter Extras may require applications
      * to authenticate with a token, before using other methods.
      *
-     * @param a implementation specific token
-     * @throws a {@link java.lang.SecurityException} if authentication failed
+     * @param token a implementation specific token
+     * @throws java.lang.SecurityException if authentication failed
      */
     public void authenticate(byte[] token) {
         try {
-            sService.authenticate(token);
+            sService.authenticate(mPackageName, token);
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
         }
diff --git a/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java b/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
index 63c2de2..f47327a 100644
--- a/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
+++ b/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
@@ -16,20 +16,17 @@
 
 package com.android.nfc_extras;
 
-import java.io.IOException;
-
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.content.Context;
-import android.nfc.INfcAdapterExtras;
-import android.nfc.NfcAdapter;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.IBinder;
 import android.os.RemoteException;
 
+import java.io.IOException;
+
 public class NfcExecutionEnvironment {
     private final NfcAdapterExtras mExtras;
+    private final Binder mToken;
 
     /**
      * Broadcast Action: An ISO-DEP AID was selected.
@@ -115,6 +112,7 @@
 
     NfcExecutionEnvironment(NfcAdapterExtras extras) {
         mExtras = extras;
+        mToken = new Binder();
     }
 
     /**
@@ -133,7 +131,7 @@
      */
     public void open() throws IOException {
         try {
-            Bundle b = mExtras.getService().open(new Binder());
+            Bundle b = mExtras.getService().open(mExtras.mPackageName, mToken);
             throwBundle(b);
         } catch (RemoteException e) {
             mExtras.attemptDeadServiceRecovery(e);
@@ -151,7 +149,7 @@
      */
     public void close() throws IOException {
         try {
-            throwBundle(mExtras.getService().close());
+            throwBundle(mExtras.getService().close(mExtras.mPackageName, mToken));
         } catch (RemoteException e) {
             mExtras.attemptDeadServiceRecovery(e);
             throw new IOException("NFC Service was dead");
@@ -169,7 +167,7 @@
     public byte[] transceive(byte[] in) throws IOException {
         Bundle b;
         try {
-            b = mExtras.getService().transceive(in);
+            b = mExtras.getService().transceive(mExtras.mPackageName, in);
         } catch (RemoteException e) {
             mExtras.attemptDeadServiceRecovery(e);
             throw new IOException("NFC Service was dead, need to re-open");
diff --git a/opengl/java/android/opengl/EGLLogWrapper.java b/opengl/java/android/opengl/EGLLogWrapper.java
index 6c0fdb3..36e88a2 100644
--- a/opengl/java/android/opengl/EGLLogWrapper.java
+++ b/opengl/java/android/opengl/EGLLogWrapper.java
@@ -314,6 +314,16 @@
         checkError();
         return result;
     }
+    
+    /** @hide **/
+    public boolean eglReleaseThread() {
+        begin("eglReleaseThread");
+        end();
+        boolean result = mEgl10.eglReleaseThread();
+        returns(result);
+        checkError();
+        return result;
+    }
 
     public boolean eglSwapBuffers(EGLDisplay display, EGLSurface surface) {
         begin("eglInitialize");
diff --git a/opengl/java/com/google/android/gles_jni/EGLImpl.java b/opengl/java/com/google/android/gles_jni/EGLImpl.java
index 51d6ca8..6992019 100644
--- a/opengl/java/com/google/android/gles_jni/EGLImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLImpl.java
@@ -31,6 +31,8 @@
     public native boolean     eglInitialize(EGLDisplay display, int[] major_minor);
     public native boolean     eglQueryContext(EGLDisplay display, EGLContext context, int attribute, int[] value);
     public native boolean     eglQuerySurface(EGLDisplay display, EGLSurface surface, int attribute, int[] value);
+    /** @hide **/
+    public native boolean     eglReleaseThread();
     public native boolean     eglChooseConfig(EGLDisplay display, int[] attrib_list, EGLConfig[] configs, int config_size, int[] num_config);
     public native boolean     eglGetConfigAttrib(EGLDisplay display, EGLConfig config, int attribute, int[] value);
     public native boolean     eglGetConfigs(EGLDisplay display, EGLConfig[] configs, int config_size, int[] num_config);
@@ -44,6 +46,9 @@
     public native boolean     eglCopyBuffers(EGLDisplay display, EGLSurface surface, Object native_pixmap);
     public native boolean     eglWaitGL();
     public native boolean     eglWaitNative(int engine, Object bindTarget);
+    
+    /** @hide **/
+    public static native int  getInitCount(EGLDisplay display);
 
     public EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list) {
         int eglContextId = _eglCreateContext(display, config, share_context, attrib_list);
@@ -85,7 +90,7 @@
             eglSurfaceId = _eglCreateWindowSurface(display, config, sur, attrib_list);
         } else if (native_window instanceof SurfaceTexture) {
             eglSurfaceId = _eglCreateWindowSurfaceTexture(display, config,
-                    (SurfaceTexture) native_window, attrib_list);
+                    native_window, attrib_list);
         } else {
             throw new java.lang.UnsupportedOperationException(
                 "eglCreateWindowSurface() can only be called with an instance of " +
diff --git a/opengl/java/javax/microedition/khronos/egl/EGL10.java b/opengl/java/javax/microedition/khronos/egl/EGL10.java
index 2ae793a..cf58888 100644
--- a/opengl/java/javax/microedition/khronos/egl/EGL10.java
+++ b/opengl/java/javax/microedition/khronos/egl/EGL10.java
@@ -114,6 +114,8 @@
     boolean     eglQueryContext(EGLDisplay display, EGLContext context, int attribute, int[] value);
     String      eglQueryString(EGLDisplay display, int name);
     boolean     eglQuerySurface(EGLDisplay display, EGLSurface surface, int attribute, int[] value);
+    /** @hide **/
+    boolean     eglReleaseThread();
     boolean     eglSwapBuffers(EGLDisplay display, EGLSurface surface);
     boolean     eglTerminate(EGLDisplay display);
     boolean     eglWaitGL();
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 1c1092c..e0a367d 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -91,6 +91,8 @@
     inline bool isValid() const { return magic == '_dpy'; }
     inline bool isAlive() const { return isValid(); }
 
+    inline uint32_t getRefsCount() const { return refs; }
+
     struct strings_t {
         char const * vendor;
         char const * version;
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notification_open.png b/packages/SystemUI/res/drawable-hdpi/ic_notification_open.png
index d697c2f..cd9a54a 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_notification_open.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_notification_open.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notification_open.png b/packages/SystemUI/res/drawable-mdpi/ic_notification_open.png
index 839c134..5661eaf 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_notification_open.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_notification_open.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notification_open.png b/packages/SystemUI/res/drawable-xhdpi/ic_notification_open.png
index 4f8c987..98455cf 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notification_open.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_notification_open.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/signal_cluster_view.xml b/packages/SystemUI/res/layout/signal_cluster_view.xml
index 93ac22e..9be9041 100644
--- a/packages/SystemUI/res/layout/signal_cluster_view.xml
+++ b/packages/SystemUI/res/layout/signal_cluster_view.xml
@@ -51,7 +51,7 @@
         android:visibility="gone"
         android:id="@+id/spacer"
         />
-    <FrameLayout
+    <!--<FrameLayout
         android:id="@+id/wimax_combo"
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
@@ -72,6 +72,7 @@
             android:layout_gravity="center|bottom"
             />
     </FrameLayout>
+    -->
     <FrameLayout
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a0d7b13..fc81f8e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -257,10 +257,16 @@
     <string name="accessibility_wifi_three_bars">Wi-Fi three bars.</string>
     <!-- Content description of the WIFI signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_wifi_signal_full">WiFi signal full.</string>
+
+    <!-- Content description of the WiMAX signal when no signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_no_wimax">No WiMAX.</string>
+    <!-- Content description of the WiMAX signal when it is one bar for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_wimax_one_bar">WiMAX one bar.</string>
+    <!-- Content description of the WiMAX signal when it is two bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_wimax_two_bars">WiMAX two bars.</string>
+    <!-- Content description of the WiMAX signal when it is three bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_wimax_three_bars">WiMAX three bars.</string>
+    <!-- Content description of the WiMAX signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_wimax_signal_full">WiMAX signal full.</string>
 
     <!-- Content description of the data connection type GPRS for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
index 2e1803e..2be35b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
@@ -118,15 +118,20 @@
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                     | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                     | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                PixelFormat.OPAQUE);
+                // We use a pixel format of RGB565 for the status bar to save memory bandwidth and
+                // to ensure that the layer can be handled by HWComposer.  On some devices the
+                // HWComposer is unable to handle SW-rendered RGBX_8888 layers.
+                PixelFormat.RGB_565);
         
         // the status bar should be in an overlay if possible
         final Display defaultDisplay 
             = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
                 .getDefaultDisplay();
-        if (ActivityManager.isHighEndGfx(defaultDisplay)) {
-            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-        }
+
+        // We explicitly leave FLAG_HARDWARE_ACCELERATED out of the flags.  The status bar occupies
+        // very little screen real-estate and is updated fairly frequently.  By using CPU rendering
+        // for the status bar, we prevent the GPU from having to wake up just to do these small
+        // updates, which should help keep power consumption down.
 
         lp.gravity = getStatusBarGravity();
         lp.setTitle("StatusBar");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index b0e6968..51fb262 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -350,11 +350,11 @@
                 (SignalClusterView)sb.findViewById(R.id.signal_cluster);
         mNetworkController.addSignalCluster(signalCluster);
         signalCluster.setNetworkController(mNetworkController);
-	final ImageView wimaxRSSI = 
-                (ImageView)sb.findViewById(R.id.wimax_signal);
-        if (wimaxRSSI != null) {
-            mNetworkController.addWimaxIconView(wimaxRSSI);
-        }
+//        final ImageView wimaxRSSI = 
+//                (ImageView)sb.findViewById(R.id.wimax_signal);
+//        if (wimaxRSSI != null) {
+//            mNetworkController.addWimaxIconView(wimaxRSSI);
+//        }
         // Recents Panel
         mRecentTasksLoader = new RecentTasksLoader(context);
         updateRecentsPanel();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index f77e93f..55a5b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -111,6 +111,7 @@
         com.android.internal.R.drawable.stat_sys_tether_bluetooth;
 
     //wimax
+    private boolean mWimaxSupported = false;
     private boolean mIsWimaxEnabled = false;
     private boolean mWimaxConnected = false;
     private boolean mWimaxIdle = false;
@@ -213,9 +214,9 @@
         filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        boolean isWimaxConfigEnabled = mContext.getResources().getBoolean(
+        mWimaxSupported = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_wimaxEnabled);
-        if(isWimaxConfigEnabled) {
+        if(mWimaxSupported) {
             filter.addAction(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION);
             filter.addAction(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION);
             filter.addAction(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION);
@@ -262,19 +263,36 @@
 
     public void addSignalCluster(SignalCluster cluster) {
         mSignalClusters.add(cluster);
+        refreshSignalCluster(cluster);
+    }
+
+    public void refreshSignalCluster(SignalCluster cluster) {
         cluster.setWifiIndicators(
                 mWifiConnected, // only show wifi in the cluster if connected
                 mWifiIconId,
                 mWifiActivityIconId,
                 mContentDescriptionWifi);
-        cluster.setMobileDataIndicators(
-                mHasMobileDataFeature,
-                mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId,
-                mMobileActivityIconId,
-                mDataTypeIconId,
-                mContentDescriptionPhoneSignal,
-                mContentDescriptionDataType);
 
+        if (mIsWimaxEnabled && mWimaxConnected) {
+            // wimax is special
+            cluster.setMobileDataIndicators(
+                    true,
+                    mWimaxIconId,
+                    mMobileActivityIconId,
+                    mDataTypeIconId,
+                    mContentDescriptionWimax,
+                    mContentDescriptionDataType);
+        } else {
+            // normal mobile data
+            cluster.setMobileDataIndicators(
+                    mHasMobileDataFeature,
+                    mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId,
+                    mMobileActivityIconId,
+                    mDataTypeIconId,
+                    mContentDescriptionPhoneSignal,
+                    mContentDescriptionDataType);
+        }
+        cluster.setIsAirplaneMode(mAirplaneMode);
     }
 
     public void setStackedMode(boolean stacked) {
@@ -311,7 +329,7 @@
         } else if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION) ||
                 action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION) ||
                 action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) {
-           updateWimaxState(intent);
+            updateWimaxState(intent);
             refreshViews();
         }
     }
@@ -466,91 +484,100 @@
     }
 
     private final void updateDataNetType() {
-        switch (mDataNetType) {
-            case TelephonyManager.NETWORK_TYPE_UNKNOWN:
-                if (!mShowAtLeastThreeGees) {
-                    mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
-                    mDataTypeIconId = 0;
-                    mContentDescriptionDataType = mContext.getString(
-                            R.string.accessibility_data_connection_gprs);
-                    break;
-                } else {
-                    // fall through
-                }
-            case TelephonyManager.NETWORK_TYPE_EDGE:
-                if (!mShowAtLeastThreeGees) {
-                    mDataIconList = TelephonyIcons.DATA_E[mInetCondition];
-                    mDataTypeIconId = R.drawable.stat_sys_data_connected_e;
-                    mContentDescriptionDataType = mContext.getString(
-                            R.string.accessibility_data_connection_edge);
-                    break;
-                } else {
-                    // fall through
-                }
-            case TelephonyManager.NETWORK_TYPE_UMTS:
-                mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
-                mDataTypeIconId = R.drawable.stat_sys_data_connected_3g;
-                mContentDescriptionDataType = mContext.getString(
-                        R.string.accessibility_data_connection_3g);
-                break;
-            case TelephonyManager.NETWORK_TYPE_HSDPA:
-            case TelephonyManager.NETWORK_TYPE_HSUPA:
-            case TelephonyManager.NETWORK_TYPE_HSPA:
-            case TelephonyManager.NETWORK_TYPE_HSPAP:
-                if (mHspaDataDistinguishable) {
-                    mDataIconList = TelephonyIcons.DATA_H[mInetCondition];
-                    mDataTypeIconId = R.drawable.stat_sys_data_connected_h;
-                    mContentDescriptionDataType = mContext.getString(
-                            R.string.accessibility_data_connection_3_5g);
-                } else {
+        if (mIsWimaxEnabled && mWimaxConnected) {
+            // wimax is a special 4g network not handled by telephony
+            mDataIconList = TelephonyIcons.DATA_4G[mInetCondition];
+            mDataTypeIconId = R.drawable.stat_sys_data_connected_4g;
+            mContentDescriptionDataType = mContext.getString(
+                    R.string.accessibility_data_connection_4g);
+        } else {
+            switch (mDataNetType) {
+                case TelephonyManager.NETWORK_TYPE_UNKNOWN:
+                    if (!mShowAtLeastThreeGees) {
+                        mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
+                        mDataTypeIconId = 0;
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_gprs);
+                        break;
+                    } else {
+                        // fall through
+                    }
+                case TelephonyManager.NETWORK_TYPE_EDGE:
+                    if (!mShowAtLeastThreeGees) {
+                        mDataIconList = TelephonyIcons.DATA_E[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_connected_e;
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_edge);
+                        break;
+                    } else {
+                        // fall through
+                    }
+                case TelephonyManager.NETWORK_TYPE_UMTS:
                     mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
                     mDataTypeIconId = R.drawable.stat_sys_data_connected_3g;
                     mContentDescriptionDataType = mContext.getString(
                             R.string.accessibility_data_connection_3g);
-                }
-                break;
-            case TelephonyManager.NETWORK_TYPE_CDMA:
-                // display 1xRTT for IS95A/B
-                mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
-                mDataTypeIconId = R.drawable.stat_sys_data_connected_1x;
-                mContentDescriptionDataType = mContext.getString(
-                        R.string.accessibility_data_connection_cdma);
-                break;
-            case TelephonyManager.NETWORK_TYPE_1xRTT:
-                mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
-                mDataTypeIconId = R.drawable.stat_sys_data_connected_1x;
-                mContentDescriptionDataType = mContext.getString(
-                        R.string.accessibility_data_connection_cdma);
-                break;
-            case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
-            case TelephonyManager.NETWORK_TYPE_EVDO_A:
-            case TelephonyManager.NETWORK_TYPE_EVDO_B:
-            case TelephonyManager.NETWORK_TYPE_EHRPD:
-                mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
-                mDataTypeIconId = R.drawable.stat_sys_data_connected_3g;
-                mContentDescriptionDataType = mContext.getString(
-                        R.string.accessibility_data_connection_3g);
-                break;
-            case TelephonyManager.NETWORK_TYPE_LTE:
-                mDataIconList = TelephonyIcons.DATA_4G[mInetCondition];
-                mDataTypeIconId = R.drawable.stat_sys_data_connected_4g;
-                mContentDescriptionDataType = mContext.getString(
-                        R.string.accessibility_data_connection_4g);
-                break;
-            default:
-                if (!mShowAtLeastThreeGees) {
-                    mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
-                    mDataTypeIconId = R.drawable.stat_sys_data_connected_g;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_HSDPA:
+                case TelephonyManager.NETWORK_TYPE_HSUPA:
+                case TelephonyManager.NETWORK_TYPE_HSPA:
+                case TelephonyManager.NETWORK_TYPE_HSPAP:
+                    if (mHspaDataDistinguishable) {
+                        mDataIconList = TelephonyIcons.DATA_H[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_connected_h;
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_3_5g);
+                    } else {
+                        mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_connected_3g;
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_3g);
+                    }
+                    break;
+                case TelephonyManager.NETWORK_TYPE_CDMA:
+                    // display 1xRTT for IS95A/B
+                    mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
+                    mDataTypeIconId = R.drawable.stat_sys_data_connected_1x;
                     mContentDescriptionDataType = mContext.getString(
-                            R.string.accessibility_data_connection_gprs);
-                } else {
+                            R.string.accessibility_data_connection_cdma);
+                    break;
+                case TelephonyManager.NETWORK_TYPE_1xRTT:
+                    mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
+                    mDataTypeIconId = R.drawable.stat_sys_data_connected_1x;
+                    mContentDescriptionDataType = mContext.getString(
+                            R.string.accessibility_data_connection_cdma);
+                    break;
+                case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
+                case TelephonyManager.NETWORK_TYPE_EVDO_A:
+                case TelephonyManager.NETWORK_TYPE_EVDO_B:
+                case TelephonyManager.NETWORK_TYPE_EHRPD:
                     mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
                     mDataTypeIconId = R.drawable.stat_sys_data_connected_3g;
                     mContentDescriptionDataType = mContext.getString(
                             R.string.accessibility_data_connection_3g);
-                }
-                break;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_LTE:
+                    mDataIconList = TelephonyIcons.DATA_4G[mInetCondition];
+                    mDataTypeIconId = R.drawable.stat_sys_data_connected_4g;
+                    mContentDescriptionDataType = mContext.getString(
+                            R.string.accessibility_data_connection_4g);
+                    break;
+                default:
+                    if (!mShowAtLeastThreeGees) {
+                        mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_connected_g;
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_gprs);
+                    } else {
+                        mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
+                        mDataTypeIconId = R.drawable.stat_sys_data_connected_3g;
+                        mContentDescriptionDataType = mContext.getString(
+                                R.string.accessibility_data_connection_3g);
+                    }
+                    break;
+            }
         }
+
         if ((isCdma() && isCdmaEri()) || mPhone.isNetworkRoaming()) {
             mDataTypeIconId = R.drawable.stat_sys_data_connected_roam;
         }
@@ -763,8 +790,7 @@
     }
 
 
- // ===== Wimax ===================================================================
-
+    // ===== Wimax ===================================================================
     private final void updateWimaxState(Intent intent) {
         final String action = intent.getAction();
         boolean wasConnected = mWimaxConnected;
@@ -772,42 +798,41 @@
             int wimaxStatus = intent.getIntExtra(WimaxManagerConstants.EXTRA_4G_STATE,
                     WimaxManagerConstants.NET_4G_STATE_UNKNOWN);
             mIsWimaxEnabled = (wimaxStatus ==
-		WimaxManagerConstants.NET_4G_STATE_ENABLED)? true : false;
+                    WimaxManagerConstants.NET_4G_STATE_ENABLED);
         } else if (action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION)) {
             mWimaxSignal = intent.getIntExtra(WimaxManagerConstants.EXTRA_NEW_SIGNAL_LEVEL, 0);
         } else if (action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) {
-		mWimaxState = intent.getIntExtra(WimaxManagerConstants.EXTRA_WIMAX_STATE,
+            mWimaxState = intent.getIntExtra(WimaxManagerConstants.EXTRA_WIMAX_STATE,
                     WimaxManagerConstants.NET_4G_STATE_UNKNOWN);
             mWimaxExtraState = intent.getIntExtra(
                     WimaxManagerConstants.EXTRA_WIMAX_STATE_DETAIL,
                     WimaxManagerConstants.NET_4G_STATE_UNKNOWN);
             mWimaxConnected = (mWimaxState ==
-		WimaxManagerConstants.WIMAX_STATE_CONNECTED) ? true : false;
-            mWimaxIdle = (mWimaxExtraState == WimaxManagerConstants.WIMAX_IDLE)? true : false;
+                    WimaxManagerConstants.WIMAX_STATE_CONNECTED);
+            mWimaxIdle = (mWimaxExtraState == WimaxManagerConstants.WIMAX_IDLE);
         }
+        updateDataNetType();
         updateWimaxIcons();
     }
-       private void updateWimaxIcons() {
-            Slog.d(TAG, "in ....  updateWimaxIcons function    :  "+mIsWimaxEnabled);
-                if (mIsWimaxEnabled) {
-                        if (mWimaxConnected) {
-                                Slog.d(TAG, "in ....  updateWimaxIcons function WiMAX COnnected");
-                                if (mWimaxIdle)
-                                        mWimaxIconId = WimaxIcons.WIMAX_IDLE;
-                                else
-                                        mWimaxIconId = WimaxIcons.WIMAX_SIGNAL_STRENGTH[mInetCondition][mWimaxSignal];
-                                mContentDescriptionWimax = mContext.getString(
-                                                AccessibilityContentDescriptions.WIMAX_CONNECTION_STRENGTH[mWimaxSignal]);
-                        } else {
-                                 Slog.d(TAG, "in ....  updateWimaxIcons function WiMAX Disconnected");
-                                mWimaxIconId = WimaxIcons.WIMAX_DISCONNECTED;
-                                mContentDescriptionWimax = mContext.getString(R.string.accessibility_no_wimax);
-                        }
-                } else {
-                         Slog.d(TAG, "in ....  updateWimaxIcons function wimax icon id 0");
-                        mWimaxIconId = 0;
-                }
+
+    private void updateWimaxIcons() {
+        if (mIsWimaxEnabled) {
+            if (mWimaxConnected) {
+                if (mWimaxIdle)
+                    mWimaxIconId = WimaxIcons.WIMAX_IDLE;
+                else
+                    mWimaxIconId = WimaxIcons.WIMAX_SIGNAL_STRENGTH[mInetCondition][mWimaxSignal];
+                mContentDescriptionWimax = mContext.getString(
+                        AccessibilityContentDescriptions.WIMAX_CONNECTION_STRENGTH[mWimaxSignal]);
+            } else {
+                mWimaxIconId = WimaxIcons.WIMAX_DISCONNECTED;
+                mContentDescriptionWimax = mContext.getString(R.string.accessibility_no_wimax);
+            }
+        } else {
+            mWimaxIconId = 0;
         }
+    }
+
     // ===== Full or limited Internet connectivity ==================================
 
     private void updateConnectivity(Intent intent) {
@@ -827,14 +852,14 @@
         mInetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
 
         if (info != null && info.getType() == ConnectivityManager.TYPE_BLUETOOTH) {
-            mBluetoothTethered = info.isConnected() ? true: false;
+            mBluetoothTethered = info.isConnected();
         } else {
             mBluetoothTethered = false;
         }
 
         // We want to update all the icons, all at once, for any condition change
         updateDataNetType();
-		updateWimaxIcons();
+        updateWimaxIcons();
         updateDataIcon();
         updateTelephonySignalStrength();
         updateWifiIcons();
@@ -921,7 +946,7 @@
 
             combinedSignalIconId = mDataSignalIconId;
         }
-        else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered) {
+        else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered && !mWimaxConnected) {
             // pretty much totally disconnected
 
             label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
@@ -961,23 +986,12 @@
         if (mLastPhoneSignalIconId          != mPhoneSignalIconId
          || mLastDataDirectionOverlayIconId != combinedActivityIconId
          || mLastWifiIconId                 != mWifiIconId
+         || mLastWimaxIconId                != mWimaxIconId
          || mLastDataTypeIconId             != mDataTypeIconId)
         {
             // NB: the mLast*s will be updated later
             for (SignalCluster cluster : mSignalClusters) {
-                cluster.setWifiIndicators(
-                        mWifiConnected, // only show wifi in the cluster if connected
-                        mWifiIconId,
-                        mWifiActivityIconId,
-                        mContentDescriptionWifi);
-                cluster.setMobileDataIndicators(
-                        mHasMobileDataFeature,
-                        mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId,
-                        mMobileActivityIconId,
-                        mDataTypeIconId,
-                        mContentDescriptionPhoneSignal,
-                        mContentDescriptionDataType);
-                cluster.setIsAirplaneMode(mAirplaneMode);
+                refreshSignalCluster(cluster);
             }
         }
 
@@ -1152,11 +1166,22 @@
         pw.println(mWifiLevel);
         pw.print("  mWifiSsid=");
         pw.println(mWifiSsid);
-        pw.print(String.format("  mWifiIconId=0x%08x/%s",
+        pw.println(String.format("  mWifiIconId=0x%08x/%s",
                     mWifiIconId, getResourceName(mWifiIconId)));
         pw.print("  mWifiActivity=");
         pw.println(mWifiActivity);
 
+        if (mWimaxSupported) {
+            pw.println("  - wimax ------");
+            pw.print("  mIsWimaxEnabled="); pw.println(mIsWimaxEnabled);
+            pw.print("  mWimaxConnected="); pw.println(mWimaxConnected);
+            pw.print("  mWimaxIdle="); pw.println(mWimaxIdle);
+            pw.println(String.format("  mWimaxIconId=0x%08x/%s",
+                        mWimaxIconId, getResourceName(mWimaxIconId)));
+            pw.println(String.format("  mWimaxSignal=%d", mWimaxSignal));
+            pw.println(String.format("  mWimaxState=%d", mWimaxState));
+            pw.println(String.format("  mWimaxExtraState=%d", mWimaxExtraState));
+        }
 
         pw.println("  - Bluetooth ----");
         pw.print("  mBtReverseTethered=");
@@ -1190,7 +1215,7 @@
         pw.print("  mLastDataTypeIconId=0x");
         pw.print(Integer.toHexString(mLastDataTypeIconId));
         pw.print("/");
-        pw.println(getResourceName(mLastCombinedSignalIconId));
+        pw.println(getResourceName(mLastDataTypeIconId));
         pw.print("  mLastLabel=");
         pw.print(mLastLabel);
         pw.println("");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java
index 8605489..d3d4338 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java
@@ -1,5 +1,5 @@
 /*

- * Copyright (C) 2008 The Android Open Source Project

+ * Copyright (C) 2011 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.

@@ -16,22 +16,13 @@
 

 package com.android.systemui.statusbar.policy;

 

+import com.android.systemui.statusbar.policy.TelephonyIcons;

 import com.android.systemui.R;

 

 class WimaxIcons {

-    static final int[][] WIMAX_SIGNAL_STRENGTH = {

-	{ R.drawable.stat_sys_data_wimax_signal_0,

-            R.drawable.stat_sys_data_wimax_signal_1,

-            R.drawable.stat_sys_data_wimax_signal_2,

-            R.drawable.stat_sys_data_wimax_signal_3 },

-          { R.drawable.stat_sys_data_wimax_signal_0_fully,

-            R.drawable.stat_sys_data_wimax_signal_1_fully,

-            R.drawable.stat_sys_data_wimax_signal_2_fully,

-            R.drawable.stat_sys_data_wimax_signal_3_fully }

-        };

+    static final int[][] WIMAX_SIGNAL_STRENGTH = TelephonyIcons.DATA_SIGNAL_STRENGTH;

 

-    static final int WIMAX_DISCONNECTED =

-            R.drawable.stat_sys_data_wimax_signal_disconnected;

-    static final int WIMAX_IDLE = R.drawable.stat_sys_data_wimax_signal_idle;

-    static final int WIFI_LEVEL_COUNT = WIMAX_SIGNAL_STRENGTH[0].length;

+    static final int WIMAX_DISCONNECTED = WIMAX_SIGNAL_STRENGTH[0][0];

+

+    static final int WIMAX_IDLE = WIMAX_DISCONNECTED; // XXX: unclear if we need a different icon

 }

diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index ed9ba79..921f331 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -360,6 +360,9 @@
     int mResettingSystemUiFlags = 0;
     // Bits that we are currently always keeping cleared.
     int mForceClearedSystemUiFlags = 0;
+    // What we last reported to system UI about whether the compatibility
+    // menu needs to be displayed.
+    boolean mLastFocusNeedsMenu = false;
 
     FakeWindow mHideNavFakeWindow = null;
 
@@ -370,8 +373,6 @@
     static final Rect mTmpNavigationFrame = new Rect();
     
     WindowState mTopFullscreenOpaqueWindowState;
-    WindowState mTopAppWindowState;
-    WindowState mLastTopAppWindowState;
     boolean mTopIsFullscreen;
     boolean mForceStatusBar;
     boolean mHideLockScreen;
@@ -2250,7 +2251,6 @@
     /** {@inheritDoc} */
     public void beginAnimationLw(int displayWidth, int displayHeight) {
         mTopFullscreenOpaqueWindowState = null;
-        mTopAppWindowState = null;
         mForceStatusBar = false;
         
         mHideLockScreen = false;
@@ -2288,12 +2288,6 @@
                 }
             }
         }
-        if (mTopAppWindowState == null && win.isVisibleOrBehindKeyguardLw()) {
-            if (attrs.type >= FIRST_APPLICATION_WINDOW
-                    && attrs.type <= LAST_APPLICATION_WINDOW) {
-                mTopAppWindowState = win;
-            }
-        }
     }
 
     /** {@inheritDoc} */
@@ -2349,35 +2343,6 @@
 
         mTopIsFullscreen = topIsFullscreen;
 
-        if (mTopAppWindowState != null && mTopAppWindowState != mLastTopAppWindowState) {
-            mLastTopAppWindowState = mTopAppWindowState;
-
-            final boolean topNeedsMenu = (mTopAppWindowState.getAttrs().flags
-                    & WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY) != 0;
-
-            mHandler.post(new Runnable() {
-                    public void run() {
-                        if (mStatusBarService == null) {
-                            // This is the one that can not go away, but it doesn't come up
-                            // before the window manager does, so don't fail if it doesn't
-                            // exist. This works as long as no fullscreen windows come up
-                            // before the status bar service does.
-                            mStatusBarService = IStatusBarService.Stub.asInterface(
-                                    ServiceManager.getService("statusbar"));
-                        }
-                        final IStatusBarService sbs = mStatusBarService;
-                        if (mStatusBarService != null) {
-                            try {
-                                sbs.topAppWindowChanged(topNeedsMenu);
-                            } catch (RemoteException e) {
-                                // This should be impossible because we're in the same process.
-                                mStatusBarService = null;
-                            }
-                        }
-                    }
-                });
-        }
-
         // Hide the key guard if a visible window explicitly specifies that it wants to be displayed
         // when the screen is locked
         if (mKeyguard != null) {
@@ -3711,10 +3676,13 @@
                 & ~mResettingSystemUiFlags
                 & ~mForceClearedSystemUiFlags;
         int diff = visibility ^ mLastSystemUiFlags;
-        if (diff == 0) {
+        final boolean needsMenu = (mFocusedWindow.getAttrs().flags
+                & WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY) != 0;
+        if (diff == 0 && mLastFocusNeedsMenu == needsMenu) {
             return 0;
         }
         mLastSystemUiFlags = visibility;
+        mLastFocusNeedsMenu = needsMenu;
         mHandler.post(new Runnable() {
                 public void run() {
                     if (mStatusBarService == null) {
@@ -3724,6 +3692,7 @@
                     if (mStatusBarService != null) {
                         try {
                             mStatusBarService.setSystemUiVisibility(visibility);
+                            mStatusBarService.topAppWindowChanged(needsMenu);
                         } catch (RemoteException e) {
                             // not much to be done
                             mStatusBarService = null;
@@ -3756,6 +3725,10 @@
                     pw.print(" mForceClearedSystemUiFlags=0x");
                     pw.println(Integer.toHexString(mForceClearedSystemUiFlags));
         }
+        if (mLastFocusNeedsMenu) {
+            pw.print(prefix); pw.print("mLastFocusNeedsMenu=");
+                    pw.println(mLastFocusNeedsMenu);
+        }
         pw.print(prefix); pw.print("mUiMode="); pw.print(mUiMode);
                 pw.print(" mDockMode="); pw.print(mDockMode);
                 pw.print(" mCarDockRotation="); pw.print(mCarDockRotation);
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 4af6112..6e4aca7 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -514,7 +514,9 @@
                 continue;
             }
             mCurrentLinkProperties[netType] = null;
-            if (mNetConfigs[netType].isDefault()) mNetTrackers[netType].reconnect();
+            if (mNetTrackers[netType] != null && mNetConfigs[netType].isDefault()) {
+                mNetTrackers[netType].reconnect();
+            }
         }
 
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
@@ -1595,6 +1597,7 @@
                 if (checkType == prevNetType) continue;
                 if (mNetConfigs[checkType] == null) continue;
                 if (!mNetConfigs[checkType].isDefault()) continue;
+                if (mNetTrackers[checkType] == null) continue;
 
 // Enabling the isAvailable() optimization caused mobile to not get
 // selected if it was in the middle of error handling. Specifically
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 05d42ada..cd63090 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1669,7 +1669,7 @@
     final void setFocusedActivityLocked(ActivityRecord r) {
         if (mFocusedActivity != r) {
             mFocusedActivity = r;
-            mWindowManager.setFocusedApp(r, true);
+            mWindowManager.setFocusedApp(r.appToken, true);
         }
     }
 
@@ -2346,7 +2346,8 @@
             // XXX we are not dealing with propagating grantedUriPermissions...
             // those are not yet exposed to user code, so there is no need.
             int res = mMainStack.startActivityLocked(r.app.thread, intent,
-                    r.resolvedType, null, 0, aInfo, resultTo, resultWho,
+                    r.resolvedType, null, 0, aInfo,
+                    resultTo != null ? resultTo.appToken : null, resultWho,
                     requestCode, -1, r.launchedFromUid, false, false, null);
             Binder.restoreCallingIdentity(origId);
 
@@ -2429,10 +2430,10 @@
                 return;
             }
             final long origId = Binder.clearCallingIdentity();
-            mWindowManager.setAppOrientation(r, requestedOrientation);
+            mWindowManager.setAppOrientation(r.appToken, requestedOrientation);
             Configuration config = mWindowManager.updateOrientationFromAppTokens(
                     mConfiguration,
-                    r.mayFreezeScreenLocked(r.app) ? r : null);
+                    r.mayFreezeScreenLocked(r.app) ? r.appToken : null);
             if (config != null) {
                 r.frozenBeforeDestroy = true;
                 if (!updateConfigurationLocked(config, r, false)) {
@@ -2449,7 +2450,7 @@
             if (r == null) {
                 return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
             }
-            return mWindowManager.getAppOrientation(r);
+            return mWindowManager.getAppOrientation(r.appToken);
         }
     }
 
@@ -2515,7 +2516,7 @@
             for (int i=0; i<activities.size(); i++) {
                 ActivityRecord r = activities.get(i);
                 if (!r.finishing) {
-                    int index = mMainStack.indexOfTokenLocked(r);
+                    int index = mMainStack.indexOfTokenLocked(r.appToken);
                     if (index >= 0) {
                         mMainStack.finishActivityLocked(r, index, Activity.RESULT_CANCELED,
                                 null, "finish-heavy");
@@ -2617,7 +2618,7 @@
             int i;
             for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
                 ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
-                if (r == token) {
+                if (r.appToken == token) {
                     return true;
                 }
                 if (r.fullscreen && !r.finishing) {
@@ -2705,9 +2706,9 @@
                     r.makeFinishing();
                     mMainStack.mHistory.remove(i);
                     r.takeFromHistory();
-                    mWindowManager.removeAppToken(r);
+                    mWindowManager.removeAppToken(r.appToken);
                     if (VALIDATE_TOKENS) {
-                        mWindowManager.validateAppTokens(mMainStack.mHistory);
+                        mMainStack.validateAppTokensLocked();
                     }
                     r.removeUriPermissionsLocked();
 
@@ -5173,10 +5174,10 @@
         if (topThumbnail != null) {
             if (localLOGV) Slog.v(TAG, "Requesting top thumbnail");
             try {
-                topThumbnail.requestThumbnail(topRecord);
+                topThumbnail.requestThumbnail(topRecord.appToken);
             } catch (Exception e) {
                 Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
-                sendPendingThumbnail(null, topRecord, null, null, true);
+                sendPendingThumbnail(null, topRecord.appToken, null, null, true);
             }
         }
 
@@ -5547,7 +5548,7 @@
         TaskRecord lastTask = null;
         for (int i=0; i<N; i++) {
             ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
-            if (r == token) {
+            if (r.appToken == token) {
                 if (!onlyRoot || lastTask != r.task) {
                     return r.task.taskId;
                 }
@@ -5568,7 +5569,7 @@
             for (int i=0; i<N; i++) {
                 ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
                 if (r.realActivity.equals(className)
-                        && r != token && lastTask != r.task) {
+                        && r.appToken != token && lastTask != r.task) {
                     if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED,
                             null, "others")) {
                         i--;
@@ -7112,7 +7113,7 @@
                 // process, then terminate it to avoid getting in a loop.
                 Slog.w(TAG, "  Force finishing activity "
                         + r.intent.getComponent().flattenToShortString());
-                int index = mMainStack.indexOfTokenLocked(r);
+                int index = mMainStack.indexOfActivityLocked(r);
                 r.stack.finishActivityLocked(r, index,
                         Activity.RESULT_CANCELED, null, "crashed");
                 // Also terminate any activities below it that aren't yet
@@ -8631,8 +8632,8 @@
             try {
                 TransferPipe tp = new TransferPipe();
                 try {
-                    r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(), r,
-                            innerPrefix, args);
+                    r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(),
+                            r.appToken, innerPrefix, args);
                     tp.go(fd);
                 } finally {
                     tp.kill();
@@ -9048,8 +9049,8 @@
                 try {
                     TransferPipe tp = new TransferPipe();
                     try {
-                        r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(), r,
-                                innerPrefix, args);
+                        r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(),
+                                r.appToken, innerPrefix, args);
                         // Short timeout, since blocking here can
                         // deadlock with the application.
                         tp.go(fd, 2000);
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 00e6cb2..951a946 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -29,6 +29,7 @@
 import android.graphics.Bitmap;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
@@ -48,9 +49,10 @@
 /**
  * An entry in the history stack, representing an activity.
  */
-final class ActivityRecord extends IApplicationToken.Stub {
+final class ActivityRecord {
     final ActivityManagerService service; // owner
     final ActivityStack stack; // owner
+    final IApplicationToken.Stub appToken; // window manager token
     final ActivityInfo info; // all about me
     final int launchedFromUid; // always the uid who started the activity.
     final Intent intent;    // the original intent that generated us
@@ -200,6 +202,70 @@
         }
     }
 
+    static class Token extends IApplicationToken.Stub {
+        final WeakReference<ActivityRecord> weakActivity;
+
+        Token(ActivityRecord activity) {
+            weakActivity = new WeakReference<ActivityRecord>(activity);
+        }
+
+        @Override public void windowsDrawn() throws RemoteException {
+            ActivityRecord activity = weakActivity.get();
+            if (activity != null) {
+                activity.windowsDrawn();
+            }
+        }
+
+        @Override public void windowsVisible() throws RemoteException {
+            ActivityRecord activity = weakActivity.get();
+            if (activity != null) {
+                activity.windowsVisible();
+            }
+        }
+
+        @Override public void windowsGone() throws RemoteException {
+            ActivityRecord activity = weakActivity.get();
+            if (activity != null) {
+                activity.windowsGone();
+            }
+        }
+
+        @Override public boolean keyDispatchingTimedOut() throws RemoteException {
+            ActivityRecord activity = weakActivity.get();
+            if (activity != null) {
+                return activity.keyDispatchingTimedOut();
+            }
+            return false;
+        }
+
+        @Override public long getKeyDispatchingTimeout() throws RemoteException {
+            ActivityRecord activity = weakActivity.get();
+            if (activity != null) {
+                return activity.getKeyDispatchingTimeout();
+            }
+            return 0;
+        }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("Token{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(' ');
+            sb.append(weakActivity.get());
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    static ActivityRecord forToken(IBinder token) {
+        try {
+            return token != null ? ((Token)token).weakActivity.get() : null;
+        } catch (ClassCastException e) {
+            Slog.w(ActivityManagerService.TAG, "Bad activity token: " + token, e);
+            return null;
+        }
+    }
+
     ActivityRecord(ActivityManagerService _service, ActivityStack _stack, ProcessRecord _caller,
             int _launchedFromUid, Intent _intent, String _resolvedType,
             ActivityInfo aInfo, Configuration _configuration,
@@ -207,6 +273,7 @@
             boolean _componentSpecified) {
         service = _service;
         stack = _stack;
+        appToken = new Token(this);
         info = aInfo;
         launchedFromUid = _launchedFromUid;
         intent = _intent;
@@ -445,7 +512,7 @@
                 ar.add(intent);
                 service.grantUriPermissionFromIntentLocked(callingUid, packageName,
                         intent, getUriPermissionsLocked());
-                app.thread.scheduleNewIntent(ar, this);
+                app.thread.scheduleNewIntent(ar, appToken);
                 sent = true;
             } catch (RemoteException e) {
                 Slog.w(ActivityManagerService.TAG,
@@ -470,14 +537,14 @@
     void pauseKeyDispatchingLocked() {
         if (!keysPaused) {
             keysPaused = true;
-            service.mWindowManager.pauseKeyDispatching(this);
+            service.mWindowManager.pauseKeyDispatching(appToken);
         }
     }
 
     void resumeKeyDispatchingLocked() {
         if (keysPaused) {
             keysPaused = false;
-            service.mWindowManager.resumeKeyDispatching(this);
+            service.mWindowManager.resumeKeyDispatching(appToken);
         }
     }
 
@@ -512,14 +579,14 @@
     
     public void startFreezingScreenLocked(ProcessRecord app, int configChanges) {
         if (mayFreezeScreenLocked(app)) {
-            service.mWindowManager.startAppFreezingScreen(this, configChanges);
+            service.mWindowManager.startAppFreezingScreen(appToken, configChanges);
         }
     }
     
     public void stopFreezingScreenLocked(boolean force) {
         if (force || frozenBeforeDestroy) {
             frozenBeforeDestroy = false;
-            service.mWindowManager.stopAppFreezingScreen(this, force);
+            service.mWindowManager.stopAppFreezingScreen(appToken, force);
         }
     }
     
@@ -687,7 +754,7 @@
         }
         if (app != null && app.thread != null) {
             try {
-                app.thread.scheduleSleeping(this, _sleeping);
+                app.thread.scheduleSleeping(appToken, _sleeping);
                 if (sleeping && !stack.mGoingToSleepActivities.contains(this)) {
                     stack.mGoingToSleepActivities.add(this);
                 }
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 8435eaa..c892cb1 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -145,7 +145,12 @@
      * running) activities.  It contains HistoryRecord objects.
      */
     final ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>();
-    
+
+    /**
+     * Used for validating app tokens with window manager.
+     */
+    final ArrayList<IBinder> mValidateAppTokens = new ArrayList<IBinder>();
+
     /**
      * List of running activities, sorted by recent usage.
      * The first entry in the list is the least recently used.
@@ -294,11 +299,11 @@
                     }
                 } break;
                 case PAUSE_TIMEOUT_MSG: {
-                    IBinder token = (IBinder)msg.obj;
+                    ActivityRecord r = (ActivityRecord)msg.obj;
                     // We don't at this point know if the activity is fullscreen,
                     // so we need to be conservative and assume it isn't.
-                    Slog.w(TAG, "Activity pause timeout for " + token);
-                    activityPaused(token, true);
+                    Slog.w(TAG, "Activity pause timeout for " + r);
+                    activityPaused(r != null ? r.appToken : null, true);
                 } break;
                 case IDLE_TIMEOUT_MSG: {
                     if (mService.mDidDexOpt) {
@@ -310,20 +315,20 @@
                     }
                     // We don't at this point know if the activity is fullscreen,
                     // so we need to be conservative and assume it isn't.
-                    IBinder token = (IBinder)msg.obj;
-                    Slog.w(TAG, "Activity idle timeout for " + token);
-                    activityIdleInternal(token, true, null);
+                    ActivityRecord r = (ActivityRecord)msg.obj;
+                    Slog.w(TAG, "Activity idle timeout for " + r);
+                    activityIdleInternal(r != null ? r.appToken : null, true, null);
                 } break;
                 case DESTROY_TIMEOUT_MSG: {
-                    IBinder token = (IBinder)msg.obj;
+                    ActivityRecord r = (ActivityRecord)msg.obj;
                     // We don't at this point know if the activity is fullscreen,
                     // so we need to be conservative and assume it isn't.
-                    Slog.w(TAG, "Activity destroy timeout for " + token);
-                    activityDestroyed(token);
+                    Slog.w(TAG, "Activity destroy timeout for " + r);
+                    activityDestroyed(r != null ? r.appToken : null);
                 } break;
                 case IDLE_NOW_MSG: {
-                    IBinder token = (IBinder)msg.obj;
-                    activityIdleInternal(token, false, null);
+                    ActivityRecord r = (ActivityRecord)msg.obj;
+                    activityIdleInternal(r != null ? r.appToken : null, false, null);
                 } break;
                 case LAUNCH_TIMEOUT_MSG: {
                     if (mService.mDidDexOpt) {
@@ -397,7 +402,7 @@
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
             // Note: the taskId check depends on real taskId fields being non-zero
-            if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
+            if (!r.finishing && (token != r.appToken) && (taskId != r.task.taskId)) {
                 return r;
             }
             i--;
@@ -406,23 +411,17 @@
     }
 
     final int indexOfTokenLocked(IBinder token) {
-        try {
-            ActivityRecord r = (ActivityRecord)token;
-            return mHistory.indexOf(r);
-        } catch (ClassCastException e) {
-            Slog.w(TAG, "Bad activity token: " + token, e);
-            return -1;
-        }
+        return mHistory.indexOf(ActivityRecord.forToken(token));
+    }
+
+    final int indexOfActivityLocked(ActivityRecord r) {
+        return mHistory.indexOf(r);
     }
 
     final ActivityRecord isInStackLocked(IBinder token) {
-        try {
-            ActivityRecord r = (ActivityRecord)token;
-            if (mHistory.contains(r)) {
-                return r;
-            }
-        } catch (ClassCastException e) {
-            Slog.w(TAG, "Bad activity token: " + token, e);
+        ActivityRecord r = ActivityRecord.forToken(token);
+        if (mHistory.contains(r)) {
+            return r;
         }
         return null;
     }
@@ -517,7 +516,7 @@
             throws RemoteException {
 
         r.startFreezingScreenLocked(app, 0);
-        mService.mWindowManager.setAppVisibility(r, true);
+        mService.mWindowManager.setAppVisibility(r.appToken, true);
 
         // Have the window manager re-evaluate the orientation of
         // the screen based on the new activity order.  Note that
@@ -528,7 +527,7 @@
         if (checkConfig) {
             Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
                     mService.mConfiguration,
-                    r.mayFreezeScreenLocked(app) ? r : null);
+                    r.mayFreezeScreenLocked(app) ? r.appToken : null);
             mService.updateConfigurationLocked(config, r, false);
         }
 
@@ -590,7 +589,7 @@
                     profileFd = null;
                 }
             }
-            app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
+            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                     System.identityHashCode(r), r.info, mService.mConfiguration,
                     r.compat, r.icicle, results, newIntents, !andResume,
                     mService.isNextTransitionForward(), profileFile, profileFd,
@@ -624,7 +623,7 @@
                       + r.intent.getComponent().flattenToShortString()
                       + ", giving up", e);
                 mService.appDiedLocked(app, app.pid, app.thread);
-                requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
+                requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
                         "2nd-crash");
                 return false;
             }
@@ -821,7 +820,7 @@
         }
 
         if (w > 0) {
-            return mService.mWindowManager.screenshotApplications(who, w, h);
+            return mService.mWindowManager.screenshotApplications(who.appToken, w, h);
         }
         return null;
     }
@@ -856,8 +855,8 @@
                 EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
                         System.identityHashCode(prev),
                         prev.shortComponentName);
-                prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving,
-                        prev.configChangeFlags);
+                prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
+                        userLeaving, prev.configChangeFlags);
                 if (mMainStack) {
                     mService.updateUsageStats(prev, false);
                 }
@@ -1129,7 +1128,7 @@
                     if (!r.visible) {
                         if (DEBUG_VISBILITY) Slog.v(
                                 TAG, "Starting and making visible: " + r);
-                        mService.mWindowManager.setAppVisibility(r, true);
+                        mService.mWindowManager.setAppVisibility(r.appToken, true);
                     }
                     if (r != starting) {
                         startSpecificActivityLocked(r, false, false);
@@ -1153,10 +1152,10 @@
                     if (DEBUG_VISBILITY) Slog.v(
                             TAG, "Making visible and scheduling visibility: " + r);
                     try {
-                        mService.mWindowManager.setAppVisibility(r, true);
+                        mService.mWindowManager.setAppVisibility(r.appToken, true);
                         r.sleeping = false;
                         r.app.pendingUiClean = true;
-                        r.app.thread.scheduleWindowVisibility(r, true);
+                        r.app.thread.scheduleWindowVisibility(r.appToken, true);
                         r.stopFreezingScreenLocked(false);
                     } catch (Exception e) {
                         // Just skip on any failure; we'll make it
@@ -1195,13 +1194,13 @@
                                 TAG, "Making invisible: " + r);
                         r.visible = false;
                         try {
-                            mService.mWindowManager.setAppVisibility(r, false);
+                            mService.mWindowManager.setAppVisibility(r.appToken, false);
                             if ((r.state == ActivityState.STOPPING
                                     || r.state == ActivityState.STOPPED)
                                     && r.app != null && r.app.thread != null) {
                                 if (DEBUG_VISBILITY) Slog.v(
                                         TAG, "Scheduling invisibility: " + r);
-                                r.app.thread.scheduleWindowVisibility(r, false);
+                                r.app.thread.scheduleWindowVisibility(r.appToken, false);
                             }
                         } catch (Exception e) {
                             // Just skip on any failure; we'll make it
@@ -1351,7 +1350,7 @@
                 // previous should actually be hidden depending on whether the
                 // new one is found to be full-screen or not.
                 if (prev.finishing) {
-                    mService.mWindowManager.setAppVisibility(prev, false);
+                    mService.mWindowManager.setAppVisibility(prev.appToken, false);
                     if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: "
                             + prev + ", waitingVisible="
                             + (prev != null ? prev.waitingVisible : null)
@@ -1399,8 +1398,8 @@
                             ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE
                             : WindowManagerPolicy.TRANSIT_TASK_CLOSE, false);
                 }
-                mService.mWindowManager.setAppWillBeHidden(prev);
-                mService.mWindowManager.setAppVisibility(prev, false);
+                mService.mWindowManager.setAppWillBeHidden(prev.appToken);
+                mService.mWindowManager.setAppVisibility(prev.appToken, false);
             } else {
                 if (DEBUG_TRANSITION) Slog.v(TAG,
                         "Prepare open transition: prev=" + prev);
@@ -1414,8 +1413,8 @@
                 }
             }
             if (false) {
-                mService.mWindowManager.setAppWillBeHidden(prev);
-                mService.mWindowManager.setAppVisibility(prev, false);
+                mService.mWindowManager.setAppWillBeHidden(prev.appToken);
+                mService.mWindowManager.setAppVisibility(prev.appToken, false);
             }
         } else if (mHistory.size() > 1) {
             if (DEBUG_TRANSITION) Slog.v(TAG,
@@ -1433,7 +1432,7 @@
             if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
 
             // This activity is now becoming visible.
-            mService.mWindowManager.setAppVisibility(next, true);
+            mService.mWindowManager.setAppVisibility(next.appToken, true);
 
             ActivityRecord lastResumedActivity = mResumedActivity;
             ActivityState lastState = next.state;
@@ -1457,7 +1456,7 @@
                 synchronized (mService) {
                     Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
                             mService.mConfiguration,
-                            next.mayFreezeScreenLocked(next.app) ? next : null);
+                            next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
                     if (config != null) {
                         next.frozenBeforeDestroy = true;
                     }
@@ -1496,12 +1495,12 @@
                         if (DEBUG_RESULTS) Slog.v(
                                 TAG, "Delivering results to " + next
                                 + ": " + a);
-                        next.app.thread.scheduleSendResult(next, a);
+                        next.app.thread.scheduleSendResult(next.appToken, a);
                     }
                 }
 
                 if (next.newIntents != null) {
-                    next.app.thread.scheduleNewIntent(next.newIntents, next);
+                    next.app.thread.scheduleNewIntent(next.newIntents, next.appToken);
                 }
 
                 EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY,
@@ -1511,7 +1510,7 @@
                 next.sleeping = false;
                 showAskCompatModeDialogLocked(next);
                 next.app.pendingUiClean = true;
-                next.app.thread.scheduleResumeActivity(next,
+                next.app.thread.scheduleResumeActivity(next.appToken,
                         mService.isNextTransitionForward());
                 
                 checkReadyForSleepLocked();
@@ -1528,7 +1527,7 @@
                 } else {
                     if (SHOW_APP_STARTING_PREVIEW && mMainStack) {
                         mService.mWindowManager.setAppStartingWindow(
-                                next, next.packageName, next.theme,
+                                next.appToken, next.packageName, next.theme,
                                 mService.compatibilityInfoForPackageLocked(
                                         next.info.applicationInfo),
                                 next.nonLocalizedLabel,
@@ -1549,7 +1548,7 @@
                 // If any exception gets thrown, toss away this
                 // activity and try the next one.
                 Slog.w(TAG, "Exception thrown during resume of " + next, e);
-                requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null,
+                requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
                         "resume-exception");
                 return true;
             }
@@ -1567,7 +1566,7 @@
             } else {
                 if (SHOW_APP_STARTING_PREVIEW) {
                     mService.mWindowManager.setAppStartingWindow(
-                            next, next.packageName, next.theme,
+                            next.appToken, next.packageName, next.theme,
                             mService.compatibilityInfoForPackageLocked(
                                     next.info.applicationInfo),
                             next.nonLocalizedLabel,
@@ -1610,10 +1609,10 @@
                         }
                         mHistory.add(addPos, r);
                         r.putInHistory();
-                        mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
+                        mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId,
                                 r.info.screenOrientation, r.fullscreen);
                         if (VALIDATE_TOKENS) {
-                            mService.mWindowManager.validateAppTokens(mHistory);
+                            validateAppTokensLocked();
                         }
                         return;
                     }
@@ -1677,7 +1676,7 @@
                 mNoAnimActivities.remove(r);
             }
             mService.mWindowManager.addAppToken(
-                    addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen);
+                    addPos, r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen);
             boolean doShow = true;
             if (newTask) {
                 // Even though this activity is starting fresh, we still need
@@ -1705,19 +1704,20 @@
                     else if (prev.nowVisible) prev = null;
                 }
                 mService.mWindowManager.setAppStartingWindow(
-                        r, r.packageName, r.theme,
+                        r.appToken, r.packageName, r.theme,
                         mService.compatibilityInfoForPackageLocked(
                                 r.info.applicationInfo), r.nonLocalizedLabel,
-                        r.labelRes, r.icon, r.windowFlags, prev, showStartingIcon);
+                        r.labelRes, r.icon, r.windowFlags,
+                        prev != null ? prev.appToken : null, showStartingIcon);
             }
         } else {
             // If this is the first activity, don't do any fancy animations,
             // because there is nothing for it to animate on top of.
-            mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
+            mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId,
                     r.info.screenOrientation, r.fullscreen);
         }
         if (VALIDATE_TOKENS) {
-            mService.mWindowManager.validateAppTokens(mHistory);
+            validateAppTokensLocked();
         }
 
         if (doResume) {
@@ -1725,6 +1725,15 @@
         }
     }
 
+    final void validateAppTokensLocked() {
+        mValidateAppTokens.clear();
+        mValidateAppTokens.ensureCapacity(mHistory.size());
+        for (int i=0; i<mHistory.size(); i++) {
+            mValidateAppTokens.add(mHistory.get(i).appToken);
+        }
+        mService.mWindowManager.validateAppTokens(mValidateAppTokens);
+    }
+
     /**
      * Perform a reset of the given task, if needed as part of launching it.
      * Returns the new HistoryRecord at the top of the task.
@@ -1826,7 +1835,7 @@
                             if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
                                     + " out to new task " + target.task);
                         }
-                        mService.mWindowManager.setAppGroupId(target, task.taskId);
+                        mService.mWindowManager.setAppGroupId(target.appToken, task.taskId);
                         if (replyChainEnd < 0) {
                             replyChainEnd = targetI;
                         }
@@ -1849,11 +1858,11 @@
                             }
                             mHistory.remove(srcPos);
                             mHistory.add(dstPos, p);
-                            mService.mWindowManager.moveAppToken(dstPos, p);
-                            mService.mWindowManager.setAppGroupId(p, p.task.taskId);
+                            mService.mWindowManager.moveAppToken(dstPos, p.appToken);
+                            mService.mWindowManager.setAppGroupId(p.appToken, p.task.taskId);
                             dstPos++;
                             if (VALIDATE_TOKENS) {
-                                mService.mWindowManager.validateAppTokens(mHistory);
+                                validateAppTokensLocked();
                             }
                             i++;
                         }
@@ -1985,10 +1994,10 @@
                         mHistory.add(lastReparentPos, p);
                         if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
                                 + " in to resetting task " + task);
-                        mService.mWindowManager.moveAppToken(lastReparentPos, p);
-                        mService.mWindowManager.setAppGroupId(p, p.task.taskId);
+                        mService.mWindowManager.moveAppToken(lastReparentPos, p.appToken);
+                        mService.mWindowManager.setAppGroupId(p.appToken, p.task.taskId);
                         if (VALIDATE_TOKENS) {
-                            mService.mWindowManager.validateAppTokens(mHistory);
+                            validateAppTokensLocked();
                         }
                     }
                     replyChainEnd = -1;
@@ -2081,7 +2090,7 @@
                 if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
                         && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
                     if (!ret.finishing) {
-                        int index = indexOfTokenLocked(ret);
+                        int index = indexOfTokenLocked(ret.appToken);
                         if (index >= 0) {
                             finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
                                     null, "clear");
@@ -3007,7 +3016,7 @@
                         return res;
                     }
 
-                    resultTo = outActivity[0];
+                    resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
                 }
             }
         } finally {
@@ -3065,7 +3074,7 @@
                 ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
                 list.add(new ResultInfo(resultWho, requestCode,
                         resultCode, data));
-                r.app.thread.scheduleSendResult(r, list);
+                r.app.thread.scheduleSendResult(r.appToken, list);
                 return;
             } catch (Exception e) {
                 Slog.w(TAG, "Exception thrown sending result to " + r, e);
@@ -3080,7 +3089,7 @@
         if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                 || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
             if (!r.finishing) {
-                requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
+                requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
                         "no-history");
             }
         } else if (r.app != null && r.app.thread != null) {
@@ -3098,9 +3107,9 @@
                 if (DEBUG_VISBILITY) Slog.v(
                         TAG, "Stopping visible=" + r.visible + " for " + r);
                 if (!r.visible) {
-                    mService.mWindowManager.setAppVisibility(r, false);
+                    mService.mWindowManager.setAppVisibility(r.appToken, false);
                 }
-                r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags);
+                r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);
                 if (mService.isSleeping()) {
                     r.setSleeping(true);
                 }
@@ -3145,7 +3154,7 @@
                     // normal flow and hide it once we determine that it is
                     // hidden by the activities in front of it.
                     if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s);
-                    mService.mWindowManager.setAppVisibility(s, false);
+                    mService.mWindowManager.setAppVisibility(s.appToken, false);
                 }
             }
             if ((!s.waitingVisible || mService.isSleeping()) && remove) {
@@ -3186,14 +3195,14 @@
         boolean enableScreen = false;
 
         synchronized (mService) {
-            if (token != null) {
-                mHandler.removeMessages(IDLE_TIMEOUT_MSG, token);
+            ActivityRecord r = ActivityRecord.forToken(token);
+            if (r != null) {
+                mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
             }
 
             // Get the activity record.
-            int index = indexOfTokenLocked(token);
+            int index = indexOfActivityLocked(r);
             if (index >= 0) {
-                ActivityRecord r = mHistory.get(index);
                 res = r;
 
                 if (fromTimeout) {
@@ -3413,7 +3422,7 @@
                     : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE, false);
     
             // Tell window manager to prepare for this one to be removed.
-            mService.mWindowManager.setAppVisibility(r, false);
+            mService.mWindowManager.setAppVisibility(r.appToken, false);
                 
             if (mPausingActivity == null) {
                 if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
@@ -3440,7 +3449,7 @@
 
     private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
             int mode) {
-        final int index = indexOfTokenLocked(r);
+        final int index = indexOfActivityLocked(r);
         if (index < 0) {
             return null;
         }
@@ -3570,9 +3579,9 @@
             if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r
                     + " (removed from history)");
             r.state = ActivityState.DESTROYED;
-            mService.mWindowManager.removeAppToken(r);
+            mService.mWindowManager.removeAppToken(r.appToken);
             if (VALIDATE_TOKENS) {
-                mService.mWindowManager.validateAppTokens(mHistory);
+                validateAppTokensLocked();
             }
             cleanUpActivityServicesLocked(r);
             r.removeUriPermissionsLocked();
@@ -3653,7 +3662,7 @@
             
             try {
                 if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
-                r.app.thread.scheduleDestroyActivity(r, r.finishing,
+                r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing,
                         r.configChangeFlags);
             } catch (Exception e) {
                 // We can just ignore exceptions here...  if the process
@@ -3712,11 +3721,13 @@
 
     final void activityDestroyed(IBinder token) {
         synchronized (mService) {
-            mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token);
+            ActivityRecord r = ActivityRecord.forToken(token);
+            if (r != null) {
+                mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
+            }
             
-            int index = indexOfTokenLocked(token);
+            int index = indexOfActivityLocked(r);
             if (index >= 0) {
-                ActivityRecord r = mHistory.get(index);
                 if (r.state == ActivityState.DESTROYING) {
                     final long origId = Binder.clearCallingIdentity();
                     removeActivityFromHistoryLocked(r);
@@ -3781,7 +3792,7 @@
             return;
         }
 
-        ArrayList moved = new ArrayList();
+        ArrayList<IBinder> moved = new ArrayList<IBinder>();
 
         // Applying the affinities may have removed entries from the history,
         // so get the size again.
@@ -3803,7 +3814,7 @@
                 }
                 mHistory.remove(pos);
                 mHistory.add(top, r);
-                moved.add(0, r);
+                moved.add(0, r.appToken);
                 top--;
             }
             pos--;
@@ -3826,7 +3837,7 @@
         
         mService.mWindowManager.moveAppTokensToTop(moved);
         if (VALIDATE_TOKENS) {
-            mService.mWindowManager.validateAppTokens(mHistory);
+            validateAppTokensLocked();
         }
 
         finishTaskMoveLocked(task);
@@ -3873,7 +3884,7 @@
             }
         }
 
-        ArrayList moved = new ArrayList();
+        ArrayList<IBinder> moved = new ArrayList<IBinder>();
 
         if (DEBUG_TRANSITION) Slog.v(TAG,
                 "Prepare to back transition: task=" + task);
@@ -3898,7 +3909,7 @@
                 }
                 mHistory.remove(pos);
                 mHistory.add(bottom, r);
-                moved.add(r);
+                moved.add(r.appToken);
                 bottom++;
             }
             pos++;
@@ -3918,7 +3929,7 @@
         }
         mService.mWindowManager.moveAppTokensToBottom(moved);
         if (VALIDATE_TOKENS) {
-            mService.mWindowManager.validateAppTokens(mHistory);
+            validateAppTokensLocked();
         }
 
         finishTaskMoveLocked(task);
@@ -4148,7 +4159,7 @@
         if (r.app != null && r.app.thread != null) {
             try {
                 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);
-                r.app.thread.scheduleActivityConfigurationChanged(r);
+                r.app.thread.scheduleActivityConfigurationChanged(r.appToken);
             } catch (RemoteException e) {
                 // If process died, whatever.
             }
@@ -4178,7 +4189,7 @@
         try {
             if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r);
             r.forceNewConfig = false;
-            r.app.thread.scheduleRelaunchActivity(r, results, newIntents,
+            r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents,
                     changes, !andResume, mService.mConfiguration);
             // Note: don't need to call pauseIfSleepingLocked() here, because
             // the caller will only pass in 'andResume' if this activity is
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 423a78f..c344bc6 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -73,7 +73,7 @@
     private Context mContext;
     private final static String TAG = "Tethering";
     private final static boolean DBG = true;
-    private final static boolean VDBG = true;
+    private final static boolean VDBG = false;
 
     // TODO - remove both of these - should be part of interface inspection/selection stuff
     private String[] mTetherableUsbRegexs;
@@ -228,7 +228,7 @@
                 if (isUsb(iface)) {
                     // ignore usb0 down after enabling RNDIS
                     // we will handle disconnect in interfaceRemoved instead
-                    if (VDBG) Log.d(TAG, "ignoring interface down for " + iface);
+                    if (VDBG) Log.d(TAG, "ignore interface down for " + iface);
                 } else if (sm != null) {
                     sm.sendMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN);
                     mIfaces.remove(iface);
@@ -298,7 +298,6 @@
             mIfaces.put(iface, sm);
             sm.start();
         }
-        if (VDBG) Log.d(TAG, "interfaceAdded :" + iface);
     }
 
     public void interfaceRemoved(String iface) {
@@ -415,7 +414,7 @@
         broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
                 erroredList);
         mContext.sendStickyBroadcast(broadcast);
-        if (VDBG) {
+        if (DBG) {
             Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
                     activeList.size() + ", " + erroredList.size());
         }
@@ -865,7 +864,7 @@
 
             @Override
             public boolean processMessage(Message message) {
-                if (VDBG) Log.d(TAG, "InitialState.processMessage what=" + message.what);
+                if (DBG) Log.d(TAG, "InitialState.processMessage what=" + message.what);
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_REQUESTED:
@@ -906,7 +905,7 @@
             }
             @Override
             public boolean processMessage(Message message) {
-                if (VDBG) Log.d(TAG, "StartingState.processMessage what=" + message.what);
+                if (DBG) Log.d(TAG, "StartingState.processMessage what=" + message.what);
                 boolean retValue = true;
                 switch (message.what) {
                     // maybe a parent class?
@@ -985,7 +984,7 @@
 
             @Override
             public boolean processMessage(Message message) {
-                if (VDBG) Log.d(TAG, "TetheredState.processMessage what=" + message.what);
+                if (DBG) Log.d(TAG, "TetheredState.processMessage what=" + message.what);
                 boolean retValue = true;
                 boolean error = false;
                 switch (message.what) {
@@ -1061,7 +1060,7 @@
                                     ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
                             break;
                         }
-                        if (VDBG) Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
+                        if (DBG) Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
                         sendTetherStateChangedBroadcast();
                         if (mUsb) {
                             if (!Tethering.this.configureUsbIface(false)) {
@@ -1296,7 +1295,7 @@
                     }
                 }
 
-                if (VDBG) {
+                if (DBG) {
                     Log.d(TAG, "chooseUpstreamType(" + tryCell + "), preferredApn ="
                             + mPreferredUpstreamMobileApn + ", got type=" + upType);
                 }
@@ -1328,7 +1327,7 @@
             }
 
             protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
-                if (VDBG) Log.d(TAG, "notifying tethered with iface =" + ifaceName);
+                if (DBG) Log.d(TAG, "notifying tethered with iface =" + ifaceName);
                 mUpstreamIfaceName = ifaceName;
                 for (Object o : mNotifyList) {
                     TetherInterfaceSM sm = (TetherInterfaceSM)o;
@@ -1344,7 +1343,7 @@
             }
             @Override
             public boolean processMessage(Message message) {
-                if (VDBG) Log.d(TAG, "MasterInitialState.processMessage what=" + message.what);
+                if (DBG) Log.d(TAG, "MasterInitialState.processMessage what=" + message.what);
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_MODE_REQUESTED:
@@ -1386,7 +1385,7 @@
             }
             @Override
             public boolean processMessage(Message message) {
-                if (VDBG) Log.d(TAG, "TetherModeAliveState.processMessage what=" + message.what);
+                if (DBG) Log.d(TAG, "TetherModeAliveState.processMessage what=" + message.what);
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_MODE_REQUESTED:
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 77b0d96..6365525 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -806,9 +806,7 @@
         final NetworkStats networkDevSnapshot;
         try {
             // collect any tethering stats
-            final String[] tetheredIfacePairs = mConnManager.getTetheredIfacePairs();
-            final NetworkStats tetherSnapshot = mNetworkManager.getNetworkStatsTethering(
-                    tetheredIfacePairs);
+            final NetworkStats tetherSnapshot = getNetworkStatsTethering();
 
             // record uid stats, folding in tethering stats
             uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
@@ -1505,7 +1503,7 @@
             NetworkStats before, NetworkStats current, boolean collectStale, String type) {
         if (before != null) {
             try {
-                return current.subtract(before);
+                return current.subtract(before, false);
             } catch (NonMonotonicException e) {
                 Log.w(TAG, "found non-monotonic values; saving to dropbox");
 
@@ -1517,8 +1515,13 @@
                 builder.append("right=").append(e.right).append('\n');
                 mDropBox.addText(TAG_NETSTATS_ERROR, builder.toString());
 
-                // return empty delta to avoid recording broken stats
-                return new NetworkStats(0L, 10);
+                try {
+                    // return clamped delta to help recover
+                    return current.subtract(before, true);
+                } catch (NonMonotonicException e1) {
+                    Log.wtf(TAG, "found non-monotonic values; returning empty delta", e1);
+                    return new NetworkStats(0L, 10);
+                }
             }
         } else if (collectStale) {
             // caller is okay collecting stale stats for first call.
@@ -1530,6 +1533,20 @@
         }
     }
 
+    /**
+     * Return snapshot of current tethering statistics. Will return empty
+     * {@link NetworkStats} if any problems are encountered.
+     */
+    private NetworkStats getNetworkStatsTethering() throws RemoteException {
+        try {
+            final String[] tetheredIfacePairs = mConnManager.getTetheredIfacePairs();
+            return mNetworkManager.getNetworkStatsTethering(tetheredIfacePairs);
+        } catch (IllegalStateException e) {
+            Log.wtf(TAG, "problem reading network stats", e);
+            return new NetworkStats(0L, 10);
+        }
+    }
+
     private static NetworkStats computeNetworkXtSnapshotFromUid(NetworkStats uidSnapshot) {
         return uidSnapshot.groupedByIface();
     }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 4ffc201..50321b3 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -3070,7 +3070,7 @@
     // Application Window Tokens
     // -------------------------------------------------------------
 
-    public void validateAppTokens(List tokens) {
+    public void validateAppTokens(List<IBinder> tokens) {
         int v = tokens.size()-1;
         int m = mAppTokens.size()-1;
         while (v >= 0 && m >= 0) {