Merge "Allow TextView to do its thing." into gingerbread
diff --git a/api/current.xml b/api/current.xml
index 2a2200d..5fd7a1b 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -131704,9 +131704,97 @@
 >
 <parameter name="path" type="java.lang.String">
 </parameter>
-<parameter name="state" type="java.lang.String">
+<parameter name="state" type="int">
 </parameter>
 </method>
+<field name="ERROR_ALREADY_MOUNTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="24"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_COULD_NOT_MOUNT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="21"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_COULD_NOT_UNMOUNT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="22"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_INTERNAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="20"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_NOT_MOUNTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="23"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_PERMISSION_DENIED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="25"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MOUNTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="UNMOUNTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="StorageManager"
  extends="java.lang.Object"
@@ -131741,8 +131829,6 @@
 >
 <parameter name="filename" type="java.lang.String">
 </parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
 </method>
 <method name="mountObb"
  return="boolean"
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 9494a06..de5b7b9 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -38,7 +38,10 @@
  * network access on the application's main thread, where UI
  * operations are received and animations take place.  Keeping disk
  * and network operations off the main thread makes for much smoother,
- * more responsive applications.
+ * more responsive applications.  By keeping your application's main thread
+ * responsive, you also prevent
+ * <a href="{@docRoot}guide/practices/design/responsiveness.html">ANR dialogs</a>
+ * from being shown to users.
  *
  * <p class="note">Note that even though an Android device's disk is
  * often on flash memory, many devices run a filesystem on top of that
@@ -104,6 +107,10 @@
     // Only show an annoying dialog at most every 30 seconds
     private static final long MIN_DIALOG_INTERVAL_MS = 30000;
 
+    // How many offending stacks to keep track of (and time) per loop
+    // of the Looper.
+    private static final int MAX_OFFENSES_PER_LOOP = 10;
+
     // Thread-policy:
 
     /**
@@ -680,6 +687,17 @@
         }
     }
 
+    private static final ThreadLocal<ArrayList<ViolationInfo>> violationsBeingTimed =
+            new ThreadLocal<ArrayList<ViolationInfo>>() {
+        @Override protected ArrayList<ViolationInfo> initialValue() {
+            return new ArrayList<ViolationInfo>();
+        }
+    };
+
+    private static boolean tooManyViolationsThisLoop() {
+        return violationsBeingTimed.get().size() >= MAX_OFFENSES_PER_LOOP;
+    }
+
     private static class AndroidBlockGuardPolicy implements BlockGuard.Policy {
         private int mPolicyMask;
 
@@ -707,6 +725,9 @@
             if ((mPolicyMask & DETECT_DISK_WRITE) == 0) {
                 return;
             }
+            if (tooManyViolationsThisLoop()) {
+                return;
+            }
             BlockGuard.BlockGuardPolicyException e = new StrictModeDiskWriteViolation(mPolicyMask);
             e.fillInStackTrace();
             startHandlingViolationException(e);
@@ -717,6 +738,9 @@
             if ((mPolicyMask & DETECT_DISK_READ) == 0) {
                 return;
             }
+            if (tooManyViolationsThisLoop()) {
+                return;
+            }
             BlockGuard.BlockGuardPolicyException e = new StrictModeDiskReadViolation(mPolicyMask);
             e.fillInStackTrace();
             startHandlingViolationException(e);
@@ -727,6 +751,9 @@
             if ((mPolicyMask & DETECT_NETWORK) == 0) {
                 return;
             }
+            if (tooManyViolationsThisLoop()) {
+                return;
+            }
             BlockGuard.BlockGuardPolicyException e = new StrictModeNetworkViolation(mPolicyMask);
             e.fillInStackTrace();
             startHandlingViolationException(e);
@@ -747,13 +774,6 @@
             handleViolationWithTimingAttempt(info);
         }
 
-        private static final ThreadLocal<ArrayList<ViolationInfo>> violationsBeingTimed =
-                new ThreadLocal<ArrayList<ViolationInfo>>() {
-            @Override protected ArrayList<ViolationInfo> initialValue() {
-                return new ArrayList<ViolationInfo>();
-            }
-        };
-
         // Attempts to fill in the provided ViolationInfo's
         // durationMillis field if this thread has a Looper we can use
         // to measure with.  We measure from the time of violation
@@ -780,7 +800,7 @@
 
             MessageQueue queue = Looper.myQueue();
             final ArrayList<ViolationInfo> records = violationsBeingTimed.get();
-            if (records.size() >= 10) {
+            if (records.size() >= MAX_OFFENSES_PER_LOOP) {
                 // Not worth measuring.  Too many offenses in one loop.
                 return;
             }
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 60ea95c..467a0ac 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -484,7 +484,7 @@
              * IObbActionListener to inform it of the terminal state of the
              * call.
              */
-            public void mountObb(String filename, String key, IObbActionListener token)
+            public void mountObb(String filename, String key, IObbActionListener token, int nonce)
                     throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
@@ -493,6 +493,7 @@
                     _data.writeString(filename);
                     _data.writeString(key);
                     _data.writeStrongBinder((token != null ? token.asBinder() : null));
+                    _data.writeInt(nonce);
                     mRemote.transact(Stub.TRANSACTION_mountObb, _data, _reply, 0);
                     _reply.readException();
                 } finally {
@@ -508,8 +509,8 @@
              * IObbActionListener to inform it of the terminal state of the
              * call.
              */
-            public void unmountObb(String filename, boolean force, IObbActionListener token)
-                    throws RemoteException {
+            public void unmountObb(String filename, boolean force, IObbActionListener token,
+                    int nonce) throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
                 try {
@@ -517,6 +518,7 @@
                     _data.writeString(filename);
                     _data.writeInt((force ? 1 : 0));
                     _data.writeStrongBinder((token != null ? token.asBinder() : null));
+                    _data.writeInt(nonce);
                     mRemote.transact(Stub.TRANSACTION_unmountObb, _data, _reply, 0);
                     _reply.readException();
                 } finally {
@@ -855,7 +857,9 @@
                     key = data.readString();
                     IObbActionListener observer;
                     observer = IObbActionListener.Stub.asInterface(data.readStrongBinder());
-                    mountObb(filename, key, observer);
+                    int nonce;
+                    nonce = data.readInt();
+                    mountObb(filename, key, observer, nonce);
                     reply.writeNoException();
                     return true;
                 }
@@ -867,7 +871,9 @@
                     force = 0 != data.readInt();
                     IObbActionListener observer;
                     observer = IObbActionListener.Stub.asInterface(data.readStrongBinder());
-                    unmountObb(filename, force, observer);
+                    int nonce;
+                    nonce = data.readInt();
+                    unmountObb(filename, force, observer, nonce);
                     reply.writeNoException();
                     return true;
                 }
@@ -979,7 +985,7 @@
      * MountService will call back to the supplied IObbActionListener to inform
      * it of the terminal state of the call.
      */
-    public void mountObb(String filename, String key, IObbActionListener token)
+    public void mountObb(String filename, String key, IObbActionListener token, int nonce)
             throws RemoteException;
 
     /*
@@ -1023,7 +1029,7 @@
      * MountService will call back to the supplied IObbActionListener to inform
      * it of the terminal state of the call.
      */
-    public void unmountObb(String filename, boolean force, IObbActionListener token)
+    public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce)
             throws RemoteException;
 
     /*
diff --git a/core/java/android/os/storage/IObbActionListener.java b/core/java/android/os/storage/IObbActionListener.java
index 2c098ac..d6fa58a 100644
--- a/core/java/android/os/storage/IObbActionListener.java
+++ b/core/java/android/os/storage/IObbActionListener.java
@@ -69,9 +69,11 @@
                     data.enforceInterface(DESCRIPTOR);
                     String filename;
                     filename = data.readString();
-                    String status;
-                    status = data.readString();
-                    this.onObbResult(filename, status);
+                    int nonce;
+                    nonce = data.readInt();
+                    int status;
+                    status = data.readInt();
+                    this.onObbResult(filename, nonce, status);
                     reply.writeNoException();
                     return true;
                 }
@@ -101,13 +103,15 @@
              *            on
              * @param returnCode status of the operation
              */
-            public void onObbResult(String filename, String status) throws RemoteException {
+            public void onObbResult(String filename, int nonce, int status)
+                    throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
                 try {
                     _data.writeInterfaceToken(DESCRIPTOR);
                     _data.writeString(filename);
-                    _data.writeString(status);
+                    _data.writeInt(nonce);
+                    _data.writeInt(status);
                     mRemote.transact(Stub.TRANSACTION_onObbResult, _data, _reply, 0);
                     _reply.readException();
                 } finally {
@@ -124,7 +128,8 @@
      * Return from an OBB action result.
      * 
      * @param filename the path to the OBB the operation was performed on
-     * @param returnCode status of the operation
+     * @param nonce identifier that is meaningful to the receiver
+     * @param status status code as defined in {@link OnObbStateChangeListener}
      */
-    public void onObbResult(String filename, String status) throws RemoteException;
+    public void onObbResult(String filename, int nonce, int status) throws RemoteException;
 }
diff --git a/core/java/android/os/storage/OnObbStateChangeListener.java b/core/java/android/os/storage/OnObbStateChangeListener.java
index a2d0a56..950195b 100644
--- a/core/java/android/os/storage/OnObbStateChangeListener.java
+++ b/core/java/android/os/storage/OnObbStateChangeListener.java
@@ -17,15 +17,69 @@
 package android.os.storage;
 
 /**
- * Used for receiving notifications from {@link StorageManager}.
+ * Used for receiving notifications from {@link StorageManager} about OBB file
+ * states.
  */
 public abstract class OnObbStateChangeListener {
+
+    /**
+     * The OBB container is now mounted and ready for use. Returned in status
+     * messages from calls made via {@link StorageManager}
+     */
+    public static final int MOUNTED = 1;
+
+    /**
+     * The OBB container is now unmounted and not usable. Returned in status
+     * messages from calls made via {@link StorageManager}
+     */
+    public static final int UNMOUNTED = 2;
+
+    /**
+     * There was an internal system error encountered while trying to mount the
+     * OBB. Returned in status messages from calls made via
+     * {@link StorageManager}
+     */
+    public static final int ERROR_INTERNAL = 20;
+
+    /**
+     * The OBB could not be mounted by the system. Returned in status messages
+     * from calls made via {@link StorageManager}
+     */
+    public static final int ERROR_COULD_NOT_MOUNT = 21;
+
+    /**
+     * The OBB could not be unmounted. This most likely indicates that a file is
+     * in use on the OBB. Returned in status messages from calls made via
+     * {@link StorageManager}
+     */
+    public static final int ERROR_COULD_NOT_UNMOUNT = 22;
+
+    /**
+     * A call was made to unmount the OBB when it was not mounted. Returned in
+     * status messages from calls made via {@link StorageManager}
+     */
+    public static final int ERROR_NOT_MOUNTED = 23;
+
+    /**
+     * The OBB has already been mounted. Returned in status messages from calls
+     * made via {@link StorageManager}
+     */
+    public static final int ERROR_ALREADY_MOUNTED = 24;
+
+    /**
+     * The current application does not have permission to use this OBB because
+     * the OBB indicates it's owned by a different package or the key used to
+     * open it is incorrect. Returned in status messages from calls made via
+     * {@link StorageManager}
+     */
+    public static final int ERROR_PERMISSION_DENIED = 25;
+
     /**
      * Called when an OBB has changed states.
      * 
      * @param path path to the OBB file the state change has happened on
      * @param state the current state of the OBB
      */
-    public void onObbStateChange(String path, String state) {
+    public void onObbStateChange(String path, int state) {
     }
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 2ebd049..73ac79f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -22,12 +22,13 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * StorageManager is the interface to the systems storage service. The storage
@@ -69,7 +70,12 @@
     /*
      * List of our listeners
      */
-    private ArrayList<ListenerDelegate> mListeners = new ArrayList<ListenerDelegate>();
+    private List<ListenerDelegate> mListeners = new ArrayList<ListenerDelegate>();
+
+    /*
+     * Next available nonce
+     */
+    final private AtomicInteger mNextNonce = new AtomicInteger(0);
 
     private class MountServiceBinderListener extends IMountServiceListener.Stub {
         public void onUsbMassStorageConnectionChanged(boolean available) {
@@ -93,57 +99,38 @@
     private final ObbActionListener mObbActionListener = new ObbActionListener();
 
     private class ObbActionListener extends IObbActionListener.Stub {
-        private List<WeakReference<ObbListenerDelegate>> mListeners = new LinkedList<WeakReference<ObbListenerDelegate>>();
+        private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>();
 
         @Override
-        public void onObbResult(String filename, String status) throws RemoteException {
+        public void onObbResult(String filename, int nonce, int status) throws RemoteException {
+            final ObbListenerDelegate delegate;
             synchronized (mListeners) {
-                final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator();
-                while (iter.hasNext()) {
-                    final WeakReference<ObbListenerDelegate> ref = iter.next();
-
-                    final ObbListenerDelegate delegate = (ref == null) ? null : ref.get();
-                    if (delegate == null) {
-                        iter.remove();
-                        continue;
-                    }
-
-                    delegate.sendObbStateChanged(filename, status);
+                delegate = mListeners.get(nonce);
+                if (delegate != null) {
+                    mListeners.remove(nonce);
                 }
             }
+
+            if (delegate != null) {
+                delegate.sendObbStateChanged(filename, status);
+            }
         }
 
-        public void addListener(OnObbStateChangeListener listener) {
-            if (listener == null) {
-                return;
-            }
+        public int addListener(OnObbStateChangeListener listener) {
+            final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
 
             synchronized (mListeners) {
-                final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator();
-                while (iter.hasNext()) {
-                    final WeakReference<ObbListenerDelegate> ref = iter.next();
-
-                    final ObbListenerDelegate delegate = (ref == null) ? null : ref.get();
-                    if (delegate == null) {
-                        iter.remove();
-                        continue;
-                    }
-
-                    /*
-                     * If we're already in the listeners, we don't need to be in
-                     * there again.
-                     */
-                    if (listener.equals(delegate.getListener())) {
-                        return;
-                    }
-                }
-
-                final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
-                mListeners.add(new WeakReference<ObbListenerDelegate>(delegate));
+                mListeners.put(delegate.nonce, delegate);
             }
+
+            return delegate.nonce;
         }
     }
 
+    private int getNextNonce() {
+        return mNextNonce.getAndIncrement();
+    }
+
     /**
      * Private class containing sender and receiver code for StorageEvents.
      */
@@ -151,7 +138,10 @@
         private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
         private final Handler mHandler;
 
+        private final int nonce;
+
         ObbListenerDelegate(OnObbStateChangeListener listener) {
+            nonce = getNextNonce();
             mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
             mHandler = new Handler(mTgtLooper) {
                 @Override
@@ -180,7 +170,7 @@
             return mObbEventListenerRef.get();
         }
 
-        void sendObbStateChanged(String path, String state) {
+        void sendObbStateChanged(String path, int state) {
             ObbStateChangedStorageEvent e = new ObbStateChangedStorageEvent(path, state);
             mHandler.sendMessage(e.getMessage());
         }
@@ -191,9 +181,10 @@
      */
     private class ObbStateChangedStorageEvent extends StorageEvent {
         public final String path;
-        public final String state;
 
-        public ObbStateChangedStorageEvent(String path, String state) {
+        public final int state;
+
+        public ObbStateChangedStorageEvent(String path, int state) {
             super(EVENT_OBB_STATE_CHANGED);
             this.path = path;
             this.state = state;
@@ -420,10 +411,8 @@
      * <p>
      * The OBB will remain mounted for as long as the StorageManager reference
      * is held by the application. As soon as this reference is lost, the OBBs
-     * in use will be unmounted. The {@link OnObbStateChangeListener} registered with
-     * this call will receive all further OBB-related events until it goes out
-     * of scope. If the caller is not interested in whether the call succeeds,
-     * the <code>listener</code> may be specified as <code>null</code>.
+     * in use will be unmounted. The {@link OnObbStateChangeListener} registered
+     * with this call will receive the success or failure of this operation.
      * <p>
      * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
      * file matches a package ID that is owned by the calling program's UID.
@@ -433,12 +422,21 @@
      * @param filename the path to the OBB file
      * @param key secret used to encrypt the OBB; may be <code>null</code> if no
      *            encryption was used on the OBB.
+     * @param listener will receive the success or failure of the operation
      * @return whether the mount call was successfully queued or not
      */
     public boolean mountObb(String filename, String key, OnObbStateChangeListener listener) {
+        if (filename == null) {
+            throw new IllegalArgumentException("filename cannot be null");
+        }
+
+        if (listener == null) {
+            throw new IllegalArgumentException("listener cannot be null");
+        }
+
         try {
-            mObbActionListener.addListener(listener);
-            mMountService.mountObb(filename, key, mObbActionListener);
+            final int nonce = mObbActionListener.addListener(listener);
+            mMountService.mountObb(filename, key, mObbActionListener, nonce);
             return true;
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to mount OBB", e);
@@ -452,10 +450,8 @@
      * <code>force</code> flag is true, it will kill any application needed to
      * unmount the given OBB (even the calling application).
      * <p>
-     * The {@link OnObbStateChangeListener} registered with this call will receive all
-     * further OBB-related events until it goes out of scope. If the caller is
-     * not interested in whether the call succeeded, the listener may be
-     * specified as <code>null</code>.
+     * The {@link OnObbStateChangeListener} registered with this call will
+     * receive the success or failure of this operation.
      * <p>
      * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
      * file matches a package ID that is owned by the calling program's UID.
@@ -466,12 +462,21 @@
      * @param filename path to the OBB file
      * @param force whether to kill any programs using this in order to unmount
      *            it
+     * @param listener will receive the success or failure of the operation
      * @return whether the unmount call was successfully queued or not
      */
     public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener) {
+        if (filename == null) {
+            throw new IllegalArgumentException("filename cannot be null");
+        }
+
+        if (listener == null) {
+            throw new IllegalArgumentException("listener cannot be null");
+        }
+
         try {
-            mObbActionListener.addListener(listener);
-            mMountService.unmountObb(filename, force, mObbActionListener);
+            final int nonce = mObbActionListener.addListener(listener);
+            mMountService.unmountObb(filename, force, mObbActionListener, nonce);
             return true;
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to mount OBB", e);
@@ -486,7 +491,11 @@
      * @param filename path to OBB image
      * @return true if OBB is mounted; false if not mounted or on error
      */
-    public boolean isObbMounted(String filename) throws IllegalArgumentException {
+    public boolean isObbMounted(String filename) {
+        if (filename == null) {
+            throw new IllegalArgumentException("filename cannot be null");
+        }
+
         try {
             return mMountService.isObbMounted(filename);
         } catch (RemoteException e) {
@@ -506,12 +515,14 @@
      *         not mounted or exception encountered trying to read status
      */
     public String getMountedObbPath(String filename) {
+        if (filename == null) {
+            throw new IllegalArgumentException("filename cannot be null");
+        }
+
         try {
             return mMountService.getMountedObbPath(filename);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to find mounted path for OBB", e);
-        } catch (IllegalArgumentException e) {
-            Log.d(TAG, "Couldn't read OBB file", e);
         }
 
         return null;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index ef7716e..1fd31a3 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -77,6 +77,7 @@
     public final static int FLAG_VIRTUAL = 0x00000100;
 
     public final static int FLAG_INJECTED = 0x01000000;
+    public final static int FLAG_TRUSTED = 0x02000000;
 
     public final static int FLAG_WOKE_HERE = 0x10000000;
     public final static int FLAG_BRIGHT_HERE = 0x20000000;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9d914ad..43eeda8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1332,10 +1332,6 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-        <activity android:name="com.android.internal.app.ExternalMediaFormatActivity"
-                android:theme="@style/Theme.Dialog.Alert"
-                android:excludeFromRecents="true">
-        </activity>
 
         <activity android:name="android.accounts.ChooseAccountActivity"
                 android:excludeFromRecents="true"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 340e23c..849c564 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -148,6 +148,9 @@
     <!-- Flag indicating whether the we should enable the automatic brightness in Settings.
          Software implementation will be used if config_hardware_auto_brightness_available is not set -->
     <bool name="config_automatic_brightness_available">false</bool>
+
+    <!-- If this is true, the screen will come on when you unplug usb/power/whatever. -->
+    <bool name="config_unplugTurnsOnScreen">false</bool>
     
     <!-- XXXXXX END OF RESOURCES USING WRONG NAMING CONVENTION -->
 
@@ -351,7 +354,7 @@
     <integer name="config_datause_notification_type">2</integer>
 
     <!-- Enables SIP on WIFI only -->
-    <bool name="config_sip_wifi_only">false</bool>
+    <bool name="config_sip_wifi_only">true</bool>
 
     <!-- Boolean indicating if restoring network selection should be skipped -->
     <!-- The restoring is handled by modem if it is true-->
diff --git a/core/tests/coretests/src/com/android/server/MountServiceTests.java b/core/tests/coretests/src/com/android/server/MountServiceTests.java
index 83e9d18..1f8c92e 100644
--- a/core/tests/coretests/src/com/android/server/MountServiceTests.java
+++ b/core/tests/coretests/src/com/android/server/MountServiceTests.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
-import android.os.Environment;
 import android.os.FileUtils;
 import android.os.storage.OnObbStateChangeListener;
 import android.os.storage.StorageManager;
@@ -57,17 +56,15 @@
         }
     }
 
-    private interface CompletableTask {
-        public boolean isDone();
-    }
+    private static class ObbObserver extends OnObbStateChangeListener {
+        private String path;
 
-    private static class ObbObserver extends OnObbStateChangeListener implements CompletableTask {
-        public String path;
-        public String state;
+        public int state = -1;
         boolean done = false;
 
         @Override
-        public void onObbStateChange(String path, String state) {
+        public void onObbStateChange(String path, int state) {
+            Log.d(TAG, "Received message.  path=" + path + ", state=" + state);
             synchronized (this) {
                 this.path = path;
                 this.state = state;
@@ -76,32 +73,43 @@
             }
         }
 
+        public String getPath() {
+            assertTrue("Expected ObbObserver to have received a state change.", done);
+            return path;
+        }
+
+        public int getState() {
+            assertTrue("Expected ObbObserver to have received a state change.", done);
+            return state;
+        }
+
         public void reset() {
             this.path = null;
-            this.state = null;
+            this.state = -1;
             done = false;
         }
 
         public boolean isDone() {
             return done;
         }
-    }
 
-    private boolean waitForCompletion(CompletableTask task) {
-        long waitTime = 0;
-        synchronized (task) {
-            while (!task.isDone() && waitTime < MAX_WAIT_TIME) {
-                try {
-                    task.wait(WAIT_TIME_INCR);
-                    waitTime += WAIT_TIME_INCR;
-                } catch (InterruptedException e) {
-                    Log.i(TAG, "Interrupted during sleep", e);
+        public boolean waitForCompletion() {
+            long waitTime = 0;
+            synchronized (this) {
+                while (!isDone() && waitTime < MAX_WAIT_TIME) {
+                    try {
+                        wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    } catch (InterruptedException e) {
+                        Log.i(TAG, "Interrupted during sleep", e);
+                    }
                 }
             }
-        }
 
-        return task.isDone();
+            return isDone();
+        }
     }
+
     private File getFilePath(String name) {
         final File filesDir = mContext.getFilesDir();
         final File outFile = new File(filesDir, name);
@@ -128,23 +136,52 @@
     }
 
     private void mountObb(StorageManager sm, final int resource, final File file,
-            String expectedState) {
+            int expectedState) {
         copyRawToFile(resource, file);
 
-        ObbObserver observer = new ObbObserver();
+        final ObbObserver observer = new ObbObserver();
         assertTrue("mountObb call on " + file.getPath() + " should succeed",
                 sm.mountObb(file.getPath(), null, observer));
 
         assertTrue("Mount should have completed",
-                waitForCompletion(observer));
+                observer.waitForCompletion());
+
+        if (expectedState == OnObbStateChangeListener.MOUNTED) {
+            assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath()));
+        }
 
         assertEquals("Actual file and resolved file should be the same",
-                file.getPath(), observer.path);
+                file.getPath(), observer.getPath());
 
-        assertEquals(expectedState, observer.state);
+        assertEquals(expectedState, observer.getState());
     }
 
-    private String checkMountedPath(StorageManager sm, File file) {
+    private ObbObserver mountObbWithoutWait(final StorageManager sm, final int resource,
+            final File file) {
+        copyRawToFile(resource, file);
+
+        final ObbObserver observer = new ObbObserver();
+        assertTrue("mountObb call on " + file.getPath() + " should succeed", sm.mountObb(file
+                .getPath(), null, observer));
+
+        return observer;
+    }
+
+    private void waitForObbActionCompletion(final StorageManager sm, final File file,
+            final ObbObserver observer, int expectedState, boolean checkPath) {
+        assertTrue("Mount should have completed", observer.waitForCompletion());
+
+        assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath()));
+
+        if (checkPath) {
+            assertEquals("Actual file and resolved file should be the same", file.getPath(),
+                    observer.getPath());
+        }
+
+        assertEquals(expectedState, observer.getState());
+    }
+
+    private String checkMountedPath(final StorageManager sm, final File file) {
         final String mountPath = sm.getMountedObbPath(file.getPath());
         assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX,
                 OBB_MOUNT_PREFIX,
@@ -152,13 +189,21 @@
         return mountPath;
     }
 
-    private void unmountObb(StorageManager sm, final File outFile) {
-        ObbObserver observer = new ObbObserver();
+    private void unmountObb(final StorageManager sm, final File file, int expectedState) {
+        final ObbObserver observer = new ObbObserver();
+
         assertTrue("unmountObb call on test1.obb should succeed",
-                sm.unmountObb(outFile.getPath(), false, observer));
+ sm.unmountObb(file.getPath(),
+                false, observer));
 
         assertTrue("Unmount should have completed",
-                waitForCompletion(observer));
+                observer.waitForCompletion());
+
+        assertEquals(expectedState, observer.getState());
+
+        if (expectedState == OnObbStateChangeListener.UNMOUNTED) {
+            assertFalse("OBB should not be mounted", sm.isObbMounted(file.getPath()));
+        }
     }
 
     @LargeTest
@@ -167,7 +212,9 @@
 
         final File outFile = getFilePath("test1.obb");
 
-        mountObb(sm, R.raw.test1, outFile, Environment.MEDIA_MOUNTED);
+        mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED);
+
+        mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
 
         final String mountPath = checkMountedPath(sm, outFile);
         final File mountDir = new File(mountPath);
@@ -175,7 +222,7 @@
         assertTrue("OBB mounted path should be a directory",
                 mountDir.isDirectory());
 
-        unmountObb(sm, outFile);
+        unmountObb(sm, outFile, OnObbStateChangeListener.UNMOUNTED);
     }
 
     @LargeTest
@@ -184,7 +231,7 @@
 
         final File outFile = getFilePath("test1_nosig.obb");
 
-        mountObb(sm, R.raw.test1_nosig, outFile, Environment.MEDIA_BAD_REMOVAL);
+        mountObb(sm, R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL);
 
         assertFalse("OBB should not be mounted",
                 sm.isObbMounted(outFile.getPath()));
@@ -199,7 +246,8 @@
 
         final File outFile = getFilePath("test1_wrongpackage.obb");
 
-        mountObb(sm, R.raw.test1_wrongpackage, outFile, Environment.MEDIA_BAD_REMOVAL);
+        mountObb(sm, R.raw.test1_wrongpackage, outFile,
+                OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
 
         assertFalse("OBB should not be mounted",
                 sm.isObbMounted(outFile.getPath()));
@@ -207,4 +255,31 @@
         assertNull("OBB's mounted path should be null",
                 sm.getMountedObbPath(outFile.getPath()));
     }
+
+    @LargeTest
+    public void testMountAndUnmountTwoObbs() {
+        StorageManager sm = getStorageManager();
+
+        final File file1 = getFilePath("test1.obb");
+        final File file2 = getFilePath("test2.obb");
+
+        ObbObserver oo1 = mountObbWithoutWait(sm, R.raw.test1, file1);
+        ObbObserver oo2 = mountObbWithoutWait(sm, R.raw.test1, file2);
+
+        Log.d(TAG, "Waiting for OBB #1 to complete mount");
+        waitForObbActionCompletion(sm, file1, oo1, OnObbStateChangeListener.MOUNTED, false);
+        Log.d(TAG, "Waiting for OBB #2 to complete mount");
+        waitForObbActionCompletion(sm, file2, oo2, OnObbStateChangeListener.MOUNTED, false);
+
+        final String mountPath1 = checkMountedPath(sm, file1);
+        final File mountDir1 = new File(mountPath1);
+        assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory());
+
+        final String mountPath2 = checkMountedPath(sm, file2);
+        final File mountDir2 = new File(mountPath2);
+        assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory());
+
+        unmountObb(sm, file1, OnObbStateChangeListener.UNMOUNTED);
+        unmountObb(sm, file2, OnObbStateChangeListener.UNMOUNTED);
+    }
 }
diff --git a/docs/html/guide/practices/design/responsiveness.jd b/docs/html/guide/practices/design/responsiveness.jd
index b811d1b..a00e3aa 100644
--- a/docs/html/guide/practices/design/responsiveness.jd
+++ b/docs/html/guide/practices/design/responsiveness.jd
@@ -99,6 +99,10 @@
 event timeout. These same practices should be followed for any other threads
 that display UI, as they are also subject to the same timeouts.</p>
 
+<p>You can use {@link android.os.StrictMode} to help find potentially
+long running operations such as network or database operations that
+you might accidentally be doing your main thread.</p>
+
 <p>The specific constraint on IntentReceiver execution time emphasizes what
 they were meant to do: small, discrete amounts of work in the background such
 as saving a setting or registering a Notification. So as with other methods
diff --git a/include/storage/IMountService.h b/include/storage/IMountService.h
index 436fc38..51f9aeb 100644
--- a/include/storage/IMountService.h
+++ b/include/storage/IMountService.h
@@ -61,9 +61,9 @@
     virtual void shutdown(const sp<IMountShutdownObserver>& observer) = 0;
     virtual void finishMediaUpdate() = 0;
     virtual void mountObb(const String16& filename, const String16& key,
-            const sp<IObbActionListener>& token) = 0;
+            const sp<IObbActionListener>& token, const int32_t nonce) = 0;
     virtual void unmountObb(const String16& filename, const bool force,
-            const sp<IObbActionListener>& token) = 0;
+            const sp<IObbActionListener>& token, const int32_t nonce) = 0;
     virtual bool isObbMounted(const String16& filename) = 0;
     virtual bool getMountedObbPath(const String16& filename, String16& path) = 0;
 };
diff --git a/include/storage/IObbActionListener.h b/include/storage/IObbActionListener.h
index 1bedcc6..d78273c 100644
--- a/include/storage/IObbActionListener.h
+++ b/include/storage/IObbActionListener.h
@@ -29,7 +29,7 @@
 public:
     DECLARE_META_INTERFACE(ObbActionListener);
 
-    virtual void onObbResult(const String16& filename, const String16& status) = 0;
+    virtual void onObbResult(const String16& filename, const int32_t nonce, const int32_t state) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 66061fd..8c6018b 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -95,6 +95,10 @@
     // Indicates that the input event was injected.
     POLICY_FLAG_INJECTED = 0x01000000,
 
+    // Indicates that the input event is from a trusted source such as a directly attached
+    // input device or an application with system-wide event injection permission.
+    POLICY_FLAG_TRUSTED = 0x02000000,
+
     /* These flags are set by the input reader policy as it intercepts each event. */
 
     // Indicates that the screen was off when the event was received and the event
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 0834e86..3599163 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -913,7 +913,6 @@
     void drainInboundQueueLocked();
     void releasePendingEventLocked();
     void releaseInboundEventLocked(EventEntry* entry);
-    bool isEventFromTrustedSourceLocked(EventEntry* entry);
 
     // Dispatch state.
     bool mDispatchEnabled;
@@ -960,10 +959,10 @@
             nsecs_t currentTime, ConfigurationChangedEntry* entry);
     bool dispatchKeyLocked(
             nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
-            bool dropEvent, nsecs_t* nextWakeupTime);
+            DropReason* dropReason, nsecs_t* nextWakeupTime);
     bool dispatchMotionLocked(
             nsecs_t currentTime, MotionEntry* entry,
-            bool dropEvent, nsecs_t* nextWakeupTime);
+            DropReason* dropReason, nsecs_t* nextWakeupTime);
     void dispatchEventToCurrentInputTargetsLocked(
             nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample);
 
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 3ad9319..9ff6930 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -430,13 +430,14 @@
     }
 
     void mountObb(const String16& filename, const String16& key,
-            const sp<IObbActionListener>& token)
+            const sp<IObbActionListener>& token, int32_t nonce)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
         data.writeString16(filename);
         data.writeString16(key);
         data.writeStrongBinder(token->asBinder());
+        data.writeInt32(nonce);
         if (remote()->transact(TRANSACTION_mountObb, data, &reply) != NO_ERROR) {
             LOGD("mountObb could not contact remote\n");
             return;
@@ -448,12 +449,15 @@
         }
     }
 
-    void unmountObb(const String16& filename, const bool force, const sp<IObbActionListener>& token)
+    void unmountObb(const String16& filename, const bool force,
+            const sp<IObbActionListener>& token, const int32_t nonce)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
         data.writeString16(filename);
         data.writeInt32(force ? 1 : 0);
+        data.writeStrongBinder(token->asBinder());
+        data.writeInt32(nonce);
         if (remote()->transact(TRANSACTION_unmountObb, data, &reply) != NO_ERROR) {
             LOGD("unmountObb could not contact remote\n");
             return;
diff --git a/libs/storage/IObbActionListener.cpp b/libs/storage/IObbActionListener.cpp
index 5bfece7..eaa211e 100644
--- a/libs/storage/IObbActionListener.cpp
+++ b/libs/storage/IObbActionListener.cpp
@@ -30,7 +30,7 @@
         : BpInterface<IObbActionListener>(impl)
     { }
 
-    virtual void onObbResult(const String16& filename, const String16& status) { }
+    virtual void onObbResult(const String16& filename, const int32_t nonce, const int32_t state) { }
 };
 
 IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener");
@@ -44,8 +44,9 @@
         case TRANSACTION_onObbResult: {
             CHECK_INTERFACE(IObbActionListener, data, reply);
             String16 filename = data.readString16();
-            String16 state = data.readString16();
-            onObbResult(filename, state);
+            int32_t nonce = data.readInt32();
+            int32_t state = data.readInt32();
+            onObbResult(filename, nonce, state);
             reply->writeNoException();
             return NO_ERROR;
         } break;
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 58c2cdf..6ba19d7 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -370,7 +370,7 @@
             }
         }
         done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,
-                dropReason != DROP_REASON_NOT_DROPPED, nextWakeupTime);
+                &dropReason, nextWakeupTime);
         break;
     }
 
@@ -380,7 +380,7 @@
             dropReason = DROP_REASON_APP_SWITCH;
         }
         done = dispatchMotionLocked(currentTime, typedEntry,
-                dropReason != DROP_REASON_NOT_DROPPED, nextWakeupTime);
+                &dropReason, nextWakeupTime);
         break;
     }
 
@@ -431,8 +431,10 @@
     const char* reason;
     switch (dropReason) {
     case DROP_REASON_POLICY:
-        reason = "inbound event was dropped because the policy requested that it not be "
-                "delivered to the application";
+#if DEBUG_INBOUND_EVENT_DETAILS
+        LOGD("Dropped event because policy consumed it.");
+#endif
+        reason = "inbound event was dropped because the policy consumed it";
         break;
     case DROP_REASON_DISABLED:
         LOGI("Dropped event because input dispatch is disabled.");
@@ -473,7 +475,7 @@
 bool InputDispatcher::isAppSwitchKeyEventLocked(KeyEntry* keyEntry) {
     return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED)
             && isAppSwitchKeyCode(keyEntry->keyCode)
-            && isEventFromTrustedSourceLocked(keyEntry)
+            && (keyEntry->policyFlags & POLICY_FLAG_TRUSTED)
             && (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER);
 }
 
@@ -541,12 +543,6 @@
     mAllocator.releaseEventEntry(entry);
 }
 
-bool InputDispatcher::isEventFromTrustedSourceLocked(EventEntry* entry) {
-    InjectionState* injectionState = entry->injectionState;
-    return ! injectionState
-            || hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid);
-}
-
 void InputDispatcher::resetKeyRepeatLocked() {
     if (mKeyRepeatState.lastKeyEntry) {
         mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
@@ -559,7 +555,8 @@
     KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
 
     // Reuse the repeated key entry if it is otherwise unreferenced.
-    uint32_t policyFlags = entry->policyFlags & (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER);
+    uint32_t policyFlags = (entry->policyFlags & POLICY_FLAG_RAW_MASK)
+            | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED;
     if (entry->refCount == 1) {
         mAllocator.recycleKeyEntry(entry);
         entry->eventTime = currentTime;
@@ -608,19 +605,13 @@
 
 bool InputDispatcher::dispatchKeyLocked(
         nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
-        bool dropEvent, nsecs_t* nextWakeupTime) {
+        DropReason* dropReason, nsecs_t* nextWakeupTime) {
     // Give the policy a chance to intercept the key.
     if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
-        bool trusted;
-        if (! dropEvent && mFocusedWindow) {
-            trusted = checkInjectionPermission(mFocusedWindow, entry->injectionState);
-        } else {
-            trusted = isEventFromTrustedSourceLocked(entry);
-        }
-        if (trusted) {
+        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
             CommandEntry* commandEntry = postCommandLocked(
                     & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
-            if (! dropEvent && mFocusedWindow) {
+            if (mFocusedWindow) {
                 commandEntry->inputChannel = mFocusedWindow->inputChannel;
             }
             commandEntry->keyEntry = entry;
@@ -630,15 +621,16 @@
             entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
         }
     } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
-        resetTargetsLocked();
-        setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_SUCCEEDED);
-        return true;
+        if (*dropReason == DROP_REASON_NOT_DROPPED) {
+            *dropReason = DROP_REASON_POLICY;
+        }
     }
 
     // Clean up if dropping the event.
-    if (dropEvent) {
+    if (*dropReason != DROP_REASON_NOT_DROPPED) {
         resetTargetsLocked();
-        setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+        setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
+                ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
         return true;
     }
 
@@ -648,7 +640,8 @@
 
         if (entry->repeatCount == 0
                 && entry->action == AKEY_EVENT_ACTION_DOWN
-                && ! entry->isInjected()) {
+                && (entry->policyFlags & POLICY_FLAG_TRUSTED)
+                && !entry->isInjected()) {
             if (mKeyRepeatState.lastKeyEntry
                     && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
                 // We have seen two identical key downs in a row which indicates that the device
@@ -713,11 +706,12 @@
 }
 
 bool InputDispatcher::dispatchMotionLocked(
-        nsecs_t currentTime, MotionEntry* entry, bool dropEvent, nsecs_t* nextWakeupTime) {
+        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
     // Clean up if dropping the event.
-    if (dropEvent) {
+    if (*dropReason != DROP_REASON_NOT_DROPPED) {
         resetTargetsLocked();
-        setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+        setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
+                ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
         return true;
     }
 
@@ -2085,6 +2079,7 @@
         return;
     }
 
+    policyFlags |= POLICY_FLAG_TRUSTED;
     mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags,
             keyCode, scanCode, /*byref*/ policyFlags);
 
@@ -2130,6 +2125,7 @@
         return;
     }
 
+    policyFlags |= POLICY_FLAG_TRUSTED;
     mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags);
 
     bool needWake;
@@ -2263,6 +2259,7 @@
             switchCode, switchValue, policyFlags);
 #endif
 
+    policyFlags |= POLICY_FLAG_TRUSTED;
     mPolicy->notifySwitch(when, switchCode, switchValue, policyFlags);
 }
 
@@ -2275,7 +2272,11 @@
 #endif
 
     nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
-    bool trusted = hasInjectionPermission(injectorPid, injectorUid);
+
+    uint32_t policyFlags = POLICY_FLAG_INJECTED;
+    if (hasInjectionPermission(injectorPid, injectorUid)) {
+        policyFlags |= POLICY_FLAG_TRUSTED;
+    }
 
     EventEntry* injectedEntry;
     switch (event->getType()) {
@@ -2291,11 +2292,8 @@
         int32_t flags = keyEvent->getFlags();
         int32_t keyCode = keyEvent->getKeyCode();
         int32_t scanCode = keyEvent->getScanCode();
-        uint32_t policyFlags = POLICY_FLAG_INJECTED;
-        if (trusted) {
-            mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags,
-                    keyCode, scanCode, /*byref*/ policyFlags);
-        }
+        mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags,
+                keyCode, scanCode, /*byref*/ policyFlags);
 
         mLock.lock();
         injectedEntry = mAllocator.obtainKeyEntry(eventTime, deviceId, keyEvent->getSource(),
@@ -2314,10 +2312,7 @@
         }
 
         nsecs_t eventTime = motionEvent->getEventTime();
-        uint32_t policyFlags = POLICY_FLAG_INJECTED;
-        if (trusted) {
-            mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags);
-        }
+        mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags);
 
         mLock.lock();
         const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
@@ -3323,7 +3318,7 @@
         }
     }
 
-    for (size_t i = 0; i < mMotionMementos.size(); i++) {
+    for (size_t i = 0; i < mMotionMementos.size(); ) {
         const MotionMemento& memento = mMotionMementos.itemAt(i);
         if (shouldCancelEvent(memento.source, options)) {
             outEvents.push(allocator->obtainMotionEntry(currentTime,
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 05dd61b..0f4c1f3 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -28,6 +28,7 @@
 #include "ASessionDescription.h"
 
 #include <ctype.h>
+#include <cutils/properties.h>
 
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -35,6 +36,9 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MediaErrors.h>
 
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
 // If no access units are received within 3 secs, assume that the rtp
 // stream has ended and signal end of stream.
 static int64_t kAccessUnitTimeoutUs = 3000000ll;
@@ -45,6 +49,19 @@
 
 namespace android {
 
+static void MakeUserAgentString(AString *s) {
+    s->setTo("stagefright/1.1 (Linux;Android ");
+
+#if (PROPERTY_VALUE_MAX < 8)
+#error "PROPERTY_VALUE_MAX must be at least 8"
+#endif
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.build.version.release", value, "Unknown");
+    s->append(value);
+    s->append(")");
+}
+
 static bool GetAttribute(const char *s, const char *key, AString *value) {
     value->clear();
 
@@ -137,6 +154,131 @@
         return maxTimeUs;
     }
 
+    static void addRR(const sp<ABuffer> &buf) {
+        uint8_t *ptr = buf->data() + buf->size();
+        ptr[0] = 0x80 | 0;
+        ptr[1] = 201;  // RR
+        ptr[2] = 0;
+        ptr[3] = 1;
+        ptr[4] = 0xde;  // SSRC
+        ptr[5] = 0xad;
+        ptr[6] = 0xbe;
+        ptr[7] = 0xef;
+
+        buf->setRange(0, buf->size() + 8);
+    }
+
+    static void addSDES(int s, const sp<ABuffer> &buffer) {
+        struct sockaddr_in addr;
+        socklen_t addrSize = sizeof(addr);
+        CHECK_EQ(0, getsockname(s, (sockaddr *)&addr, &addrSize));
+
+        uint8_t *data = buffer->data() + buffer->size();
+        data[0] = 0x80 | 1;
+        data[1] = 202;  // SDES
+        data[4] = 0xde;  // SSRC
+        data[5] = 0xad;
+        data[6] = 0xbe;
+        data[7] = 0xef;
+
+        size_t offset = 8;
+
+        data[offset++] = 1;  // CNAME
+
+        AString cname = "stagefright@";
+        cname.append(inet_ntoa(addr.sin_addr));
+        data[offset++] = cname.size();
+
+        memcpy(&data[offset], cname.c_str(), cname.size());
+        offset += cname.size();
+
+        data[offset++] = 6;  // TOOL
+
+        AString tool;
+        MakeUserAgentString(&tool);
+
+        data[offset++] = tool.size();
+
+        memcpy(&data[offset], tool.c_str(), tool.size());
+        offset += tool.size();
+
+        data[offset++] = 0;
+
+        if ((offset % 4) > 0) {
+            size_t count = 4 - (offset % 4);
+            switch (count) {
+                case 3:
+                    data[offset++] = 0;
+                case 2:
+                    data[offset++] = 0;
+                case 1:
+                    data[offset++] = 0;
+            }
+        }
+
+        size_t numWords = (offset / 4) - 1;
+        data[2] = numWords >> 8;
+        data[3] = numWords & 0xff;
+
+        buffer->setRange(buffer->offset(), buffer->size() + offset);
+    }
+
+    // In case we're behind NAT, fire off two UDP packets to the remote
+    // rtp/rtcp ports to poke a hole into the firewall for future incoming
+    // packets. We're going to send an RR/SDES RTCP packet to both of them.
+    void pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) {
+        AString source;
+        AString server_port;
+        if (!GetAttribute(transport.c_str(),
+                          "source",
+                          &source)
+                || !GetAttribute(transport.c_str(),
+                                 "server_port",
+                                 &server_port)) {
+            return;
+        }
+
+        int rtpPort, rtcpPort;
+        if (sscanf(server_port.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2
+                || rtpPort <= 0 || rtpPort > 65535
+                || rtcpPort <=0 || rtcpPort > 65535
+                || rtcpPort != rtpPort + 1
+                || (rtpPort & 1) != 0) {
+            return;
+        }
+
+        struct sockaddr_in addr;
+        memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+        addr.sin_family = AF_INET;
+        addr.sin_addr.s_addr = inet_addr(source.c_str());
+
+        if (addr.sin_addr.s_addr == INADDR_NONE) {
+            return;
+        }
+
+        // Make up an RR/SDES RTCP packet.
+        sp<ABuffer> buf = new ABuffer(65536);
+        buf->setRange(0, 0);
+        addRR(buf);
+        addSDES(rtpSocket, buf);
+
+        addr.sin_port = htons(rtpPort);
+
+        ssize_t n = sendto(
+                rtpSocket, buf->data(), buf->size(), 0,
+                (const sockaddr *)&addr, sizeof(addr));
+        CHECK_EQ(n, (ssize_t)buf->size());
+
+        addr.sin_port = htons(rtcpPort);
+
+        n = sendto(
+                rtcpSocket, buf->data(), buf->size(), 0,
+                (const sockaddr *)&addr, sizeof(addr));
+        CHECK_EQ(n, (ssize_t)buf->size());
+
+        LOGV("successfully poked holes.");
+    }
+
     virtual void onMessageReceived(const sp<AMessage> &msg) {
         switch (msg->what()) {
             case 'conn':
@@ -285,6 +427,17 @@
                         sp<AMessage> notify = new AMessage('accu', id());
                         notify->setSize("track-index", trackIndex);
 
+                        i = response->mHeaders.indexOfKey("transport");
+                        CHECK_GE(i, 0);
+
+                        if (!track->mUsingInterleavedTCP) {
+                            AString transport = response->mHeaders.valueAt(i);
+
+                            pokeAHole(track->mRTPSocket,
+                                      track->mRTCPSocket,
+                                      transport);
+                        }
+
                         mRTPConn->addStream(
                                 track->mRTPSocket, track->mRTCPSocket,
                                 mSessionDesc, index,
diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp
index 2f20641..a4233e7 100644
--- a/native/android/storage_manager.cpp
+++ b/native/android/storage_manager.cpp
@@ -21,10 +21,13 @@
 
 #include <binder/Binder.h>
 #include <binder/IServiceManager.h>
+#include <utils/Atomic.h>
 #include <utils/Log.h>
 #include <utils/RefBase.h>
 #include <utils/String8.h>
 #include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
 
 
 using namespace android;
@@ -38,20 +41,46 @@
             mStorageManager(mgr)
     {}
 
-    virtual void onObbResult(const android::String16& filename, const android::String16& state);
+    virtual void onObbResult(const android::String16& filename, const int32_t nonce,
+            const int32_t state);
+};
+
+class ObbCallback {
+public:
+    ObbCallback(int32_t _nonce, AStorageManager_obbCallbackFunc _cb, void* _data)
+            : nonce(_nonce)
+            , cb(_cb)
+            , data(_data)
+    {}
+
+    int32_t nonce;
+    AStorageManager_obbCallbackFunc cb;
+    void* data;
 };
 
 struct AStorageManager : public RefBase {
 protected:
-    AStorageManager_obbCallbackFunc mObbCallback;
-    void* mObbCallbackData;
+    Mutex mCallbackLock;
+    Vector<ObbCallback*> mCallbacks;
+    volatile int32_t mNextNonce;
     sp<ObbActionListener> mObbActionListener;
     sp<IMountService> mMountService;
 
+    int32_t getNextNonce() {
+        return android_atomic_inc(&mNextNonce);
+    }
+
+    ObbCallback* registerObbCallback(AStorageManager_obbCallbackFunc func, void* data) {
+        ObbCallback* cb = new ObbCallback(getNextNonce(), func, data);
+        {
+            AutoMutex _l(mCallbackLock);
+            mCallbacks.push(cb);
+        }
+        return cb;
+    }
+
 public:
     AStorageManager()
-            : mObbCallback(NULL)
-            , mObbCallbackData(NULL)
     {
     }
 
@@ -73,26 +102,40 @@
         return true;
     }
 
-    void setObbCallback(AStorageManager_obbCallbackFunc cb, void* data) {
-        mObbCallback = cb;
-        mObbCallbackData = data;
-    }
+    void fireCallback(const char* filename, const int32_t nonce, const int32_t state) {
+        ObbCallback* target = NULL;
+        {
+            AutoMutex _l(mCallbackLock);
+            int N = mCallbacks.size();
+            for (int i = 0; i < N; i++) {
+                ObbCallback* cb = mCallbacks.editItemAt(i);
+                if (cb->nonce == nonce) {
+                    target = cb;
+                    mCallbacks.removeAt(i);
+                    break;
+                }
+            }
+        }
 
-    void fireCallback(const char* filename, const char* state) {
-        if (mObbCallback != NULL) {
-            mObbCallback(filename, state, mObbCallbackData);
+        if (target != NULL) {
+            target->cb(filename, state, target->data);
+            delete target;
+        } else {
+            LOGI("Didn't find the callback handler for: %s\n", filename);
         }
     }
 
-    void mountObb(const char* filename, const char* key) {
+    void mountObb(const char* filename, const char* key, AStorageManager_obbCallbackFunc func, void* data) {
+        ObbCallback* cb = registerObbCallback(func, data);
         String16 filename16(filename);
         String16 key16(key);
-        mMountService->mountObb(filename16, key16, mObbActionListener);
+        mMountService->mountObb(filename16, key16, mObbActionListener, cb->nonce);
     }
 
-    void unmountObb(const char* filename, const bool force) {
+    void unmountObb(const char* filename, const bool force, AStorageManager_obbCallbackFunc func, void* data) {
+        ObbCallback* cb = registerObbCallback(func, data);
         String16 filename16(filename);
-        mMountService->unmountObb(filename16, force, mObbActionListener);
+        mMountService->unmountObb(filename16, force, mObbActionListener, cb->nonce);
     }
 
     int isObbMounted(const char* filename) {
@@ -111,8 +154,8 @@
     }
 };
 
-void ObbActionListener::onObbResult(const android::String16& filename, const android::String16& state) {
-    mStorageManager->fireCallback(String8(filename).string(), String8(state).string());
+void ObbActionListener::onObbResult(const android::String16& filename, const int32_t nonce, const int32_t state) {
+    mStorageManager->fireCallback(String8(filename).string(), nonce, state);
 }
 
 
@@ -131,16 +174,14 @@
     }
 }
 
-void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data) {
-    mgr->setObbCallback(cb, data);
+void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key,
+        AStorageManager_obbCallbackFunc cb, void* data) {
+    mgr->mountObb(filename, key, cb, data);
 }
 
-void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key) {
-    mgr->mountObb(filename, key);
-}
-
-void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force) {
-    mgr->unmountObb(filename, force != 0);
+void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force,
+        AStorageManager_obbCallbackFunc cb, void* data) {
+    mgr->unmountObb(filename, force != 0, cb, data);
 }
 
 int AStorageManager_isObbMounted(AStorageManager* mgr, const char* filename) {
diff --git a/native/include/android/storage_manager.h b/native/include/android/storage_manager.h
index 6f925c1..c202693 100644
--- a/native/include/android/storage_manager.h
+++ b/native/include/android/storage_manager.h
@@ -18,6 +18,8 @@
 #ifndef ANDROID_STORAGE_MANAGER_H
 #define ANDROID_STORAGE_MANAGER_H
 
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -25,6 +27,60 @@
 struct AStorageManager;
 typedef struct AStorageManager AStorageManager;
 
+enum {
+    /*
+     * The OBB container is now mounted and ready for use. Can be returned
+     * as the status for callbacks made during asynchronous OBB actions.
+     */
+    AOBB_STATE_MOUNTED = 1,
+
+    /*
+     * The OBB container is now unmounted and not usable. Can be returned
+     * as the status for callbacks made during asynchronous OBB actions.
+     */
+    AOBB_STATE_UNMOUNTED = 2,
+
+    /*
+     * There was an internal system error encountered while trying to
+     * mount the OBB. Can be returned as the status for callbacks made
+     * during asynchronous OBB actions.
+     */
+    AOBB_STATE_ERROR_INTERNAL = 20,
+
+    /*
+     * The OBB could not be mounted by the system. Can be returned as the
+     * status for callbacks made during asynchronous OBB actions.
+     */
+    AOBB_STATE_ERROR_COULD_NOT_MOUNT = 21,
+
+    /*
+     * The OBB could not be unmounted. This most likely indicates that a
+     * file is in use on the OBB. Can be returned as the status for
+     * callbacks made during asynchronous OBB actions.
+     */
+    AOBB_STATE_ERROR_COULD_NOT_UNMOUNT = 22,
+
+    /*
+     * A call was made to unmount the OBB when it was not mounted. Can be
+     * returned as the status for callbacks made during asynchronous OBB
+     * actions.
+     */
+    AOBB_STATE_ERROR_NOT_MOUNTED = 23,
+
+    /*
+     * The OBB has already been mounted. Can be returned as the status for
+     * callbacks made during asynchronous OBB actions.
+     */
+    AOBB_STATE_ERROR_ALREADY_MOUNTED = 24,
+
+    /*
+     * The current application does not have permission to use this OBB
+     * because the OBB indicates it's owned by a different package or the
+     * key used to open it is incorrect. Can be returned as the status for
+     * callbacks made during asynchronous OBB actions.
+     */
+    AOBB_STATE_ERROR_PERMISSION_DENIED = 25,
+};
 
 /**
  * Obtains a new instance of AStorageManager.
@@ -39,22 +95,19 @@
 /**
  * Callback function for asynchronous calls made on OBB files.
  */
-typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const char* state, void* data);
-
-/**
- * Callback to call when requested asynchronous OBB operation is complete.
- */
-void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data);
+typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const int32_t state, void* data);
 
 /**
  * Attempts to mount an OBB file. This is an asynchronous operation.
  */
-void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key);
+void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key,
+        AStorageManager_obbCallbackFunc cb, void* data);
 
 /**
  * Attempts to unmount an OBB file. This is an asynchronous operation.
  */
-void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force);
+void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force,
+        AStorageManager_obbCallbackFunc cb, void* data);
 
 /**
  * Check whether an OBB is mounted.
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0fccbe7..7995869 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -20,6 +20,10 @@
         <activity android:name=".usb.UsbStorageActivity"
                 android:excludeFromRecents="true">
         </activity>
+        <activity android:name="com.android.internal.app.ExternalMediaFormatActivity"
+                android:theme="@*android:style/Theme.Dialog.Alert"
+                android:excludeFromRecents="true">
+        </activity>
 
     </application>
 </manifest>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
index f3da2a3..b1c6ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
@@ -294,6 +294,7 @@
         mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
         mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
 
+        mExpandedView.setVisibility(View.GONE);
         mOngoingTitle.setVisibility(View.GONE);
         mLatestTitle.setVisibility(View.GONE);
 
@@ -335,7 +336,7 @@
                 PixelFormat.RGBX_8888);
         lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
         lp.setTitle("StatusBar");
-        // TODO lp.windowAnimations = R.style.Animation_StatusBar;
+        lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar;
 
         WindowManagerImpl.getDefault().addView(view, lp);
     }
@@ -691,6 +692,7 @@
         mExpandedDialog.getWindow().setAttributes(mExpandedParams);
         mExpandedView.requestFocus(View.FOCUS_FORWARD);
         mTrackingView.setVisibility(View.VISIBLE);
+        mExpandedView.setVisibility(View.VISIBLE);
 
         if (!mTicking) {
             setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
@@ -767,6 +769,7 @@
         mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         mExpandedDialog.getWindow().setAttributes(mExpandedParams);
         mTrackingView.setVisibility(View.GONE);
+        mExpandedView.setVisibility(View.GONE);
 
         if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
             setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
@@ -1323,29 +1326,26 @@
             mExpandedContents.getLocationInWindow(mPositionTmp);
             final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
 
-            mExpandedParams.y = pos + mTrackingView.getHeight()
-                    - (mTrackingParams.height-closePos) - contentsBottom;
-            int max = h;
-            if (mExpandedParams.y > max) {
-                mExpandedParams.y = max;
-            }
-            int min = mTrackingPosition;
-            if (mExpandedParams.y < min) {
-                mExpandedParams.y = min;
-            }
-
-            boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
-            if (!visible) {
-                // if the contents aren't visible, move the expanded view way off screen
-                // because the window itself extends below the content view.
-                mExpandedParams.y = -disph;
-            }
-            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-
-            // As long as this isn't just a repositioning that's not supposed to affect
-            // the user's perception of what's showing, call to say that the visibility
-            // has changed. (Otherwise, someone else will call to do that).
             if (expandedPosition != EXPANDED_LEAVE_ALONE) {
+                mExpandedParams.y = pos + mTrackingView.getHeight()
+                        - (mTrackingParams.height-closePos) - contentsBottom;
+                int max = h;
+                if (mExpandedParams.y > max) {
+                    mExpandedParams.y = max;
+                }
+                int min = mTrackingPosition;
+                if (mExpandedParams.y < min) {
+                    mExpandedParams.y = min;
+                }
+
+                boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
+                if (!visible) {
+                    // if the contents aren't visible, move the expanded view way off screen
+                    // because the window itself extends below the content view.
+                    mExpandedParams.y = -disph;
+                }
+                mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+
                 if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
                 visibilityChanged(visible);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index b9e915a4..47ed7da 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -16,33 +16,17 @@
 
 package com.android.systemui.usb;
 
-import android.app.Activity;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.content.res.Resources;
-import android.os.Bundle;
 import android.os.Environment;
-import android.os.Handler;
-import android.os.storage.IMountService;
-import android.os.Message;
-import android.os.ServiceManager;
 import android.os.storage.StorageEventListener;
 import android.os.storage.StorageManager;
-import android.os.storage.StorageResultCode;
 import android.provider.Settings;
 import android.util.Slog;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
 
 public class StorageNotification extends StorageEventListener {
     private static final String TAG = "StorageNotification";
@@ -165,10 +149,16 @@
                      * Show safe to unmount media notification, and enable UMS
                      * notification if connected.
                      */
-                    setMediaStorageNotification(
-                            com.android.internal.R.string.ext_media_safe_unmount_notification_title,
-                            com.android.internal.R.string.ext_media_safe_unmount_notification_message,
-                            com.android.internal.R.drawable.stat_notify_sdcard, true, true, null);
+                    if (Environment.isExternalStorageRemovable()) {
+                        setMediaStorageNotification(
+                                com.android.internal.R.string.ext_media_safe_unmount_notification_title,
+                                com.android.internal.R.string.ext_media_safe_unmount_notification_message,
+                                com.android.internal.R.drawable.stat_notify_sdcard, true, true, null);
+                    } else {
+                        // This device does not have removable storage, so
+                        // don't tell the user they can remove it.
+                        setMediaStorageNotification(0, 0, 0, false, false, null);
+                    }
                     updateUsbMassStorageNotification(mUmsAvailable);
                 }
             } else {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index f21d357..3cf4360 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -1083,7 +1083,7 @@
                 if (!down) {
                     mHomePressed = false;
                     
-                    if (! canceled) {
+                    if (!canceled) {
                         // If an incoming call is ringing, HOME is totally disabled.
                         // (The user is already on the InCallScreen at this point,
                         // and his ONLY options are to answer or reject the call.)
@@ -1735,10 +1735,21 @@
     public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
             int policyFlags, boolean isScreenOn) {
         int result = ACTION_PASS_TO_USER;
-        
+
+        if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0) {
+            performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
+        }
+
         final boolean isWakeKey = (policyFlags
                 & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
-        
+
+        // If the key is injected, pretend that the screen is on and don't let the
+        // device go to sleep.  This feature is mainly used for testing purposes.
+        final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
+        if (isInjected) {
+            isScreenOn = true;
+        }
+
         // If screen is off then we treat the case where the keyguard is open but hidden
         // the same as if it were open and in front.
         // This will prevent any keys other than the power button from waking the screen
@@ -1837,7 +1848,7 @@
                         || (handled && hungUp && keyCode == KeyEvent.KEYCODE_POWER)) {
                     mShouldTurnOffOnKeyUp = false;
                 } else {
-                    // only try to turn off the screen if we didn't already hang up
+                    // Only try to turn off the screen if we didn't already hang up.
                     mShouldTurnOffOnKeyUp = true;
                     mHandler.postDelayed(mPowerLongPress,
                             ViewConfiguration.getGlobalActionKeyTimeout());
@@ -1860,12 +1871,14 @@
                     if (keyguardActive
                             || (sleeps && !gohome)
                             || (gohome && !goHome() && sleeps)) {
-                        // they must already be on the keyguad or home screen,
-                        // go to sleep instead
-                        Log.d(TAG, "I'm tired mEndcallBehavior=0x"
-                                + Integer.toHexString(mEndcallBehavior));
-                        result &= ~ACTION_POKE_USER_ACTIVITY;
-                        result |= ACTION_GO_TO_SLEEP;
+                        // They must already be on the keyguard or home screen,
+                        // go to sleep instead unless the event was injected.
+                        if (!isInjected) {
+                            Log.d(TAG, "I'm tired mEndcallBehavior=0x"
+                                    + Integer.toHexString(mEndcallBehavior));
+                            result &= ~ACTION_POKE_USER_ACTIVITY;
+                            result |= ACTION_GO_TO_SLEEP;
+                        }
                     }
                     result &= ~ACTION_PASS_TO_USER;
                 }
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index aa87f29..50b3abe 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -84,7 +84,7 @@
 
 class BackupManagerService extends IBackupManager.Stub {
     private static final String TAG = "BackupManagerService";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     // How often we perform a backup pass.  Privileged external callers can
     // trigger an immediate pass.
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index ed1243a..df41264 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -359,11 +359,6 @@
         private static final String CALIBRATION_DIR_PATH = "usr/idc/";
         
         @SuppressWarnings("unused")
-        public void virtualKeyDownFeedback() {
-            mWindowManagerService.mInputMonitor.virtualKeyDownFeedback();
-        }
-        
-        @SuppressWarnings("unused")
         public void notifyConfigurationChanged(long whenNanos) {
             mWindowManagerService.sendNewConfiguration();
         }
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 3b2d836..775f5c8 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -44,6 +44,7 @@
 import android.os.storage.IMountServiceListener;
 import android.os.storage.IMountShutdownObserver;
 import android.os.storage.IObbActionListener;
+import android.os.storage.OnObbStateChangeListener;
 import android.os.storage.StorageResultCode;
 import android.security.MessageDigest;
 import android.util.Slog;
@@ -53,7 +54,6 @@
 import java.io.PrintWriter;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -161,25 +161,25 @@
     final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
 
     class ObbState implements IBinder.DeathRecipient {
-        public ObbState(String filename, IObbActionListener token, int callerUid)
+        public ObbState(String filename, int callerUid, IObbActionListener token, int nonce)
                 throws RemoteException {
             this.filename = filename;
-            this.token = token;
             this.callerUid = callerUid;
-            mounted = false;
+            this.token = token;
+            this.nonce = nonce;
         }
 
         // OBB source filename
-        final String filename;
-
-        // Token of remote Binder caller
-        final IObbActionListener token;
+        String filename;
 
         // Binder.callingUid()
         final public int callerUid;
 
-        // Whether this is mounted currently.
-        boolean mounted;
+        // Token of remote Binder caller
+        final IObbActionListener token;
+
+        // Identifier to pass back to the token
+        final int nonce;
 
         public IBinder getBinder() {
             return token.asBinder();
@@ -208,8 +208,6 @@
             sb.append(token.toString());
             sb.append(",callerUid=");
             sb.append(callerUid);
-            sb.append(",mounted=");
-            sb.append(mounted);
             sb.append('}');
             return sb.toString();
         }
@@ -223,6 +221,7 @@
     private static final int OBB_MCS_BOUND = 2;
     private static final int OBB_MCS_UNBIND = 3;
     private static final int OBB_MCS_RECONNECT = 4;
+    private static final int OBB_FLUSH_MOUNT_STATE = 5;
 
     /*
      * Default Container Service information
@@ -500,40 +499,23 @@
             Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
             return;
         }
-        // Update state on PackageManager
+
         if (Environment.MEDIA_UNMOUNTED.equals(state)) {
+            // Tell the package manager the media is gone.
             mPms.updateExternalMediaStatus(false, false);
+
+            /*
+             * Some OBBs might have been unmounted when this volume was
+             * unmounted, so send a message to the handler to let it know to
+             * remove those from the list of mounted OBBS.
+             */
+            mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
+                    path));
         } else if (Environment.MEDIA_MOUNTED.equals(state)) {
+            // Tell the package manager the media is available for use.
             mPms.updateExternalMediaStatus(true, false);
         }
 
-        // Remove all OBB mappings and listeners from this path
-        synchronized (mObbMounts) {
-            final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
-
-            final Iterator<Entry<String, ObbState>> i = mObbPathToStateMap.entrySet().iterator();
-            while (i.hasNext()) {
-                final Entry<String, ObbState> obbEntry = i.next();
-
-                // If this entry's source file is in the volume path that got
-                // unmounted, remove it because it's no longer valid.
-                if (obbEntry.getKey().startsWith(path)) {
-                    obbStatesToRemove.add(obbEntry.getValue());
-                }
-            }
-
-            for (final ObbState obbState : obbStatesToRemove) {
-                removeObbState(obbState);
-
-                try {
-                    obbState.token.onObbResult(obbState.filename, Environment.MEDIA_UNMOUNTED);
-                } catch (RemoteException e) {
-                    Slog.i(TAG, "Couldn't send unmount notification for  OBB: "
-                            + obbState.filename);
-                }
-            }
-        }
-
         String oldState = mLegacyState;
         mLegacyState = state;
 
@@ -1530,6 +1512,10 @@
     }
 
     public String getMountedObbPath(String filename) {
+        if (filename == null) {
+            throw new IllegalArgumentException("filename cannot be null");
+        }
+
         waitForReady();
         warnOnNotMounted();
 
@@ -1552,164 +1538,98 @@
     }
 
     public boolean isObbMounted(String filename) {
-        synchronized (mObbMounts) {
-            final ObbState obbState = mObbPathToStateMap.get(filename);
-            if (obbState != null) {
-                synchronized (obbState) {
-                    return obbState.mounted;
-                }
-            }
+        if (filename == null) {
+            throw new IllegalArgumentException("filename cannot be null");
         }
-        return false;
+
+        synchronized (mObbMounts) {
+            return mObbPathToStateMap.containsKey(filename);
+        }
     }
 
-    public void mountObb(String filename, String key, IObbActionListener token)
+    public void mountObb(String filename, String key, IObbActionListener token, int nonce)
             throws RemoteException {
-        waitForReady();
-        warnOnNotMounted();
-
         if (filename == null) {
             throw new IllegalArgumentException("filename cannot be null");
-        } else if (token == null) {
+        }
+
+        if (token == null) {
             throw new IllegalArgumentException("token cannot be null");
         }
 
-        final ObbState obbState;
-
-        synchronized (mObbMounts) {
-            if (isObbMounted(filename)) {
-                try {
-                    token.onObbResult(filename, Environment.MEDIA_MOUNTED);
-                } catch (RemoteException e) {
-                    Slog.d(TAG, "Could not send unmount notification for: " + filename);
-                }
-                return;
-            }
-
-            final int callerUid = Binder.getCallingUid();
-            obbState = new ObbState(filename, token, callerUid);
-            addObbState(obbState);
-        }
-
-        String hashedKey = null;
-        if (key != null) {
-            final MessageDigest md;
-            try {
-                md = MessageDigest.getInstance("MD5");
-            } catch (NoSuchAlgorithmException e) {
-                Slog.e(TAG, "Could not load MD5 algorithm", e);
-                try {
-                    token.onObbResult(filename, Environment.MEDIA_UNMOUNTED);
-                } catch (RemoteException e1) {
-                    Slog.d(TAG, "Could not send unmount notification for: " + filename);
-                }
-                return;
-            }
-
-            hashedKey = HexDump.toHexString(md.digest(key.getBytes()));
-        }
-
-        ObbAction action = new MountObbAction(obbState, hashedKey);
+        final int callerUid = Binder.getCallingUid();
+        final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
+        final ObbAction action = new MountObbAction(obbState, key);
         mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
 
         if (DEBUG_OBB)
             Slog.i(TAG, "Send to OBB handler: " + action.toString());
     }
 
-    public void unmountObb(String filename, boolean force, IObbActionListener token) {
+    public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce)
+            throws RemoteException {
         if (filename == null) {
             throw new IllegalArgumentException("filename cannot be null");
-        } else if (token == null) {
-            throw new IllegalArgumentException("token cannot be null");
         }
 
-        final ObbState obbState;
-
-        synchronized (mObbMounts) {
-            if (!isObbMounted(filename)) {
-                try {
-                    token.onObbResult(filename, Environment.MEDIA_UNMOUNTED);
-                } catch (RemoteException e) {
-                    Slog.d(TAG, "Could not send unmount notification for: " + filename);
-                }
-                return;
-            }
-
-            obbState = mObbPathToStateMap.get(filename);
-
-            if (Binder.getCallingUid() != obbState.callerUid) {
-                throw new SecurityException("caller UID does not match original mount caller UID");
-            } else if (!token.asBinder().equals(obbState.getBinder())) {
-                throw new SecurityException("caller does not match original mount caller");
-            }
-        }
-
-        ObbAction action = new UnmountObbAction(obbState, force);
+        final int callerUid = Binder.getCallingUid();
+        final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
+        final ObbAction action = new UnmountObbAction(obbState, force);
         mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
 
         if (DEBUG_OBB)
             Slog.i(TAG, "Send to OBB handler: " + action.toString());
     }
 
-    private void addObbState(ObbState obbState) throws RemoteException {
-        synchronized (mObbMounts) {
-            final IBinder binder = obbState.getBinder();
-            List<ObbState> obbStates = mObbMounts.get(binder);
-            final boolean unique;
+    private void addObbStateLocked(ObbState obbState) throws RemoteException {
+        final IBinder binder = obbState.getBinder();
+        List<ObbState> obbStates = mObbMounts.get(binder);
 
-            if (obbStates == null) {
-                obbStates = new ArrayList<ObbState>();
-                mObbMounts.put(binder, obbStates);
-                unique = true;
-            } else {
-                unique = obbStates.contains(obbState);
-            }
-
-            if (unique) {
-                obbStates.add(obbState);
-                try {
-                    obbState.link();
-                } catch (RemoteException e) {
-                    /*
-                     * The binder died before we could link it, so clean up our
-                     * state and return failure.
-                     */
-                    obbStates.remove(obbState);
-                    if (obbStates.isEmpty()) {
-                        mObbMounts.remove(binder);
-                    }
-
-                    // Rethrow the error so mountObb can get it
-                    throw e;
+        if (obbStates == null) {
+            obbStates = new ArrayList<ObbState>();
+            mObbMounts.put(binder, obbStates);
+        } else {
+            for (final ObbState o : obbStates) {
+                if (o.filename.equals(obbState.filename)) {
+                    throw new IllegalStateException("Attempt to add ObbState twice. "
+                            + "This indicates an error in the MountService logic.");
                 }
             }
-
-            mObbPathToStateMap.put(obbState.filename, obbState);
         }
+
+        obbStates.add(obbState);
+        try {
+            obbState.link();
+        } catch (RemoteException e) {
+            /*
+             * The binder died before we could link it, so clean up our state
+             * and return failure.
+             */
+            obbStates.remove(obbState);
+            if (obbStates.isEmpty()) {
+                mObbMounts.remove(binder);
+            }
+
+            // Rethrow the error so mountObb can get it
+            throw e;
+        }
+
+        mObbPathToStateMap.put(obbState.filename, obbState);
     }
 
-    private void removeObbState(ObbState obbState) {
-        synchronized (mObbMounts) {
-            final IBinder binder = obbState.getBinder();
-            final List<ObbState> obbStates = mObbMounts.get(binder);
-            if (obbStates != null) {
-                if (obbStates.remove(obbState)) {
-                    obbState.unlink();
-                }
-                if (obbStates.isEmpty()) {
-                    mObbMounts.remove(binder);
-                }
+    private void removeObbStateLocked(ObbState obbState) {
+        final IBinder binder = obbState.getBinder();
+        final List<ObbState> obbStates = mObbMounts.get(binder);
+        if (obbStates != null) {
+            if (obbStates.remove(obbState)) {
+                obbState.unlink();
             }
-
-            mObbPathToStateMap.remove(obbState.filename);
+            if (obbStates.isEmpty()) {
+                mObbMounts.remove(binder);
+            }
         }
-    }
 
-    private void replaceObbState(ObbState oldObbState, ObbState newObbState) throws RemoteException {
-        synchronized (mObbMounts) {
-            removeObbState(oldObbState);
-            addObbState(newObbState);
-        }
+        mObbPathToStateMap.remove(obbState.filename);
     }
 
     private class ObbActionHandler extends Handler {
@@ -1808,6 +1728,47 @@
                     }
                     break;
                 }
+                case OBB_FLUSH_MOUNT_STATE: {
+                    final String path = (String) msg.obj;
+
+                    if (DEBUG_OBB)
+                        Slog.i(TAG, "Flushing all OBB state for path " + path);
+
+                    synchronized (mObbMounts) {
+                        final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
+
+                        final Iterator<Entry<String, ObbState>> i =
+                                mObbPathToStateMap.entrySet().iterator();
+                        while (i.hasNext()) {
+                            final Entry<String, ObbState> obbEntry = i.next();
+
+                            /*
+                             * If this entry's source file is in the volume path
+                             * that got unmounted, remove it because it's no
+                             * longer valid.
+                             */
+                            if (obbEntry.getKey().startsWith(path)) {
+                                obbStatesToRemove.add(obbEntry.getValue());
+                            }
+                        }
+
+                        for (final ObbState obbState : obbStatesToRemove) {
+                            if (DEBUG_OBB)
+                                Slog.i(TAG, "Removing state for " + obbState.filename);
+
+                            removeObbStateLocked(obbState);
+
+                            try {
+                                obbState.token.onObbResult(obbState.filename, obbState.nonce,
+                                        OnObbStateChangeListener.UNMOUNTED);
+                            } catch (RemoteException e) {
+                                Slog.i(TAG, "Couldn't send unmount notification for  OBB: "
+                                        + obbState.filename);
+                            }
+                        }
+                    }
+                    break;
+                }
             }
         }
 
@@ -1886,9 +1847,13 @@
             return obbInfo;
         }
 
-        protected void sendNewStatusOrIgnore(String filename, String status) {
+        protected void sendNewStatusOrIgnore(int status) {
+            if (mObbState == null || mObbState.token == null) {
+                return;
+            }
+
             try {
-                mObbState.token.onObbResult(filename, status);
+                mObbState.token.onObbResult(mObbState.filename, mObbState.nonce, status);
             } catch (RemoteException e) {
                 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
             }
@@ -1904,89 +1869,80 @@
         }
 
         public void handleExecute() throws IOException, RemoteException {
+            waitForReady();
+            warnOnNotMounted();
+
             final ObbInfo obbInfo = getObbInfo();
 
-            /*
-             * If someone tried to trick us with some weird characters, rectify
-             * it here.
-             */
-            if (!mObbState.filename.equals(obbInfo.filename)) {
-                if (DEBUG_OBB)
-                    Slog.i(TAG, "OBB filename " + mObbState.filename + " is actually "
-                            + obbInfo.filename);
-
-                synchronized (mObbMounts) {
-                    /*
-                     * If the real filename is already mounted, discard this
-                     * state and notify the caller that the OBB is already
-                     * mounted.
-                     */
-                    if (isObbMounted(obbInfo.filename)) {
-                        if (DEBUG_OBB)
-                            Slog.i(TAG, "OBB already mounted as " + obbInfo.filename);
-
-                        removeObbState(mObbState);
-                        sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_MOUNTED);
-                        return;
-                    }
-
-                    /*
-                     * It's not already mounted, so we have to replace the state
-                     * with the state containing the actual filename.
-                     */
-                    ObbState newObbState = new ObbState(obbInfo.filename, mObbState.token,
-                            mObbState.callerUid);
-                    replaceObbState(mObbState, newObbState);
-                    mObbState = newObbState;
-                }
-            }
-
             if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
-                throw new IllegalArgumentException("Caller package does not match OBB file");
+                Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
+                        + " which is owned by " + obbInfo.packageName);
+                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+                return;
             }
 
-            boolean mounted = false;
-            int rc;
-            synchronized (mObbState) {
-                if (mObbState.mounted) {
-                    sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
+            final boolean isMounted;
+            synchronized (mObbMounts) {
+                isMounted = mObbPathToStateMap.containsKey(obbInfo.filename);
+            }
+            if (isMounted) {
+                Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
+                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
+                return;
+            }
+
+            /*
+             * The filename passed in might not be the canonical name, so just
+             * set the filename to the canonicalized version.
+             */
+            mObbState.filename = obbInfo.filename;
+
+            final String hashedKey;
+            if (mKey == null) {
+                hashedKey = "none";
+            } else {
+                final MessageDigest md;
+                try {
+                    md = MessageDigest.getInstance("MD5");
+                } catch (NoSuchAlgorithmException e) {
+                    Slog.e(TAG, "Could not load MD5 algorithm", e);
+                    sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
                     return;
                 }
 
-                rc = StorageResultCode.OperationSucceeded;
-                String cmd = String.format("obb mount %s %s %d", mObbState.filename,
-                        mKey != null ? mKey : "none",
-                        mObbState.callerUid);
-                try {
-                    mConnector.doCommand(cmd);
-                } catch (NativeDaemonConnectorException e) {
-                    int code = e.getCode();
-                    if (code != VoldResponseCode.OpFailedStorageBusy) {
-                        rc = StorageResultCode.OperationFailedInternalError;
-                    }
-                }
+                hashedKey = HexDump.toHexString(md.digest(mKey.getBytes()));
+            }
 
-                if (rc == StorageResultCode.OperationSucceeded) {
-                    mObbState.mounted = mounted = true;
+            int rc = StorageResultCode.OperationSucceeded;
+            String cmd = String.format("obb mount %s %s %d", mObbState.filename, hashedKey,
+                    mObbState.callerUid);
+            try {
+                mConnector.doCommand(cmd);
+            } catch (NativeDaemonConnectorException e) {
+                int code = e.getCode();
+                if (code != VoldResponseCode.OpFailedStorageBusy) {
+                    rc = StorageResultCode.OperationFailedInternalError;
                 }
             }
 
-            if (mounted) {
-                sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
+            if (rc == StorageResultCode.OperationSucceeded) {
+                if (DEBUG_OBB)
+                    Slog.d(TAG, "Successfully mounted OBB " + mObbState.filename);
+
+                synchronized (mObbMounts) {
+                    addObbStateLocked(mObbState);
+                }
+
+                sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
             } else {
                 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
 
-                // We didn't succeed, so remove this from the mount-set.
-                removeObbState(mObbState);
-
-                sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
+                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
             }
         }
 
         public void handleError() {
-            removeObbState(mObbState);
-
-            sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
+            sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
         }
 
         @Override
@@ -1999,6 +1955,8 @@
             sb.append(mObbState.callerUid);
             sb.append(",token=");
             sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
+            sb.append(",binder=");
+            sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
             sb.append('}');
             return sb.toString();
         }
@@ -2013,68 +1971,61 @@
         }
 
         public void handleExecute() throws IOException {
+            waitForReady();
+            warnOnNotMounted();
+
             final ObbInfo obbInfo = getObbInfo();
 
-            /*
-             * If someone tried to trick us with some weird characters, rectify
-             * it here.
-             */
+            final ObbState obbState;
             synchronized (mObbMounts) {
-                if (!isObbMounted(obbInfo.filename)) {
-                    removeObbState(mObbState);
-                    sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
-                    return;
-                }
+                obbState = mObbPathToStateMap.get(obbInfo.filename);
+            }
 
-                if (!mObbState.filename.equals(obbInfo.filename)) {
-                    removeObbState(mObbState);
-                    mObbState = mObbPathToStateMap.get(obbInfo.filename);
+            if (obbState == null) {
+                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
+                return;
+            }
+
+            if (obbState.callerUid != mObbState.callerUid) {
+                Slog.w(TAG, "Permission denied attempting to unmount OBB " + obbInfo.filename
+                        + " (owned by " + obbInfo.packageName + ")");
+                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+                return;
+            }
+
+            mObbState.filename = obbInfo.filename;
+
+            int rc = StorageResultCode.OperationSucceeded;
+            String cmd = String.format("obb unmount %s%s", mObbState.filename,
+                    (mForceUnmount ? " force" : ""));
+            try {
+                mConnector.doCommand(cmd);
+            } catch (NativeDaemonConnectorException e) {
+                int code = e.getCode();
+                if (code == VoldResponseCode.OpFailedStorageBusy) {
+                    rc = StorageResultCode.OperationFailedStorageBusy;
+                } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
+                    // If it's not mounted then we've already won.
+                    rc = StorageResultCode.OperationSucceeded;
+                } else {
+                    rc = StorageResultCode.OperationFailedInternalError;
                 }
             }
 
-            boolean unmounted = false;
-            synchronized (mObbState) {
-                if (!mObbState.mounted) {
-                    sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_UNMOUNTED);
-                    return;
+            if (rc == StorageResultCode.OperationSucceeded) {
+                synchronized (mObbMounts) {
+                    removeObbStateLocked(obbState);
                 }
 
-                int rc = StorageResultCode.OperationSucceeded;
-                String cmd = String.format("obb unmount %s%s", mObbState.filename,
-                        (mForceUnmount ? " force" : ""));
-                try {
-                    mConnector.doCommand(cmd);
-                } catch (NativeDaemonConnectorException e) {
-                    int code = e.getCode();
-                    if (code == VoldResponseCode.OpFailedStorageBusy) {
-                        rc = StorageResultCode.OperationFailedStorageBusy;
-                    } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
-                        // If it's not mounted then we've already won.
-                        rc = StorageResultCode.OperationSucceeded;
-                    } else {
-                        rc = StorageResultCode.OperationFailedInternalError;
-                    }
-                }
-
-                if (rc == StorageResultCode.OperationSucceeded) {
-                    mObbState.mounted = false;
-                    unmounted = true;
-                }
-            }
-
-            if (unmounted) {
-                removeObbState(mObbState);
-
-                sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
+                sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
             } else {
-                sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
+                Slog.w(TAG, "Could not mount OBB: " + mObbState.filename);
+                sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
             }
         }
 
         public void handleError() {
-            removeObbState(mObbState);
-
-            sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
+            sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
         }
 
         @Override
@@ -2090,7 +2041,7 @@
             sb.append(",token=");
             sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
             sb.append(",binder=");
-            sb.append(mObbState.getBinder().toString());
+            sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
             sb.append('}');
             return sb.toString();
         }
@@ -2105,16 +2056,27 @@
             return;
         }
 
-        pw.println("  mObbMounts:");
-
         synchronized (mObbMounts) {
-            final Collection<List<ObbState>> obbStateLists = mObbMounts.values();
+            pw.println("  mObbMounts:");
 
-            for (final List<ObbState> obbStates : obbStateLists) {
+            final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet().iterator();
+            while (binders.hasNext()) {
+                Entry<IBinder, List<ObbState>> e = binders.next();
+                pw.print("    Key="); pw.println(e.getKey().toString());
+                final List<ObbState> obbStates = e.getValue();
                 for (final ObbState obbState : obbStates) {
-                    pw.print("    "); pw.println(obbState.toString());
+                    pw.print("      "); pw.println(obbState.toString());
                 }
             }
+
+            pw.println("");
+            pw.println("  mObbPathToStateMap:");
+            final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
+            while (maps.hasNext()) {
+                final Entry<String, ObbState> e = maps.next();
+                pw.print("    "); pw.print(e.getKey());
+                pw.print(" -> "); pw.println(e.getValue().toString());
+            }
         }
     }
 }
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 37b4c1d..bc802a8 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -186,9 +186,11 @@
     static final int SCAN_UPDATE_TIME = 1<<6;
 
     static final int REMOVE_CHATTY = 1<<16;
-    
+
+    static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
+
     static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
-            "com.android.defcontainer",
+            DEFAULT_CONTAINER_PACKAGE,
             "com.android.defcontainer.DefaultContainerService");
 
     private static final String LIB_DIR_NAME = "lib";
@@ -4774,7 +4776,15 @@
                 ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
             } else {
                 // Remote call to find out default install location
-                PackageInfoLite pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags);
+                final PackageInfoLite pkgLite;
+                try {
+                    mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
+                            Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                    pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags);
+                } finally {
+                    mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                }
+
                 int loc = pkgLite.recommendedInstallLocation;
                 if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION){
                     ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
@@ -4989,8 +4999,14 @@
             libraryPath = new File(dataDir, LIB_DIR_NAME).getPath();
         }
 
-        boolean  checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
-            return imcs.checkFreeStorage(false, packageURI);
+        boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
+            try {
+                mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
+                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                return imcs.checkFreeStorage(false, packageURI);
+            } finally {
+                mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            }
         }
 
         String getCodePath() {
@@ -5034,11 +5050,14 @@
             // Copy the resource now
             int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
             try {
+                mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
+                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
                 if (imcs.copyResource(packageURI, out)) {
                     ret = PackageManager.INSTALL_SUCCEEDED;
                 }
             } finally {
                 try { if (out != null) out.close(); } catch (IOException e) {}
+                mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
             }
 
             return ret;
@@ -5209,17 +5228,31 @@
             cid = getTempContainerId();
         }
 
-        boolean  checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
-            return imcs.checkFreeStorage(true, packageURI);
+        boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
+            try {
+                mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
+                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                return imcs.checkFreeStorage(true, packageURI);
+            } finally {
+                mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            }
         }
 
         int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
             if (temp) {
                 createCopyFile();
             }
-            String newCachePath = imcs.copyResourceToContainer(
-                    packageURI, cid,
-                    getEncryptKey(), RES_FILE_NAME);
+
+            final String newCachePath;
+            try {
+                mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
+                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                newCachePath = imcs.copyResourceToContainer(packageURI, cid,
+                        getEncryptKey(), RES_FILE_NAME);
+            } finally {
+                mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            }
+
             if (newCachePath != null) {
                 setCachePath(newCachePath);
                 return PackageManager.INSTALL_SUCCEEDED;
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 496c665..88a4c90 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -245,6 +245,9 @@
     private int[] mButtonBacklightValues;
     private int[] mKeyboardBacklightValues;
     private int mLightSensorWarmupTime;
+    boolean mUnplugTurnsOnScreen;
+    private int mWarningSpewThrottleCount;
+    private long mWarningSpewThrottleTime;
 
     // Used when logging number and duration of touch-down cycles
     private long mTotalTouchDownTime;
@@ -364,8 +367,12 @@
                     // user activity when screen was already on.
                     // temporarily set mUserActivityAllowed to true so this will work
                     // even when the keyguard is on.
+                    // However, you can also set config_unplugTurnsOnScreen to have it
+                    // turn on.  Some devices want this because they don't have a
+                    // charging LED.
                     synchronized (mLocks) {
-                        if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0) {
+                        if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0 ||
+                                mUnplugTurnsOnScreen) {
                             forceUserActivityLocked();
                         }
                     }
@@ -524,6 +531,9 @@
 
         Resources resources = mContext.getResources();
 
+        mUnplugTurnsOnScreen = resources.getBoolean(
+                com.android.internal.R.bool.config_unplugTurnsOnScreen);
+
         // read settings for auto-brightness
         mUseSoftwareAutoBrightness = resources.getBoolean(
                 com.android.internal.R.bool.config_automatic_brightness_available);
@@ -2095,6 +2105,21 @@
         return (mScreenBrightness.animating && mScreenBrightness.targetValue == 0);
     }
 
+    private boolean shouldLog(long time) {
+        synchronized (mLocks) {
+            if (time > (mWarningSpewThrottleTime + (60*60*1000))) {
+                mWarningSpewThrottleTime = time;
+                mWarningSpewThrottleCount = 0;
+                return true;
+            } else if (mWarningSpewThrottleCount < 30) {
+                mWarningSpewThrottleCount++;
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
     private void forceUserActivityLocked() {
         if (isScreenTurningOffLocked()) {
             // cancel animation so userActivity will succeed
@@ -2112,7 +2137,15 @@
     }
 
     public void userActivity(long time, boolean noChangeLights) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
+                != PackageManager.PERMISSION_GRANTED) {
+            if (shouldLog(time)) {
+                Slog.w(TAG, "Caller does not have DEVICE_POWER permission.  pid="
+                        + Binder.getCallingPid() + " uid=" + Binder.getCallingUid());
+            }
+            return;
+        }
+
         userActivity(time, -1, noChangeLights, OTHER_EVENT, false);
     }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7f42429..26071ae 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -35,7 +35,12 @@
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.media.AudioService;
-import android.os.*;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.provider.Contacts.People;
 import android.provider.Settings;
 import android.server.BluetoothA2dpService;
@@ -573,6 +578,10 @@
     static Timer timer;
     static final long SNAPSHOT_INTERVAL = 60 * 60 * 1000; // 1hr
 
+    // The earliest supported time.  We pick one day into 1970, to
+    // give any timezone code room without going into negative time.
+    private static final long EARLIEST_SUPPORTED_TIME = 86400 * 1000;
+
     /**
      * This method is called from Zygote to initialize the system. This will cause the native
      * services (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call back
@@ -581,6 +590,16 @@
     native public static void init1(String[] args);
 
     public static void main(String[] args) {
+        if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
+            // If a device's clock is before 1970 (before 0), a lot of
+            // APIs crash dealing with negative numbers, notably
+            // java.io.File#setLastModified, so instead we fake it and
+            // hope that time from cell towers or NTP fixes it
+            // shortly.
+            Slog.w(TAG, "System clock is before 1970; setting to 1970.");
+            SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
+        }
+
         if (SamplingProfilerIntegration.isEnabled()) {
             SamplingProfilerIntegration.start();
             timer = new Timer();
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 0de57ab..42fffb0 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5243,13 +5243,6 @@
             mTempInputWindows.clear();
         }
         
-        /* Provides feedback for a virtual key down. */
-        public void virtualKeyDownFeedback() {
-            synchronized (mWindowMap) {
-                mPolicy.performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
-            }
-        }
-        
         /* Notifies that the lid switch changed state. */
         public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
             mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 87271e7..f9c1679 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -493,6 +493,11 @@
                 + " info: " + info);
         }
 
+        if (info != null) {
+            native_update_network_state(info.isConnected(), info.getType(),
+                    info.isRoaming(), info.getExtraInfo());
+        }
+
         if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
                 && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
             String apnName = info.getExtraInfo();
@@ -1601,4 +1606,7 @@
     private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
             int lac, int cid);
     private native void native_agps_set_id(int type, String setid);
+
+    private native void native_update_network_state(boolean connected, int type,
+            boolean roaming, String extraInfo);
 }
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 18037e4..1bd1874 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -50,7 +50,6 @@
     jmethodID notifyLidSwitchChanged;
     jmethodID notifyInputChannelBroken;
     jmethodID notifyANR;
-    jmethodID virtualKeyDownFeedback;
     jmethodID interceptKeyBeforeQueueing;
     jmethodID interceptKeyBeforeDispatching;
     jmethodID checkInjectEventsPermission;
@@ -192,6 +191,8 @@
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
+    virtual void notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
+            uint32_t policyFlags);
     virtual void notifyConfigurationChanged(nsecs_t when);
     virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
             const sp<InputChannel>& inputChannel);
@@ -205,8 +206,6 @@
     virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
     virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
             const KeyEvent* keyEvent, uint32_t policyFlags);
-    virtual void notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
-            uint32_t policyFlags);
     virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
     virtual bool checkInjectEventsPermissionNonReentrant(
             int32_t injectorPid, int32_t injectorUid);
@@ -255,7 +254,6 @@
 
     static bool populateWindow(JNIEnv* env, jobject windowObj, InputWindow& outWindow);
 
-    static bool isPolicyKey(int32_t keyCode, bool isScreenOn);
     static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
 
     static inline JNIEnv* jniEnv() {
@@ -291,37 +289,6 @@
     dump.append("\n");
 }
 
-bool NativeInputManager::isPolicyKey(int32_t keyCode, bool isScreenOn) {
-    // Special keys that the WindowManagerPolicy might care about.
-    switch (keyCode) {
-    case AKEYCODE_VOLUME_UP:
-    case AKEYCODE_VOLUME_DOWN:
-    case AKEYCODE_ENDCALL:
-    case AKEYCODE_POWER:
-    case AKEYCODE_CALL:
-    case AKEYCODE_HOME:
-    case AKEYCODE_MENU:
-    case AKEYCODE_SEARCH:
-        // media keys
-    case AKEYCODE_HEADSETHOOK:
-    case AKEYCODE_MEDIA_PLAY_PAUSE:
-    case AKEYCODE_MEDIA_STOP:
-    case AKEYCODE_MEDIA_NEXT:
-    case AKEYCODE_MEDIA_PREVIOUS:
-    case AKEYCODE_MEDIA_REWIND:
-    case AKEYCODE_MEDIA_FAST_FORWARD:
-        // The policy always cares about these keys.
-        return true;
-    default:
-        // We need to pass all keys to the policy in the following cases:
-        // - screen is off
-        // - keyguard is visible
-        // - policy is performing key chording
-        //return ! isScreenOn || keyguardVisible || chording;
-        return true; // XXX stubbed out for now
-    }
-}
-
 bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
     if (env->ExceptionCheck()) {
         LOGE("An exception was thrown by callback '%s'.", methodName);
@@ -454,115 +421,6 @@
     return result;
 }
 
-bool NativeInputManager::isScreenOn() {
-    return android_server_PowerManagerService_isScreenOn();
-}
-
-bool NativeInputManager::isScreenBright() {
-    return android_server_PowerManagerService_isScreenBright();
-}
-
-void NativeInputManager::interceptKeyBeforeQueueing(nsecs_t when,
-        int32_t deviceId, int32_t action, int32_t &flags,
-        int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("interceptKeyBeforeQueueing - when=%lld, deviceId=%d, action=%d, flags=%d, "
-            "keyCode=%d, scanCode=%d, policyFlags=0x%x",
-            when, deviceId, action, flags, keyCode, scanCode, policyFlags);
-#endif
-
-    bool down = action == AKEY_EVENT_ACTION_DOWN;
-    if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
-        policyFlags |= POLICY_FLAG_VIRTUAL;
-        flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
-
-        if (down) {
-            JNIEnv* env = jniEnv();
-            env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyDownFeedback);
-            checkAndClearExceptionFromCallback(env, "virtualKeyDownFeedback");
-        }
-    }
-
-    const int32_t WM_ACTION_PASS_TO_USER = 1;
-    const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
-    const int32_t WM_ACTION_GO_TO_SLEEP = 4;
-
-    bool isScreenOn = this->isScreenOn();
-    bool isScreenBright = this->isScreenBright();
-
-    jint wmActions = 0;
-    if (isPolicyKey(keyCode, isScreenOn)) {
-        JNIEnv* env = jniEnv();
-
-        wmActions = env->CallIntMethod(mCallbacksObj,
-                gCallbacksClassInfo.interceptKeyBeforeQueueing,
-                when, keyCode, down, policyFlags, isScreenOn);
-        if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
-            wmActions = 0;
-        }
-    } else {
-        wmActions = WM_ACTION_PASS_TO_USER;
-    }
-
-    if (! isScreenOn) {
-        // Key presses and releases wake the device.
-        policyFlags |= POLICY_FLAG_WOKE_HERE;
-        flags |= AKEY_EVENT_FLAG_WOKE_HERE;
-    }
-
-    if (! isScreenBright) {
-        // Key presses and releases brighten the screen if dimmed.
-        policyFlags |= POLICY_FLAG_BRIGHT_HERE;
-    }
-
-    if (wmActions & WM_ACTION_GO_TO_SLEEP) {
-        android_server_PowerManagerService_goToSleep(when);
-    }
-
-    if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
-        android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT);
-    }
-
-    if (wmActions & WM_ACTION_PASS_TO_USER) {
-        policyFlags |= POLICY_FLAG_PASS_TO_USER;
-    }
-}
-
-void NativeInputManager::interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("interceptGenericBeforeQueueing - when=%lld, policyFlags=0x%x", when, policyFlags);
-#endif
-
-    if (isScreenOn()) {
-        // Only dispatch events when the device is awake.
-        // Do not wake the device.
-        policyFlags |= POLICY_FLAG_PASS_TO_USER;
-
-        if (! isScreenBright()) {
-            // Brighten the screen if dimmed.
-            policyFlags |= POLICY_FLAG_BRIGHT_HERE;
-        }
-    }
-}
-
-void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode,
-        int32_t switchValue, uint32_t policyFlags) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-    LOGD("notifySwitch - when=%lld, switchCode=%d, switchValue=%d, policyFlags=0x%x",
-            when, switchCode, switchValue, policyFlags);
-#endif
-
-    JNIEnv* env = jniEnv();
-
-    switch (switchCode) {
-    case SW_LID:
-        env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged,
-                when, switchValue == 0);
-        checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged");
-        break;
-    }
-}
-
 bool NativeInputManager::filterTouchEvents() {
     if (mFilterTouchEvents < 0) {
         JNIEnv* env = jniEnv();
@@ -692,6 +550,24 @@
     }
 }
 
+void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode,
+        int32_t switchValue, uint32_t policyFlags) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("notifySwitch - when=%lld, switchCode=%d, switchValue=%d, policyFlags=0x%x",
+            when, switchCode, switchValue, policyFlags);
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    switch (switchCode) {
+    case SW_LID:
+        env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged,
+                when, switchValue == 0);
+        checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged");
+        break;
+    }
+}
+
 void NativeInputManager::notifyConfigurationChanged(nsecs_t when) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
     LOGD("notifyConfigurationChanged - when=%lld", when);
@@ -944,26 +820,121 @@
     mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen);
 }
 
-bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
-        const KeyEvent* keyEvent, uint32_t policyFlags) {
-    bool isScreenOn = this->isScreenOn();
-    if (! isPolicyKey(keyEvent->getKeyCode(), isScreenOn)) {
-        return false;
+bool NativeInputManager::isScreenOn() {
+    return android_server_PowerManagerService_isScreenOn();
+}
+
+bool NativeInputManager::isScreenBright() {
+    return android_server_PowerManagerService_isScreenBright();
+}
+
+void NativeInputManager::interceptKeyBeforeQueueing(nsecs_t when,
+        int32_t deviceId, int32_t action, int32_t &flags,
+        int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("interceptKeyBeforeQueueing - when=%lld, deviceId=%d, action=%d, flags=%d, "
+            "keyCode=%d, scanCode=%d, policyFlags=0x%x",
+            when, deviceId, action, flags, keyCode, scanCode, policyFlags);
+#endif
+
+    if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
+        policyFlags |= POLICY_FLAG_VIRTUAL;
+        flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
     }
 
-    JNIEnv* env = jniEnv();
+    // Policy:
+    // - Ignore untrusted events and pass them along.
+    // - Ask the window manager what to do with normal events and trusted injected events.
+    // - For normal events wake and brighten the screen if currently off or dim.
+    if ((policyFlags & POLICY_FLAG_TRUSTED)) {
+        const int32_t WM_ACTION_PASS_TO_USER = 1;
+        const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
+        const int32_t WM_ACTION_GO_TO_SLEEP = 4;
 
-    // Note: inputChannel may be null.
-    jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel);
-    jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
-            gCallbacksClassInfo.interceptKeyBeforeDispatching,
-            inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
-            keyEvent->getKeyCode(), keyEvent->getMetaState(),
-            keyEvent->getRepeatCount(), policyFlags);
-    bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
+        bool isScreenOn = this->isScreenOn();
+        bool isScreenBright = this->isScreenBright();
 
-    env->DeleteLocalRef(inputChannelObj);
-    return consumed && ! error;
+        JNIEnv* env = jniEnv();
+        jint wmActions = env->CallIntMethod(mCallbacksObj,
+                gCallbacksClassInfo.interceptKeyBeforeQueueing,
+                when, keyCode, action == AKEY_EVENT_ACTION_DOWN, policyFlags, isScreenOn);
+        if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
+            wmActions = 0;
+        }
+
+        if (!(flags & POLICY_FLAG_INJECTED)) {
+            if (!isScreenOn) {
+                policyFlags |= POLICY_FLAG_WOKE_HERE;
+                flags |= AKEY_EVENT_FLAG_WOKE_HERE;
+            }
+
+            if (!isScreenBright) {
+                policyFlags |= POLICY_FLAG_BRIGHT_HERE;
+            }
+        }
+
+        if (wmActions & WM_ACTION_GO_TO_SLEEP) {
+            android_server_PowerManagerService_goToSleep(when);
+        }
+
+        if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
+            android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT);
+        }
+
+        if (wmActions & WM_ACTION_PASS_TO_USER) {
+            policyFlags |= POLICY_FLAG_PASS_TO_USER;
+        }
+    } else {
+        policyFlags |= POLICY_FLAG_PASS_TO_USER;
+    }
+}
+
+void NativeInputManager::interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("interceptGenericBeforeQueueing - when=%lld, policyFlags=0x%x", when, policyFlags);
+#endif
+
+    // Policy:
+    // - Ignore untrusted events and pass them along.
+    // - No special filtering for injected events required at this time.
+    // - Filter normal events based on screen state.
+    // - For normal events brighten (but do not wake) the screen if currently dim.
+    if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) {
+        if (isScreenOn()) {
+            policyFlags |= POLICY_FLAG_PASS_TO_USER;
+
+            if (!isScreenBright()) {
+                policyFlags |= POLICY_FLAG_BRIGHT_HERE;
+            }
+        }
+    } else {
+        policyFlags |= POLICY_FLAG_PASS_TO_USER;
+    }
+}
+
+bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
+        const KeyEvent* keyEvent, uint32_t policyFlags) {
+    // Policy:
+    // - Ignore untrusted events and pass them along.
+    // - Filter normal events and trusted injected events through the window manager policy to
+    //   handle the HOME key and the like.
+    if (policyFlags & POLICY_FLAG_TRUSTED) {
+        JNIEnv* env = jniEnv();
+
+        // Note: inputChannel may be null.
+        jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel);
+        jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
+                gCallbacksClassInfo.interceptKeyBeforeDispatching,
+                inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
+                keyEvent->getKeyCode(), keyEvent->getMetaState(),
+                keyEvent->getRepeatCount(), policyFlags);
+        bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
+
+        env->DeleteLocalRef(inputChannelObj);
+        return consumed && ! error;
+    } else {
+        return false;
+    }
 }
 
 void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
@@ -1365,9 +1336,6 @@
     GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz,
             "notifyANR", "(Ljava/lang/Object;Landroid/view/InputChannel;)J");
 
-    GET_METHOD_ID(gCallbacksClassInfo.virtualKeyDownFeedback, gCallbacksClassInfo.clazz,
-            "virtualKeyDownFeedback", "()V");
-
     GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz,
             "interceptKeyBeforeQueueing", "(JIZIZ)I");
 
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
index bd722d7..43e8467 100755
--- a/services/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -586,6 +586,23 @@
     return result;
 }
 
+static void android_location_GpsLocationProvider_update_network_state(JNIEnv* env, jobject obj,
+        jboolean connected, int type, jboolean roaming, jstring extraInfo)
+{
+    const AGpsRilInterface* interface = GetAGpsRilInterface(env, obj);
+    if (interface &&
+            (interface->size > ((char *)&interface->update_network_state - (char *)&interface)) &&
+            interface->update_network_state) {
+        if (extraInfo) {
+            const char *extraInfoStr = env->GetStringUTFChars(extraInfo, NULL);
+            interface->update_network_state(connected, type, roaming, extraInfoStr);
+            env->ReleaseStringUTFChars(extraInfo, extraInfoStr);
+        } else {
+            interface->update_network_state(connected, type, roaming, NULL);
+        }
+    }
+}
+
 static JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
@@ -611,6 +628,7 @@
     {"native_send_ni_response", "(II)V", (void*)android_location_GpsLocationProvider_send_ni_response},
     {"native_agps_ni_message", "([BI)V", (void *)android_location_GpsLocationProvider_agps_send_ni_message},
     {"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state},
+    {"native_update_network_state", "(ZIZLjava/lang/String;)V", (void*)android_location_GpsLocationProvider_update_network_state },
 };
 
 int register_android_server_location_GpsLocationProvider(JNIEnv* env)
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 758b408..069b85a 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -444,11 +444,11 @@
     }
     if (transform & HAL_TRANSFORM_FLIP_V) {
         swap(vLT, vLB);
-        swap(vRB, vRT);
+        swap(vRT, vRB);
     }
     if (transform & HAL_TRANSFORM_FLIP_H) {
-        swap(vLT, vRB);
-        swap(vLB, vRT);
+        swap(vLT, vRT);
+        swap(vLB, vRB);
     }
 
     TexCoords texCoords[4];
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index d668e88..aebe1b8 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -72,14 +72,6 @@
             nsecs_t now = systemTime();
             nsecs_t nextEventTime = -1;
 
-            // invalidate messages are always handled first
-            if (mInvalidate) {
-                mInvalidate = false;
-                mInvalidateMessage->when = now;
-                result = mInvalidateMessage;
-                break;
-            }
-
             LIST::iterator cur(mMessages.begin());
             if (cur != mMessages.end()) {
                 result = *cur;
@@ -91,17 +83,29 @@
                     mMessages.remove(cur);
                     break;
                 }
-                if (timeout>=0 && timeoutTime < now) {
-                    // we timed-out, return a NULL message
-                    result = 0;
-                    break;
-                }
                 nextEventTime = result->when;
                 result = 0;
             }
 
-            if (timeout >= 0 && nextEventTime > 0) {
-                if (nextEventTime > timeoutTime) {
+            // see if we have an invalidate message
+            if (mInvalidate) {
+                mInvalidate = false;
+                mInvalidateMessage->when = now;
+                result = mInvalidateMessage;
+                break;
+            }
+
+            if (timeout >= 0) {
+                if (timeoutTime < now) {
+                    // we timed-out, return a NULL message
+                    result = 0;
+                    break;
+                }
+                if (nextEventTime > 0) {
+                    if (nextEventTime > timeoutTime) {
+                        nextEventTime = timeoutTime;
+                    }
+                } else {
                     nextEventTime = timeoutTime;
                 }
             }
diff --git a/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
index dc4b27b..d546a08 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
@@ -56,8 +56,8 @@
     private DisconnectCause mCause = DisconnectCause.NOT_DISCONNECTED;
     private PostDialState postDialState = PostDialState.NOT_STARTED;
 
-    SipConnectionBase(String calleeSipUri) {
-        dialString = calleeSipUri;
+    SipConnectionBase(String dialString) {
+        this.dialString = dialString;
 
         postDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
 
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 67f13bd..1968552 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -387,7 +387,8 @@
             try {
                 SipProfile callee =
                         new SipProfile.Builder(calleeSipUri).build();
-                SipConnection c = new SipConnection(this, callee);
+                SipConnection c = new SipConnection(this, callee,
+                        originalNumber);
                 connections.add(c);
                 c.dial();
                 setState(Call.State.DIALING);
@@ -578,6 +579,7 @@
         private SipAudioCall mSipAudioCall;
         private Call.State mState = Call.State.IDLE;
         private SipProfile mPeer;
+        private String mOriginalNumber; // may be a PSTN number
         private boolean mIncoming = false;
 
         private SipAudioCallAdapter mAdapter = new SipAudioCallAdapter() {
@@ -659,10 +661,16 @@
             }
         };
 
-        public SipConnection(SipCall owner, SipProfile callee) {
-            super(getUriString(callee));
+        public SipConnection(SipCall owner, SipProfile callee,
+                String originalNumber) {
+            super(originalNumber);
             mOwner = owner;
             mPeer = callee;
+            mOriginalNumber = originalNumber;
+        }
+
+        public SipConnection(SipCall owner, SipProfile callee) {
+            this(owner, callee, getUriString(callee));
         }
 
         void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) {
@@ -735,7 +743,10 @@
 
         @Override
         public String getAddress() {
-            return getUriString(mPeer);
+            // Phone app uses this to query caller ID. Return the original dial
+            // number (which may be a PSTN number) instead of the peer's SIP
+            // URI.
+            return mOriginalNumber;
         }
 
         @Override
diff --git a/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java b/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java
index 9b6a850..99f0abe 100644
--- a/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java
+++ b/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java
@@ -134,17 +134,15 @@
         return curstate == state;
     }
 
-
-
     /**
-     * Set an MT call
+     * Start an incoming call for the given phone number
      *
-     * @param phoneNumber for the number shown
+     * @param phoneNumber is the number to show as incoming call
+     * @return true if the incoming call is started successfully, false if it fails.
      */
-    public boolean setMTCall(String phoneNumber) {
+    public boolean startIncomingCall(String phoneNumber) {
         RilCtrlCmds.CtrlReqSetMTCall req = new RilCtrlCmds.CtrlReqSetMTCall();
 
-        // Check whether it is a valid number
         req.setPhoneNumber(phoneNumber);
         if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_MT_CALL, 0, 0, req)) {
             Log.v(TAG, "send CMD_SET_MT_CALL request failed");
@@ -153,4 +151,68 @@
         return true;
     }
 
+    /**
+     * Hang up a connection remotelly for the given call fail cause
+     *
+     * @param connectionID is the connection to be hung up
+     * @param failCause is the call fail cause defined in ril.h
+     * @return true if the hangup is successful, false if it fails
+     */
+    public boolean hangupRemote(int connectionId, int failCause) {
+        RilCtrlCmds.CtrlHangupConnRemote req = new RilCtrlCmds.CtrlHangupConnRemote();
+        req.setConnectionId(connectionId);
+        req.setCallFailCause(failCause);
+
+        if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_HANGUP_CONN_REMOTE, 0, 0, req)) {
+            Log.v(TAG, "send CTRL_CMD_HANGUP_CONN_REMOTE request failed");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Set call transition flag to the Mock Ril
+     *
+     * @param flag is a boolean value for the call transiton flag
+     *             true: call transition: dialing->alert, alert->active is controlled
+     *             false: call transition is automatically handled by Mock Ril
+     * @return true if the request is successful, false if it failed to set the flag
+     */
+    public boolean setCallTransitionFlag(boolean flag) {
+        RilCtrlCmds.CtrlSetCallTransitionFlag req = new RilCtrlCmds.CtrlSetCallTransitionFlag();
+
+        req.setFlag(flag);
+
+        if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_CALL_TRANSITION_FLAG, 0, 0, req)) {
+            Log.v(TAG, "send CTRL_CMD_SET_CALL_TRANSITION_FLAG request failed");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Set the dialing call to alert if the call transition flag is true
+     *
+     * @return true if the call transition is successful, false if it fails
+     */
+    public boolean setDialCallToAlert() {
+        if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_CALL_ALERT, 0, 0, null)) {
+            Log.v(TAG, "send CTRL_CMD_SET_CALL_ALERT request failed");
+            return false;
+        }
+        return true;
+   }
+
+   /**
+    * Set the alert call to active if the call transition flag is true
+    *
+    * @return true if the call transition is successful, false if it fails
+    */
+   public boolean setAlertCallToActive() {
+        if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_CALL_ACTIVE, 0, 0, null)) {
+            Log.v(TAG, "send CTRL_CMD_SET_CALL_ACTIVE request failed");
+            return false;
+        }
+        return true;
+   }
 }
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java
index f0d5b31..3149ee1 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java
@@ -55,7 +55,7 @@
     }
 
     /**
-     * Test protobuf serialization and deserialization
+     * Test Case 1: Test protobuf serialization and deserialization
      * @throws InvalidProtocolBufferMicroException
      */
     public void testProtobufSerDes() throws InvalidProtocolBufferMicroException {
@@ -77,7 +77,7 @@
     }
 
     /**
-     * Test echo command works using writeMsg & readMsg
+     * Test case 2: Test echo command works using writeMsg & readMsg
      */
     public void testEchoMsg() throws IOException {
         log("testEchoMsg E");
@@ -110,7 +110,7 @@
     }
 
     /**
-     * Test get as
+     * Test case 3: Test get as
      */
     public void testGetAs() {
         log("testGetAs E");
@@ -150,6 +150,9 @@
         log("testGetAs X");
     }
 
+    /**
+     * Test case 3: test get radio state
+     */
     public void testGetRadioState() throws IOException {
         log("testGetRadioState E");
 
@@ -175,6 +178,9 @@
         log("testGetRadioState X");
     }
 
+    /**
+     * Test case 5: test set radio state
+     */
     public void testSetRadioState() throws IOException {
         log("testSetRadioState E");
 
@@ -187,6 +193,9 @@
         Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_RADIO_STATE, 0, 0, cmdrs);
 
         Msg resp = Msg.recv(mMockRilChannel);
+        log("get response status :" + resp.getStatus());
+        log("get response for command: " + resp.getCmd());
+        log("get command token: " + resp.getToken());
 
         RilCtrlCmds.CtrlRspRadioState rsp = resp.getDataAs(RilCtrlCmds.CtrlRspRadioState.class);
 
@@ -194,4 +203,102 @@
         log("get response for testSetRadioState: " + state);
         assertTrue(RilCmds.RADIOSTATE_SIM_NOT_READY == state);
     }
+
+    /**
+     * Test case 6: test start incoming call and hangup it.
+     */
+    public void testStartIncomingCallAndHangup() throws IOException {
+        log("testStartIncomingCallAndHangup");
+        RilCtrlCmds.CtrlReqSetMTCall cmd = new RilCtrlCmds.CtrlReqSetMTCall();
+        String incomingCall = "6502889108";
+        // set the MT call
+        cmd.setPhoneNumber(incomingCall);
+        Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_MT_CALL, 0, 0, cmd);
+        // get response
+        Msg resp = Msg.recv(mMockRilChannel);
+        log("Get response status: " + resp.getStatus());
+        assertTrue("The ril is not in a proper state to set MT calls.",
+                   resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+
+        // allow the incoming call alerting for some time
+        try {
+            Thread.sleep(5000);
+        } catch (InterruptedException e) {}
+
+        // we are playing a trick to assume the current is 1
+        RilCtrlCmds.CtrlHangupConnRemote hangupCmd = new RilCtrlCmds.CtrlHangupConnRemote();
+        hangupCmd.setConnectionId(1);
+        hangupCmd.setCallFailCause(16);   // normal hangup
+        Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_HANGUP_CONN_REMOTE, 0, 0, hangupCmd);
+
+        // get response
+        resp = Msg.recv(mMockRilChannel);
+        log("Get response for hangup connection: " + resp.getStatus());
+        assertTrue("CTRL_CMD_HANGUP_CONN_REMOTE failed",
+                   resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+    }
+
+    /**
+     * Test case 7: test set call transition flag
+     */
+    public void testSetCallTransitionFlag() throws IOException {
+        log("testSetCallTransitionFlag");
+        // Set flag to true:
+        RilCtrlCmds.CtrlSetCallTransitionFlag cmd = new RilCtrlCmds.CtrlSetCallTransitionFlag();
+        cmd.setFlag(true);
+        Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_CALL_TRANSITION_FLAG, 0, 0, cmd);
+
+        Msg resp = Msg.recv(mMockRilChannel);
+        log("Get response status: " + resp.getStatus());
+        assertTrue("Set call transition flag failed",
+                   resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+
+        // add a dialing call
+        RilCtrlCmds.CtrlReqAddDialingCall cmdDialCall = new RilCtrlCmds.CtrlReqAddDialingCall();
+        String phoneNumber = "5102345678";
+        cmdDialCall.setPhoneNumber(phoneNumber);
+        Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_ADD_DIALING_CALL, 0, 0, cmdDialCall);
+        resp = Msg.recv(mMockRilChannel);
+        log("Get response status for adding a dialing call: " + resp.getStatus());
+        assertTrue("add dialing call failed",
+                   resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+        try {
+            Thread.sleep(5000);
+        } catch (InterruptedException e) {}
+
+        // send command to force call state change
+        Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_CALL_ALERT, 0, 0, null);
+        resp = Msg.recv(mMockRilChannel);
+        log("Get response status: " + resp.getStatus());
+        assertTrue("Set call alert failed",
+                   resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+
+        try {
+            Thread.sleep(2000);
+        } catch (InterruptedException e) {}
+
+        // send command to force call state change
+        Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_CALL_ACTIVE, 0, 0, null);
+        resp = Msg.recv(mMockRilChannel);
+        log("Get response status: " + resp.getStatus());
+        assertTrue("Set call active failed",
+                   resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+
+        // hangup the active all remotely
+        RilCtrlCmds.CtrlHangupConnRemote hangupCmd = new RilCtrlCmds.CtrlHangupConnRemote();
+        hangupCmd.setConnectionId(1);
+        hangupCmd.setCallFailCause(16);   // normal hangup
+        Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_HANGUP_CONN_REMOTE, 0, 0, hangupCmd);
+        resp = Msg.recv(mMockRilChannel);
+        log("Get response for hangup connection: " + resp.getStatus());
+        assertTrue("CTRL_CMD_HANGUP_CONN_REMOTE failed",
+                   resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+
+        // set the flag to false
+        cmd.setFlag(false);
+        Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_CALL_TRANSITION_FLAG, 0, 0, cmd);
+        resp = Msg.recv(mMockRilChannel);
+        assertTrue("Set call transition flag failed",
+                   resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK);
+    }
 }
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index ee554b5..6f426c9 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -868,6 +868,7 @@
                     case SipErrorCode.SERVER_UNREACHABLE:
                         if (DEBUG) Log.d(TAG, "   pause auto-registration");
                         stop();
+                        break;
                     default:
                         restartLater();
                 }