Merge "Collapse contentInsets and View margins in Toolbars" into lmp-preview-dev
diff --git a/api/current.txt b/api/current.txt
index 73decfb..235dca8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5029,6 +5029,11 @@
     method public boolean[] supportsCommands(java.lang.String[]);
   }
 
+  public static class VoiceInteractor.AbortVoiceRequest extends android.app.VoiceInteractor.Request {
+    ctor public VoiceInteractor.AbortVoiceRequest(java.lang.CharSequence, android.os.Bundle);
+    method public void onAbortResult(android.os.Bundle);
+  }
+
   public static class VoiceInteractor.CommandRequest extends android.app.VoiceInteractor.Request {
     ctor public VoiceInteractor.CommandRequest(java.lang.String, android.os.Bundle);
     method public void onCommandResult(android.os.Bundle);
@@ -6902,6 +6907,7 @@
     method public abstract java.io.File[] getExternalCacheDirs();
     method public abstract java.io.File getExternalFilesDir(java.lang.String);
     method public abstract java.io.File[] getExternalFilesDirs(java.lang.String);
+    method public abstract java.io.File[] getExternalMediaDirs();
     method public abstract java.io.File getFileStreamPath(java.lang.String);
     method public abstract java.io.File getFilesDir();
     method public abstract android.os.Looper getMainLooper();
@@ -7070,6 +7076,7 @@
     method public java.io.File[] getExternalCacheDirs();
     method public java.io.File getExternalFilesDir(java.lang.String);
     method public java.io.File[] getExternalFilesDirs(java.lang.String);
+    method public java.io.File[] getExternalMediaDirs();
     method public java.io.File getFileStreamPath(java.lang.String);
     method public java.io.File getFilesDir();
     method public android.os.Looper getMainLooper();
@@ -26193,16 +26200,17 @@
     method public android.view.LayoutInflater getLayoutInflater();
     method public android.app.Dialog getWindow();
     method public void hideWindow();
+    method public void onAbortVoice(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
     method public void onBackPressed();
     method public abstract void onCancel(android.service.voice.VoiceInteractionSession.Request);
     method public void onCloseSystemDialogs();
     method public abstract void onCommand(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
     method public void onComputeInsets(android.service.voice.VoiceInteractionSession.Insets);
-    method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
+    method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
     method public void onCreate(android.os.Bundle);
     method public android.view.View onCreateContentView();
     method public void onDestroy();
-    method public abstract boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
+    method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
     method public boolean onKeyDown(int, android.view.KeyEvent);
     method public boolean onKeyLongPress(int, android.view.KeyEvent);
     method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
@@ -26229,6 +26237,7 @@
   }
 
   public static class VoiceInteractionSession.Request {
+    method public void sendAbortVoiceResult(android.os.Bundle);
     method public void sendCancelResult();
     method public void sendCommandResult(boolean, android.os.Bundle);
     method public void sendConfirmResult(boolean, android.os.Bundle);
@@ -28268,6 +28277,7 @@
     method public java.io.File[] getExternalCacheDirs();
     method public java.io.File getExternalFilesDir(java.lang.String);
     method public java.io.File[] getExternalFilesDirs(java.lang.String);
+    method public java.io.File[] getExternalMediaDirs();
     method public java.io.File getFileStreamPath(java.lang.String);
     method public java.io.File getFilesDir();
     method public android.os.Looper getMainLooper();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ff8688d..8ffa6fe 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -249,6 +249,8 @@
     private File[] mExternalFilesDirs;
     @GuardedBy("mSync")
     private File[] mExternalCacheDirs;
+    @GuardedBy("mSync")
+    private File[] mExternalMediaDirs;
 
     private static final String[] EMPTY_FILE_LIST = {};
 
@@ -1032,6 +1034,18 @@
     }
 
     @Override
+    public File[] getExternalMediaDirs() {
+        synchronized (mSync) {
+            if (mExternalMediaDirs == null) {
+                mExternalMediaDirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
+            }
+
+            // Create dirs if needed
+            return ensureDirsExistOrFilter(mExternalMediaDirs);
+        }
+    }
+
+    @Override
     public File getFileStreamPath(String name) {
         return makeFilename(getFilesDir(), name);
     }
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index fe85ef4..f332c9d 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -33,7 +33,26 @@
 import java.util.ArrayList;
 
 /**
- * Interface for an {@link Activity} to interact with the user through voice.
+ * Interface for an {@link Activity} to interact with the user through voice.  Use
+ * {@link android.app.Activity#getVoiceInteractor() Activity.getVoiceInteractor}
+ * to retrieve the interface, if the activity is currently involved in a voice interaction.
+ *
+ * <p>The voice interactor revolves around submitting voice interaction requests to the
+ * back-end voice interaction service that is working with the user.  These requests are
+ * submitted with {@link #submitRequest}, providing a new instance of a
+ * {@link Request} subclass describing the type of operation to perform -- currently the
+ * possible requests are {@link ConfirmationRequest} and {@link CommandRequest}.
+ *
+ * <p>Once a request is submitted, the voice system will process it and evetually deliver
+ * the result to the request object.  The application can cancel a pending request at any
+ * time.
+ *
+ * <p>The VoiceInteractor is integrated with Activity's state saving mechanism, so that
+ * if an activity is being restarted with retained state, it will retain the current
+ * VoiceInteractor and any outstanding requests.  Because of this, you should always use
+ * {@link Request#getActivity() Request.getActivity} to get back to the activity of a
+ * request, rather than holding on to the actvitity instance yourself, either explicitly
+ * or implicitly through a non-static inner class.
  */
 public class VoiceInteractor {
     static final String TAG = "VoiceInteractor";
@@ -62,6 +81,16 @@
                         request.clear();
                     }
                     break;
+                case MSG_ABORT_VOICE_RESULT:
+                    request = pullRequest((IVoiceInteractorRequest)args.arg1, true);
+                    if (DEBUG) Log.d(TAG, "onAbortVoice: req="
+                            + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request
+                            + " result=" + args.arg1);
+                    if (request != null) {
+                        ((AbortVoiceRequest)request).onAbortResult((Bundle) args.arg2);
+                        request.clear();
+                    }
+                    break;
                 case MSG_COMMAND_RESULT:
                     request = pullRequest((IVoiceInteractorRequest)args.arg1, msg.arg1 != 0);
                     if (DEBUG) Log.d(TAG, "onCommandResult: req="
@@ -96,6 +125,12 @@
         }
 
         @Override
+        public void deliverAbortVoiceResult(IVoiceInteractorRequest request, Bundle result) {
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOO(
+                    MSG_ABORT_VOICE_RESULT, request, result));
+        }
+
+        @Override
         public void deliverCommandResult(IVoiceInteractorRequest request, boolean complete,
                 Bundle result) {
             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(
@@ -112,8 +147,9 @@
     final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
 
     static final int MSG_CONFIRMATION_RESULT = 1;
-    static final int MSG_COMMAND_RESULT = 2;
-    static final int MSG_CANCEL_RESULT = 3;
+    static final int MSG_ABORT_VOICE_RESULT = 2;
+    static final int MSG_COMMAND_RESULT = 3;
+    static final int MSG_CANCEL_RESULT = 4;
 
     public static abstract class Request {
         IVoiceInteractorRequest mRequestInterface;
@@ -188,9 +224,42 @@
 
         IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
                 IVoiceInteractorCallback callback) throws RemoteException {
-            return interactor.startConfirmation(packageName, callback, mPrompt.toString(), mExtras);
+            return interactor.startConfirmation(packageName, callback, mPrompt, mExtras);
         }
-   }
+    }
+
+    public static class AbortVoiceRequest extends Request {
+        final CharSequence mMessage;
+        final Bundle mExtras;
+
+        /**
+         * Reports that the current interaction can not be complete with voice, so the
+         * application will need to switch to a traditional input UI.  Applications should
+         * only use this when they need to completely bail out of the voice interaction
+         * and switch to a traditional UI.  When the resonsponse comes back, the voice
+         * system has handled the request and is ready to switch; at that point the application
+         * can start a new non-voice activity.  Be sure when starting the new activity
+         * to use {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK
+         * Intent.FLAG_ACTIVITY_NEW_TASK} to keep the new activity out of the current voice
+         * interaction task.
+         *
+         * @param message Optional message to tell user about not being able to complete
+         * the interaction with voice.
+         * @param extras Additional optional information.
+         */
+        public AbortVoiceRequest(CharSequence message, Bundle extras) {
+            mMessage = message;
+            mExtras = extras;
+        }
+
+        public void onAbortResult(Bundle result) {
+        }
+
+        IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
+                IVoiceInteractorCallback callback) throws RemoteException {
+            return interactor.startAbortVoice(packageName, callback, mMessage, mExtras);
+        }
+    }
 
     public static class CommandRequest extends Request {
         final String mCommand;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2ff85c6..d3a979c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -40,6 +40,7 @@
 import android.os.StatFs;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.MediaStore;
 import android.util.AttributeSet;
 import android.view.DisplayAdjustments;
 import android.view.Display;
@@ -929,6 +930,40 @@
     public abstract File[] getExternalCacheDirs();
 
     /**
+     * Returns absolute paths to application-specific directories on all
+     * external storage devices where the application can place media files.
+     * These files are scanned and made available to other apps through
+     * {@link MediaStore}.
+     * <p>
+     * This is like {@link #getExternalFilesDirs} in that these files will be
+     * deleted when the application is uninstalled, however there are some
+     * important differences:
+     * <ul>
+     * <li>External files are not always available: they will disappear if the
+     * user mounts the external storage on a computer or removes it.
+     * <li>There is no security enforced with these files.
+     * </ul>
+     * <p>
+     * External storage devices returned here are considered a permanent part of
+     * the device, including both emulated external storage and physical media
+     * slots, such as SD cards in a battery compartment. The returned paths do
+     * not include transient devices, such as USB flash drives.
+     * <p>
+     * An application may store data on any or all of the returned devices. For
+     * example, an app may choose to store large files on the device with the
+     * most available space, as measured by {@link StatFs}.
+     * <p>
+     * No permissions are required to read or write to the returned paths; they
+     * are always accessible to the calling app. Write access outside of these
+     * paths on secondary external storage devices is not available.
+     * <p>
+     * Returned paths may be {@code null} if a storage device is unavailable.
+     *
+     * @see Environment#getExternalStorageState(File)
+     */
+    public abstract File[] getExternalMediaDirs();
+
+    /**
      * Returns an array of strings naming the private files associated with
      * this Context's application package.
      *
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index c66355b..dbf9122 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -237,6 +237,11 @@
     }
 
     @Override
+    public File[] getExternalMediaDirs() {
+        return mBase.getExternalMediaDirs();
+    }
+
+    @Override
     public File getDir(String name, int mode) {
         return mBase.getDir(name, mode);
     }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e98a26b..e84b695 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -191,6 +191,10 @@
             return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_MEDIA, packageName);
         }
 
+        public File[] buildExternalStorageAppMediaDirsForVold(String packageName) {
+            return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_MEDIA, packageName);
+        }
+
         public File[] buildExternalStorageAppObbDirs(String packageName) {
             return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB, packageName);
         }
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 1e29f8e..2e9077a 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -47,9 +47,22 @@
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
 
+import java.lang.ref.WeakReference;
+
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
+/**
+ * An active voice interaction session, providing a facility for the implementation
+ * to interact with the user in the voice interaction layer.  This interface is no shown
+ * by default, but you can request that it be shown with {@link #showWindow()}, which
+ * will result in a later call to {@link #onCreateContentView()} in which the UI can be
+ * built
+ *
+ * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish}
+ * when done.  It can also initiate voice interactions with applications by calling
+ * {@link #startVoiceActivity}</p>.
+ */
 public abstract class VoiceInteractionSession implements KeyEvent.Callback {
     static final String TAG = "VoiceInteractionSession";
     static final boolean DEBUG = true;
@@ -80,11 +93,14 @@
     final Insets mTmpInsets = new Insets();
     final int[] mTmpLocation = new int[2];
 
+    final WeakReference<VoiceInteractionSession> mWeakRef
+            = new WeakReference<VoiceInteractionSession>(this);
+
     final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
         @Override
         public IVoiceInteractorRequest startConfirmation(String callingPackage,
-                IVoiceInteractorCallback callback, String prompt, Bundle extras) {
-            Request request = findRequest(callback, true);
+                IVoiceInteractorCallback callback, CharSequence prompt, Bundle extras) {
+            Request request = newRequest(callback);
             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_CONFIRMATION,
                     new Caller(callingPackage, Binder.getCallingUid()), request,
                     prompt, extras));
@@ -92,9 +108,19 @@
         }
 
         @Override
+        public IVoiceInteractorRequest startAbortVoice(String callingPackage,
+                IVoiceInteractorCallback callback, CharSequence message, Bundle extras) {
+            Request request = newRequest(callback);
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_ABORT_VOICE,
+                    new Caller(callingPackage, Binder.getCallingUid()), request,
+                    message, extras));
+            return request.mInterface;
+        }
+
+        @Override
         public IVoiceInteractorRequest startCommand(String callingPackage,
                 IVoiceInteractorCallback callback, String command, Bundle extras) {
-            Request request = findRequest(callback, true);
+            Request request = newRequest(callback);
             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMMAND,
                     new Caller(callingPackage, Binder.getCallingUid()), request,
                     command, extras));
@@ -143,29 +169,60 @@
         final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() {
             @Override
             public void cancel() throws RemoteException {
-                mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
+                VoiceInteractionSession session = mSession.get();
+                if (session != null) {
+                    session.mHandlerCaller.sendMessage(
+                            session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
+                }
             }
         };
         final IVoiceInteractorCallback mCallback;
-        final HandlerCaller mHandlerCaller;
-        Request(IVoiceInteractorCallback callback, HandlerCaller handlerCaller) {
+        final WeakReference<VoiceInteractionSession> mSession;
+
+        Request(IVoiceInteractorCallback callback, VoiceInteractionSession session) {
             mCallback = callback;
-            mHandlerCaller = handlerCaller;
+            mSession = session.mWeakRef;
+        }
+
+        void finishRequest() {
+            VoiceInteractionSession session = mSession.get();
+            if (session == null) {
+                throw new IllegalStateException("VoiceInteractionSession has been destroyed");
+            }
+            Request req = session.removeRequest(mInterface.asBinder());
+            if (req == null) {
+                throw new IllegalStateException("Request not active: " + this);
+            } else if (req != this) {
+                throw new IllegalStateException("Current active request " + req
+                        + " not same as calling request " + this);
+            }
         }
 
         public void sendConfirmResult(boolean confirmed, Bundle result) {
             try {
                 if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
                         + " confirmed=" + confirmed + " result=" + result);
+                finishRequest();
                 mCallback.deliverConfirmationResult(mInterface, confirmed, result);
             } catch (RemoteException e) {
             }
         }
 
+        public void sendAbortVoiceResult(Bundle result) {
+            try {
+                if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
+                        + " result=" + result);
+                finishRequest();
+                mCallback.deliverAbortVoiceResult(mInterface, result);
+            } catch (RemoteException e) {
+            }
+        }
+
         public void sendCommandResult(boolean complete, Bundle result) {
             try {
                 if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
                         + " result=" + result);
+                finishRequest();
                 mCallback.deliverCommandResult(mInterface, complete, result);
             } catch (RemoteException e) {
             }
@@ -174,6 +231,7 @@
         public void sendCancelResult() {
             try {
                 if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
+                finishRequest();
                 mCallback.deliverCancel(mInterface);
             } catch (RemoteException e) {
             }
@@ -191,9 +249,10 @@
     }
 
     static final int MSG_START_CONFIRMATION = 1;
-    static final int MSG_START_COMMAND = 2;
-    static final int MSG_SUPPORTS_COMMANDS = 3;
-    static final int MSG_CANCEL = 4;
+    static final int MSG_START_ABORT_VOICE = 2;
+    static final int MSG_START_COMMAND = 3;
+    static final int MSG_SUPPORTS_COMMANDS = 4;
+    static final int MSG_CANCEL = 5;
 
     static final int MSG_TASK_STARTED = 100;
     static final int MSG_TASK_FINISHED = 101;
@@ -209,9 +268,16 @@
                     args = (SomeArgs)msg.obj;
                     if (DEBUG) Log.d(TAG, "onConfirm: req=" + ((Request) args.arg2).mInterface
                             + " prompt=" + args.arg3 + " extras=" + args.arg4);
-                    onConfirm((Caller)args.arg1, (Request)args.arg2, (String)args.arg3,
+                    onConfirm((Caller)args.arg1, (Request)args.arg2, (CharSequence)args.arg3,
                             (Bundle)args.arg4);
                     break;
+                case MSG_START_ABORT_VOICE:
+                    args = (SomeArgs)msg.obj;
+                    if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + ((Request) args.arg2).mInterface
+                            + " message=" + args.arg3 + " extras=" + args.arg4);
+                    onAbortVoice((Caller) args.arg1, (Request) args.arg2, (CharSequence) args.arg3,
+                            (Bundle) args.arg4);
+                    break;
                 case MSG_START_COMMAND:
                     args = (SomeArgs)msg.obj;
                     if (DEBUG) Log.d(TAG, "onCommand: req=" + ((Request) args.arg2).mInterface
@@ -329,18 +395,20 @@
                 mCallbacks, true);
     }
 
-    Request findRequest(IVoiceInteractorCallback callback, boolean newRequest) {
+    Request newRequest(IVoiceInteractorCallback callback) {
         synchronized (this) {
-            Request req = mActiveRequests.get(callback.asBinder());
+            Request req = new Request(callback, this);
+            mActiveRequests.put(req.mInterface.asBinder(), req);
+            return req;
+        }
+    }
+
+    Request removeRequest(IBinder reqInterface) {
+        synchronized (this) {
+            Request req = mActiveRequests.get(reqInterface);
             if (req != null) {
-                if (newRequest) {
-                    throw new IllegalArgumentException("Given request callback " + callback
-                            + " is already active");
-                }
-                return req;
+                mActiveRequests.remove(req);
             }
-            req = new Request(callback, mHandlerCaller);
-            mActiveRequests.put(callback.asBinder(), req);
             return req;
         }
     }
@@ -425,6 +493,27 @@
         mTheme = theme;
     }
 
+    /**
+     * Ask that a new activity be started for voice interaction.  This will create a
+     * new dedicated task in the activity manager for this voice interaction session;
+     * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
+     * will be set for you to make it a new task.
+     *
+     * <p>The newly started activity will be displayed to the user in a special way, as
+     * a layer under the voice interaction UI.</p>
+     *
+     * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor}
+     * through which it can perform voice interactions through your session.  These requests
+     * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands},
+     * {@link #onConfirm}, {@link #onCommand}, and {@link #onCancel}.
+     *
+     * <p>You will receive a call to {@link #onTaskStarted} when the task starts up
+     * and {@link #onTaskFinished} when the last activity has finished.
+     *
+     * @param intent The Intent to start this voice interaction.  The given Intent will
+     * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since
+     * this is part of a voice interaction.
+     */
     public void startVoiceActivity(Intent intent) {
         if (mToken == null) {
             throw new IllegalStateException("Can't call before onCreate()");
@@ -439,14 +528,23 @@
         }
     }
 
+    /**
+     * Convenience for inflating views.
+     */
     public LayoutInflater getLayoutInflater() {
         return mInflater;
     }
 
+    /**
+     * Retrieve the window being used to show the session's UI.
+     */
     public Dialog getWindow() {
         return mWindow;
     }
 
+    /**
+     * Finish the session.
+     */
     public void finish() {
         if (mToken == null) {
             throw new IllegalStateException("Can't call before onCreate()");
@@ -458,6 +556,12 @@
         }
     }
 
+    /**
+     * Initiatize a new session.
+     *
+     * @param args The arguments that were supplied to
+     * {@link VoiceInteractionService#startSession VoiceInteractionService.startSession}.
+     */
     public void onCreate(Bundle args) {
         mTheme = mTheme != 0 ? mTheme
                 : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
@@ -472,9 +576,15 @@
         mWindow.setToken(mToken);
     }
 
+    /**
+     * Last callback to the session as it is being finished.
+     */
     public void onDestroy() {
     }
 
+    /**
+     * Hook in which to create the session's UI.
+     */
     public View onCreateContentView() {
         return null;
     }
@@ -507,6 +617,11 @@
         finish();
     }
 
+    /**
+     * Sessions automatically watch for requests that all system UI be closed (such as when
+     * the user presses HOME), which will appear here.  The default implementation always
+     * calls {@link #finish}.
+     */
     public void onCloseSystemDialogs() {
         finish();
     }
@@ -530,15 +645,98 @@
         outInsets.touchableRegion.setEmpty();
     }
 
+    /**
+     * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)}
+     * has actually started.
+     *
+     * @param intent The original {@link Intent} supplied to
+     * {@link #startVoiceActivity(android.content.Intent)}.
+     * @param taskId Unique ID of the now running task.
+     */
     public void onTaskStarted(Intent intent, int taskId) {
     }
 
+    /**
+     * Called when the last activity of a task initiated by
+     * {@link #startVoiceActivity(android.content.Intent)} has finished.  The default
+     * implementation calls {@link #finish()} on the assumption that this represents
+     * the completion of a voice action.  You can override the implementation if you would
+     * like a different behavior.
+     *
+     * @param intent The original {@link Intent} supplied to
+     * {@link #startVoiceActivity(android.content.Intent)}.
+     * @param taskId Unique ID of the finished task.
+     */
     public void onTaskFinished(Intent intent, int taskId) {
         finish();
     }
 
-    public abstract boolean[] onGetSupportedCommands(Caller caller, String[] commands);
-    public abstract void onConfirm(Caller caller, Request request, String prompt, Bundle extras);
+    /**
+     * Request to query for what extended commands the session supports.
+     *
+     * @param caller Who is making the request.
+     * @param commands An array of commands that are being queried.
+     * @return Return an array of booleans indicating which of each entry in the
+     * command array is supported.  A true entry in the array indicates the command
+     * is supported; false indicates it is not.  The default implementation returns
+     * an array of all false entries.
+     */
+    public boolean[] onGetSupportedCommands(Caller caller, String[] commands) {
+        return new boolean[commands.length];
+    }
+
+    /**
+     * Request to confirm with the user before proceeding with an unrecoverable operation,
+     * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest
+     * VoiceInteractor.ConfirmationRequest}.
+     *
+     * @param caller Who is making the request.
+     * @param request The active request.
+     * @param prompt The prompt informing the user of what will happen, as per
+     * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}.
+     * @param extras Any additional information, as per
+     * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}.
+     */
+    public abstract void onConfirm(Caller caller, Request request, CharSequence prompt,
+            Bundle extras);
+
+    /**
+     * Request to abort the voice interaction session because the voice activity can not
+     * complete its interaction using voice.  Corresponds to
+     * {@link android.app.VoiceInteractor.AbortVoiceRequest
+     * VoiceInteractor.AbortVoiceRequest}.  The default implementation just sends an empty
+     * confirmation back to allow the activity to exit.
+     *
+     * @param caller Who is making the request.
+     * @param request The active request.
+     * @param message The message informing the user of the problem, as per
+     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
+     * @param extras Any additional information, as per
+     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
+     */
+    public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) {
+        request.sendAbortVoiceResult(null);
+    }
+
+    /**
+     * Process an arbitrary extended command from the caller,
+     * corresponding to a {@link android.app.VoiceInteractor.CommandRequest
+     * VoiceInteractor.CommandRequest}.
+     *
+     * @param caller Who is making the request.
+     * @param request The active request.
+     * @param command The command that is being executed, as per
+     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
+     * @param extras Any additional information, as per
+     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
+     */
     public abstract void onCommand(Caller caller, Request request, String command, Bundle extras);
+
+    /**
+     * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
+     * that was previously delivered to {@link #onConfirm} or {@link #onCommand}.
+     *
+     * @param request The request that is being canceled.
+     */
     public abstract void onCancel(Request request);
 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index d45d686..2b4677c 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1199,6 +1199,9 @@
 
     /**
      * Notifies the keyguard to start fading out.
+     *
+     * @param startTime the start time of the animation in uptime milliseconds
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds
      */
-    public void startKeyguardExitAnimation(long fadeoutDuration);
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
 }
diff --git a/core/java/com/android/internal/app/IVoiceInteractor.aidl b/core/java/com/android/internal/app/IVoiceInteractor.aidl
index 737906a..2900595 100644
--- a/core/java/com/android/internal/app/IVoiceInteractor.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractor.aidl
@@ -26,7 +26,9 @@
  */
 interface IVoiceInteractor {
     IVoiceInteractorRequest startConfirmation(String callingPackage,
-            IVoiceInteractorCallback callback, String prompt, in Bundle extras);
+            IVoiceInteractorCallback callback, CharSequence prompt, in Bundle extras);
+    IVoiceInteractorRequest startAbortVoice(String callingPackage,
+            IVoiceInteractorCallback callback, CharSequence message, in Bundle extras);
     IVoiceInteractorRequest startCommand(String callingPackage,
             IVoiceInteractorCallback callback, String command, in Bundle extras);
     boolean[] supportsCommands(String callingPackage, in String[] commands);
diff --git a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
index c6f93e1..8dbf9d4 100644
--- a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
@@ -26,6 +26,7 @@
 oneway interface IVoiceInteractorCallback {
     void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed,
             in Bundle result);
+    void deliverAbortVoiceResult(IVoiceInteractorRequest request, in Bundle result);
     void deliverCommandResult(IVoiceInteractorRequest request, boolean complete, in Bundle result);
     void deliverCancel(IVoiceInteractorRequest request);
 }
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index f22800c..a5421f5 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -60,6 +60,9 @@
     /**
      * Notifies that the activity behind has now been drawn and it's safe to remove the wallpaper
      * and keyguard flag.
+     *
+     * @param startTime the start time of the animation in uptime milliseconds
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds
      */
-    oneway void startKeyguardExitAnimation(long fadeoutDuration);
+    oneway void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
 }
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index fedb1b2..a2b1ed9 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -23,6 +23,11 @@
 #define ENCODING_PCM_16BIT 2
 #define ENCODING_PCM_8BIT  3
 #define ENCODING_PCM_FLOAT 4
+#define ENCODING_INVALID 0
+#define ENCODING_DEFAULT 1
+
+#define CHANNEL_INVALID 0
+#define CHANNEL_OUT_DEFAULT 1
 
 static inline audio_format_t audioFormatToNative(int audioFormat)
 {
@@ -33,9 +38,58 @@
         return AUDIO_FORMAT_PCM_8_BIT;
     case ENCODING_PCM_FLOAT:
         return AUDIO_FORMAT_PCM_FLOAT;
+    case ENCODING_DEFAULT:
+        return AUDIO_FORMAT_DEFAULT;
     default:
         return AUDIO_FORMAT_INVALID;
     }
 }
 
+static inline int audioFormatFromNative(audio_format_t nativeFormat)
+{
+    switch (nativeFormat) {
+    case AUDIO_FORMAT_PCM_16_BIT:
+        return ENCODING_PCM_16BIT;
+    case AUDIO_FORMAT_PCM_8_BIT:
+        return ENCODING_PCM_8BIT;
+    case AUDIO_FORMAT_PCM_FLOAT:
+        return ENCODING_PCM_FLOAT;
+    case AUDIO_FORMAT_DEFAULT:
+        return ENCODING_DEFAULT;
+    default:
+        return ENCODING_INVALID;
+    }
+}
+
+static inline audio_channel_mask_t outChannelMaskToNative(int channelMask)
+{
+    switch (channelMask) {
+    case CHANNEL_OUT_DEFAULT:
+    case CHANNEL_INVALID:
+        return AUDIO_CHANNEL_NONE;
+    default:
+        return (audio_channel_mask_t)(channelMask>>2);
+    }
+}
+
+static inline int outChannelMaskFromNative(audio_channel_mask_t nativeMask)
+{
+    switch (nativeMask) {
+    case AUDIO_CHANNEL_NONE:
+        return CHANNEL_OUT_DEFAULT;
+    default:
+        return (int)nativeMask<<2;
+    }
+}
+
+static inline audio_channel_mask_t inChannelMaskToNative(int channelMask)
+{
+    return (audio_channel_mask_t)channelMask;
+}
+
+static inline int inChannelMaskFromNative(audio_channel_mask_t nativeMask)
+{
+    return (int)nativeMask;
+}
+
 #endif // ANDROID_MEDIA_AUDIOFORMAT_H
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index a9a62f8..0f7e140 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -15,7 +15,9 @@
 ** limitations under the License.
 */
 
-#define LOG_TAG "AudioSystem"
+//#define LOG_NDEBUG 0
+
+#define LOG_TAG "AudioSystem-JNI"
 #include <utils/Log.h>
 
 #include <jni.h>
@@ -26,6 +28,8 @@
 
 #include <system/audio.h>
 #include <system/audio_policy.h>
+#include "android_media_AudioFormat.h"
+#include "android_media_AudioErrors.h"
 
 // ----------------------------------------------------------------------------
 
@@ -33,12 +37,160 @@
 
 static const char* const kClassPathName = "android/media/AudioSystem";
 
+static jclass gArrayListClass;
+static struct {
+    jmethodID    add;
+} gArrayListMethods;
+
+static jclass gAudioHandleClass;
+static jmethodID gAudioHandleCstor;
+static struct {
+    jfieldID    mId;
+} gAudioHandleFields;
+
+static jclass gAudioPortClass;
+static jmethodID gAudioPortCstor;
+static struct {
+    jfieldID    mHandle;
+    jfieldID    mRole;
+    jfieldID    mGains;
+    jfieldID    mActiveConfig;
+    // other fields unused by JNI
+} gAudioPortFields;
+
+static jclass gAudioPortConfigClass;
+static jmethodID gAudioPortConfigCstor;
+static struct {
+    jfieldID    mPort;
+    jfieldID    mSamplingRate;
+    jfieldID    mChannelMask;
+    jfieldID    mFormat;
+    jfieldID    mGain;
+    jfieldID    mConfigMask;
+} gAudioPortConfigFields;
+
+static jclass gAudioDevicePortClass;
+static jmethodID gAudioDevicePortCstor;
+
+static jclass gAudioDevicePortConfigClass;
+static jmethodID gAudioDevicePortConfigCstor;
+
+static jclass gAudioMixPortClass;
+static jmethodID gAudioMixPortCstor;
+
+static jclass gAudioMixPortConfigClass;
+static jmethodID gAudioMixPortConfigCstor;
+
+static jclass gAudioGainClass;
+static jmethodID gAudioGainCstor;
+
+static jclass gAudioGainConfigClass;
+static jmethodID gAudioGainConfigCstor;
+static struct {
+    jfieldID mIndex;
+    jfieldID mMode;
+    jfieldID mChannelMask;
+    jfieldID mValues;
+    jfieldID mRampDurationMs;
+    // other fields unused by JNI
+} gAudioGainConfigFields;
+
+static jclass gAudioPatchClass;
+static jmethodID gAudioPatchCstor;
+static struct {
+    jfieldID    mHandle;
+    // other fields unused by JNI
+} gAudioPatchFields;
+
+static const char* const kEventHandlerClassPathName =
+        "android/media/AudioPortEventHandler";
+static jmethodID gPostEventFromNative;
+
 enum AudioError {
     kAudioStatusOk = 0,
     kAudioStatusError = 1,
     kAudioStatusMediaServerDied = 100
 };
 
+enum  {
+    AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1,
+    AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2,
+    AUDIOPORT_EVENT_SERVICE_DIED = 3,
+};
+
+#define MAX_PORT_GENERATION_SYNC_ATTEMPTS 5
+
+// ----------------------------------------------------------------------------
+// ref-counted object for callbacks
+class JNIAudioPortCallback: public AudioSystem::AudioPortCallback
+{
+public:
+    JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
+    ~JNIAudioPortCallback();
+
+    virtual void onAudioPortListUpdate();
+    virtual void onAudioPatchListUpdate();
+    virtual void onServiceDied();
+
+private:
+    void sendEvent(int event);
+
+    jclass      mClass;     // Reference to AudioPortEventHandlerDelegate class
+    jobject     mObject;    // Weak ref to AudioPortEventHandlerDelegate Java object to call on
+};
+
+JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz)
+{
+
+    // Hold onto the SoundTriggerModule class for use in calling the static method
+    // that posts events to the application thread.
+    jclass clazz = env->GetObjectClass(thiz);
+    if (clazz == NULL) {
+        ALOGE("Can't find class %s", kEventHandlerClassPathName);
+        return;
+    }
+    mClass = (jclass)env->NewGlobalRef(clazz);
+
+    // We use a weak reference so the SoundTriggerModule object can be garbage collected.
+    // The reference is only used as a proxy for callbacks.
+    mObject  = env->NewGlobalRef(weak_thiz);
+}
+
+JNIAudioPortCallback::~JNIAudioPortCallback()
+{
+    // remove global references
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    env->DeleteGlobalRef(mObject);
+    env->DeleteGlobalRef(mClass);
+}
+
+void JNIAudioPortCallback::sendEvent(int event)
+{
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
+                              event, 0, 0, NULL);
+    if (env->ExceptionCheck()) {
+        ALOGW("An exception occurred while notifying an event.");
+        env->ExceptionClear();
+    }
+}
+
+void JNIAudioPortCallback::onAudioPortListUpdate()
+{
+    sendEvent(AUDIOPORT_EVENT_PORT_LIST_UPDATED);
+}
+
+void JNIAudioPortCallback::onAudioPatchListUpdate()
+{
+    sendEvent(AUDIOPORT_EVENT_PATCH_LIST_UPDATED);
+}
+
+void JNIAudioPortCallback::onServiceDied()
+{
+    sendEvent(AUDIOPORT_EVENT_SERVICE_DIED);
+}
+
 static int check_AudioSystem_Command(status_t status)
 {
     switch (status) {
@@ -281,6 +433,854 @@
     return (jint) check_AudioSystem_Command(AudioSystem::checkAudioFlinger());
 }
 
+
+static bool useInChannelMask(audio_port_type_t type, audio_port_role_t role)
+{
+    return ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) ||
+                ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK));
+}
+
+static void convertAudioGainConfigToNative(JNIEnv *env,
+                                               struct audio_gain_config *nAudioGainConfig,
+                                               const jobject jAudioGainConfig,
+                                               bool useInMask)
+{
+    nAudioGainConfig->index = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mIndex);
+    nAudioGainConfig->mode = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode);
+    ALOGV("convertAudioGainConfigToNative got gain index %d", nAudioGainConfig->index);
+    jint jMask = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mChannelMask);
+    audio_channel_mask_t nMask;
+    if (useInMask) {
+        nMask = inChannelMaskToNative(jMask);
+        ALOGV("convertAudioGainConfigToNative IN mask java %x native %x", jMask, nMask);
+    } else {
+        nMask = outChannelMaskToNative(jMask);
+        ALOGV("convertAudioGainConfigToNative OUT mask java %x native %x", jMask, nMask);
+    }
+    nAudioGainConfig->channel_mask = nMask;
+    nAudioGainConfig->ramp_duration_ms = env->GetIntField(jAudioGainConfig,
+                                                       gAudioGainConfigFields.mRampDurationMs);
+    jintArray jValues = (jintArray)env->GetObjectField(jAudioGainConfig,
+                                                       gAudioGainConfigFields.mValues);
+    int *nValues = env->GetIntArrayElements(jValues, NULL);
+    size_t size = env->GetArrayLength(jValues);
+    memcpy(nAudioGainConfig->values, nValues, size * sizeof(int));
+    env->DeleteLocalRef(jValues);
+}
+
+
+static jint convertAudioPortConfigToNative(JNIEnv *env,
+                                               struct audio_port_config *nAudioPortConfig,
+                                               const jobject jAudioPortConfig)
+{
+    jobject jAudioPort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort);
+    jobject jHandle = env->GetObjectField(jAudioPort, gAudioPortFields.mHandle);
+    nAudioPortConfig->id = env->GetIntField(jHandle, gAudioHandleFields.mId);
+    nAudioPortConfig->role = (audio_port_role_t)env->GetIntField(jAudioPort,
+                                                                 gAudioPortFields.mRole);
+    if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) {
+        nAudioPortConfig->type = AUDIO_PORT_TYPE_DEVICE;
+    } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) {
+        nAudioPortConfig->type = AUDIO_PORT_TYPE_MIX;
+    } else {
+        env->DeleteLocalRef(jAudioPort);
+        env->DeleteLocalRef(jHandle);
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+    ALOGV("convertAudioPortConfigToNative handle %d role %d type %d",
+          nAudioPortConfig->id, nAudioPortConfig->role, nAudioPortConfig->type);
+
+    nAudioPortConfig->sample_rate = env->GetIntField(jAudioPortConfig,
+                                                     gAudioPortConfigFields.mSamplingRate);
+
+    bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role);
+    audio_channel_mask_t nMask;
+    jint jMask = env->GetIntField(jAudioPortConfig,
+                                   gAudioPortConfigFields.mChannelMask);
+    if (useInMask) {
+        nMask = inChannelMaskToNative(jMask);
+        ALOGV("convertAudioPortConfigToNative IN mask java %x native %x", jMask, nMask);
+    } else {
+        nMask = outChannelMaskToNative(jMask);
+        ALOGV("convertAudioPortConfigToNative OUT mask java %x native %x", jMask, nMask);
+    }
+    nAudioPortConfig->channel_mask = nMask;
+
+    jint jFormat = env->GetIntField(jAudioPortConfig, gAudioPortConfigFields.mFormat);
+    audio_format_t nFormat = audioFormatToNative(jFormat);
+    ALOGV("convertAudioPortConfigToNative format %d native %d", jFormat, nFormat);
+    nAudioPortConfig->format = nFormat;
+    jobject jGain = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mGain);
+    if (jGain != NULL) {
+        convertAudioGainConfigToNative(env, &nAudioPortConfig->gain, jGain, useInMask);
+        env->DeleteLocalRef(jGain);
+    } else {
+        ALOGV("convertAudioPortConfigToNative no gain");
+        nAudioPortConfig->gain.index = -1;
+    }
+    nAudioPortConfig->config_mask = env->GetIntField(jAudioPortConfig,
+                                                     gAudioPortConfigFields.mConfigMask);
+
+    env->DeleteLocalRef(jAudioPort);
+    env->DeleteLocalRef(jHandle);
+    return (jint)AUDIO_JAVA_SUCCESS;
+}
+
+static jint convertAudioPortConfigFromNative(JNIEnv *env,
+                                                 jobject jAudioPort,
+                                                 jobject *jAudioPortConfig,
+                                                 const struct audio_port_config *nAudioPortConfig)
+{
+    jint jStatus = AUDIO_JAVA_SUCCESS;
+    jobject jAudioGainConfig = NULL;
+    jobject jAudioGain = NULL;
+    jintArray jGainValues;
+    bool audioportCreated = false;
+
+    ALOGV("convertAudioPortConfigFromNative jAudioPort %p", jAudioPort);
+
+    if (jAudioPort == NULL) {
+        jobject jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
+                                                 nAudioPortConfig->id);
+
+        ALOGV("convertAudioPortConfigFromNative handle %d is a %s", nAudioPortConfig->id,
+              nAudioPortConfig->type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix");
+
+        if (jHandle == NULL) {
+            return (jint)AUDIO_JAVA_ERROR;
+        }
+        // create dummy port and port config objects with just the correct handle
+        // and configuration data. The actual AudioPortConfig objects will be
+        // constructed by java code with correct class type (device, mix etc...)
+        // and reference to AudioPort instance in this client
+        jAudioPort = env->NewObject(gAudioPortClass, gAudioPortCstor,
+                                           jHandle,
+                                           0,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL);
+        env->DeleteLocalRef(jHandle);
+        if (jAudioPort == NULL) {
+            return (jint)AUDIO_JAVA_ERROR;
+        }
+        ALOGV("convertAudioPortConfigFromNative jAudioPort created for handle %d",
+              nAudioPortConfig->id);
+
+        audioportCreated = true;
+    }
+
+    bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role);
+
+    audio_channel_mask_t nMask;
+    jint jMask;
+
+    int gainIndex = nAudioPortConfig->gain.index;
+    if (gainIndex >= 0) {
+        ALOGV("convertAudioPortConfigFromNative gain found with index %d mode %x",
+              gainIndex, nAudioPortConfig->gain.mode);
+        if (audioportCreated) {
+            ALOGV("convertAudioPortConfigFromNative creating gain");
+            jAudioGain = env->NewObject(gAudioGainClass, gAudioGainCstor,
+                                               gainIndex,
+                                               0,
+                                               0,
+                                               0,
+                                               0,
+                                               0,
+                                               0,
+                                               0,
+                                               0);
+            if (jAudioGain == NULL) {
+                ALOGV("convertAudioPortConfigFromNative creating gain FAILED");
+                jStatus = (jint)AUDIO_JAVA_ERROR;
+                goto exit;
+            }
+        } else {
+            ALOGV("convertAudioPortConfigFromNative reading gain from port");
+            jobjectArray jGains = (jobjectArray)env->GetObjectField(jAudioPort,
+                                                                      gAudioPortFields.mGains);
+            if (jGains == NULL) {
+                ALOGV("convertAudioPortConfigFromNative could not get gains from port");
+                jStatus = (jint)AUDIO_JAVA_ERROR;
+                goto exit;
+            }
+            jAudioGain = env->GetObjectArrayElement(jGains, gainIndex);
+            env->DeleteLocalRef(jGains);
+            if (jAudioGain == NULL) {
+                ALOGV("convertAudioPortConfigFromNative could not get gain at index %d", gainIndex);
+                jStatus = (jint)AUDIO_JAVA_ERROR;
+                goto exit;
+            }
+        }
+        //TODO: replace popcount by audio utils function mask to count
+        int numValues = popcount(nAudioPortConfig->gain.channel_mask);
+        jGainValues = env->NewIntArray(numValues);
+        if (jGainValues == NULL) {
+            ALOGV("convertAudioPortConfigFromNative could not create gain values %d", numValues);
+            jStatus = (jint)AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+        env->SetIntArrayRegion(jGainValues, 0, numValues,
+                               nAudioPortConfig->gain.values);
+
+        nMask = nAudioPortConfig->gain.channel_mask;
+        if (useInMask) {
+            jMask = inChannelMaskFromNative(nMask);
+            ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
+        } else {
+            jMask = outChannelMaskFromNative(nMask);
+            ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
+        }
+
+        jAudioGainConfig = env->NewObject(gAudioGainConfigClass,
+                                        gAudioGainConfigCstor,
+                                        gainIndex,
+                                        jAudioGain,
+                                        nAudioPortConfig->gain.mode,
+                                        jMask,
+                                        jGainValues,
+                                        nAudioPortConfig->gain.ramp_duration_ms);
+        env->DeleteLocalRef(jGainValues);
+        if (jAudioGainConfig == NULL) {
+            ALOGV("convertAudioPortConfigFromNative could not create gain config");
+            jStatus = (jint)AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+    }
+    jclass clazz;
+    jmethodID methodID;
+    if (audioportCreated) {
+        clazz = gAudioPortConfigClass;
+        methodID = gAudioPortConfigCstor;
+        ALOGV("convertAudioPortConfigFromNative building a generic port config");
+    } else {
+        if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) {
+            clazz = gAudioDevicePortConfigClass;
+            methodID = gAudioDevicePortConfigCstor;
+            ALOGV("convertAudioPortConfigFromNative building a device config");
+        } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) {
+            clazz = gAudioMixPortConfigClass;
+            methodID = gAudioMixPortConfigCstor;
+            ALOGV("convertAudioPortConfigFromNative building a mix config");
+        } else {
+            jStatus = (jint)AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+    }
+    nMask = nAudioPortConfig->channel_mask;
+    if (useInMask) {
+        jMask = inChannelMaskFromNative(nMask);
+        ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
+    } else {
+        jMask = outChannelMaskFromNative(nMask);
+        ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
+    }
+
+    *jAudioPortConfig = env->NewObject(clazz, methodID,
+                                       jAudioPort,
+                                       nAudioPortConfig->sample_rate,
+                                       jMask,
+                                       audioFormatFromNative(nAudioPortConfig->format),
+                                       jAudioGainConfig);
+    if (*jAudioPortConfig == NULL) {
+        ALOGV("convertAudioPortConfigFromNative could not create new port config");
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+    } else {
+        ALOGV("convertAudioPortConfigFromNative OK");
+    }
+
+exit:
+    if (audioportCreated) {
+        env->DeleteLocalRef(jAudioPort);
+        if (jAudioGain != NULL) {
+            env->DeleteLocalRef(jAudioGain);
+        }
+    }
+    if (jAudioGainConfig != NULL) {
+        env->DeleteLocalRef(jAudioGainConfig);
+    }
+    return jStatus;
+}
+
+static jint convertAudioPortFromNative(JNIEnv *env,
+                                           jobject *jAudioPort, const struct audio_port *nAudioPort)
+{
+    jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
+    jintArray jSamplingRates = NULL;
+    jintArray jChannelMasks = NULL;
+    jintArray jFormats = NULL;
+    jobjectArray jGains = NULL;
+    jobject jHandle = NULL;
+    bool useInMask;
+
+    ALOGV("convertAudioPortFromNative id %d role %d type %d",
+                                  nAudioPort->id, nAudioPort->role, nAudioPort->type);
+
+    jSamplingRates = env->NewIntArray(nAudioPort->num_sample_rates);
+    if (jSamplingRates == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    if (nAudioPort->num_sample_rates) {
+        env->SetIntArrayRegion(jSamplingRates, 0, nAudioPort->num_sample_rates,
+                               (jint *)nAudioPort->sample_rates);
+    }
+
+    jChannelMasks = env->NewIntArray(nAudioPort->num_channel_masks);
+    if (jChannelMasks == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    useInMask = useInChannelMask(nAudioPort->type, nAudioPort->role);
+
+    jint jMask;
+    for (size_t j = 0; j < nAudioPort->num_channel_masks; j++) {
+        if (useInMask) {
+            jMask = inChannelMaskFromNative(nAudioPort->channel_masks[j]);
+        } else {
+            jMask = outChannelMaskFromNative(nAudioPort->channel_masks[j]);
+        }
+        env->SetIntArrayRegion(jChannelMasks, j, 1, &jMask);
+    }
+
+    jFormats = env->NewIntArray(nAudioPort->num_formats);
+    if (jFormats == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    for (size_t j = 0; j < nAudioPort->num_formats; j++) {
+        jint jFormat = audioFormatFromNative(nAudioPort->formats[j]);
+        env->SetIntArrayRegion(jFormats, j, 1, &jFormat);
+    }
+
+    jGains = env->NewObjectArray(nAudioPort->num_gains,
+                                          gAudioGainClass, NULL);
+    if (jGains == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    for (size_t j = 0; j < nAudioPort->num_gains; j++) {
+        audio_channel_mask_t nMask = nAudioPort->gains[j].channel_mask;
+        if (useInMask) {
+            jMask = inChannelMaskFromNative(nMask);
+            ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
+        } else {
+            jMask = outChannelMaskFromNative(nMask);
+            ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
+        }
+
+        jobject jGain = env->NewObject(gAudioGainClass, gAudioGainCstor,
+                                                 j,
+                                                 nAudioPort->gains[j].mode,
+                                                 jMask,
+                                                 nAudioPort->gains[j].min_value,
+                                                 nAudioPort->gains[j].max_value,
+                                                 nAudioPort->gains[j].default_value,
+                                                 nAudioPort->gains[j].step_value,
+                                                 nAudioPort->gains[j].min_ramp_ms,
+                                                 nAudioPort->gains[j].max_ramp_ms);
+        if (jGain == NULL) {
+            jStatus = (jint)AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+        env->SetObjectArrayElement(jGains, j, jGain);
+        env->DeleteLocalRef(jGain);
+    }
+
+    jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
+                                             nAudioPort->id);
+    if (jHandle == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+
+    if (nAudioPort->type == AUDIO_PORT_TYPE_DEVICE) {
+        ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type);
+        jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address);
+        *jAudioPort = env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor,
+                                     jHandle, jSamplingRates, jChannelMasks, jFormats, jGains,
+                                     nAudioPort->ext.device.type, jAddress);
+        env->DeleteLocalRef(jAddress);
+    } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) {
+        ALOGV("convertAudioPortFromNative is a mix");
+        *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor,
+                                     jHandle, nAudioPort->role, jSamplingRates, jChannelMasks,
+                                     jFormats, jGains);
+    } else {
+        ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type);
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    if (*jAudioPort == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+
+    jobject jAudioPortConfig;
+    jStatus = convertAudioPortConfigFromNative(env,
+                                                       *jAudioPort,
+                                                       &jAudioPortConfig,
+                                                       &nAudioPort->active_config);
+    if (jStatus != AUDIO_JAVA_SUCCESS) {
+        return jStatus;
+    }
+
+    env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig);
+
+exit:
+    if (jSamplingRates != NULL) {
+        env->DeleteLocalRef(jSamplingRates);
+    }
+    if (jChannelMasks != NULL) {
+        env->DeleteLocalRef(jChannelMasks);
+    }
+    if (jFormats != NULL) {
+        env->DeleteLocalRef(jFormats);
+    }
+    if (jGains != NULL) {
+        env->DeleteLocalRef(jGains);
+    }
+    if (jHandle != NULL) {
+        env->DeleteLocalRef(jHandle);
+    }
+
+    return jStatus;
+}
+
+
+static jint
+android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz,
+                                         jobject jPorts, jintArray jGeneration)
+{
+    ALOGV("listAudioPorts");
+
+    if (jPorts == NULL) {
+        ALOGE("listAudioPorts NULL AudioPort ArrayList");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jPorts, gArrayListClass)) {
+        ALOGE("listAudioPorts not an arraylist");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    status_t status;
+    unsigned int generation1;
+    unsigned int generation;
+    unsigned int numPorts;
+    jint *nGeneration;
+    struct audio_port *nPorts = NULL;
+    int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS;
+
+    // get the port count and all the ports until they both return the same generation
+    do {
+        if (attempts-- < 0) {
+            status = TIMED_OUT;
+            break;
+        }
+
+        numPorts = 0;
+        status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE,
+                                             AUDIO_PORT_TYPE_NONE,
+                                                      &numPorts,
+                                                      NULL,
+                                                      &generation1);
+        if (status != NO_ERROR || numPorts == 0) {
+            ALOGE_IF(status != NO_ERROR, "AudioSystem::listAudioPorts error %d", status);
+            break;
+        }
+        nPorts = (struct audio_port *)realloc(nPorts, numPorts * sizeof(struct audio_port));
+
+        status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE,
+                                             AUDIO_PORT_TYPE_NONE,
+                                                      &numPorts,
+                                                      nPorts,
+                                                      &generation);
+        ALOGV("listAudioPorts AudioSystem::listAudioPorts numPorts %d generation %d generation1 %d",
+              numPorts, generation, generation1);
+    } while (generation1 != generation && status == NO_ERROR);
+
+    jint jStatus = nativeToJavaStatus(status);
+    if (jStatus != AUDIO_JAVA_SUCCESS) {
+        goto exit;
+    }
+
+    nGeneration = env->GetIntArrayElements(jGeneration, NULL);
+    if (nGeneration == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    nGeneration[0] = generation1;
+    env->ReleaseIntArrayElements(jGeneration, nGeneration, 0);
+
+    for (size_t i = 0; i < numPorts; i++) {
+        jobject jAudioPort;
+        jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]);
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            goto exit;
+        }
+        env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort);
+    }
+
+exit:
+    free(nPorts);
+    return jStatus;
+}
+
+static int
+android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz,
+                                 jobjectArray jPatches, jobjectArray jSources, jobjectArray jSinks)
+{
+    status_t status;
+    jint jStatus;
+
+    ALOGV("createAudioPatch");
+    if (jPatches == NULL || jSources == NULL || jSinks == NULL) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    if (env->GetArrayLength(jPatches) != 1) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    jint numSources = env->GetArrayLength(jSources);
+    if (numSources == 0 || numSources > AUDIO_PATCH_PORTS_MAX) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    jint numSinks = env->GetArrayLength(jSinks);
+    if (numSinks == 0 || numSinks > AUDIO_PATCH_PORTS_MAX) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    audio_patch_handle_t handle = (audio_patch_handle_t)0;
+    jobject jPatch = env->GetObjectArrayElement(jPatches, 0);
+    jobject jPatchHandle = NULL;
+    if (jPatch != NULL) {
+        if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) {
+            return (jint)AUDIO_JAVA_BAD_VALUE;
+        }
+        jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle);
+        handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId);
+    }
+
+    struct audio_patch nPatch;
+
+    nPatch.id = handle;
+    nPatch.num_sources = 0;
+    nPatch.num_sinks = 0;
+    jobject jSource = NULL;
+    jobject jSink = NULL;
+
+    for (jint i = 0; i < numSources; i++) {
+        jSource = env->GetObjectArrayElement(jSources, i);
+        if (!env->IsInstanceOf(jSource, gAudioPortConfigClass)) {
+            jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
+            goto exit;
+        }
+        jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource);
+        env->DeleteLocalRef(jSource);
+        jSource = NULL;
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            goto exit;
+        }
+        nPatch.num_sources++;
+    }
+
+    for (jint i = 0; i < numSinks; i++) {
+        jSink = env->GetObjectArrayElement(jSinks, i);
+        if (!env->IsInstanceOf(jSink, gAudioPortConfigClass)) {
+            jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
+            goto exit;
+        }
+        jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink);
+        env->DeleteLocalRef(jSink);
+        jSink = NULL;
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            goto exit;
+        }
+        nPatch.num_sinks++;
+    }
+
+    ALOGV("AudioSystem::createAudioPatch");
+    status = AudioSystem::createAudioPatch(&nPatch, &handle);
+    ALOGV("AudioSystem::createAudioPatch() returned %d hande %d", status, handle);
+
+    jStatus = nativeToJavaStatus(status);
+    if (jStatus != AUDIO_JAVA_SUCCESS) {
+        goto exit;
+    }
+
+    if (jPatchHandle == NULL) {
+        jPatchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
+                                           handle);
+        if (jPatchHandle == NULL) {
+            jStatus = (jint)AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+        jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, jPatchHandle, jSources, jSinks);
+        if (jPatch == NULL) {
+            jStatus = (jint)AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+        env->SetObjectArrayElement(jPatches, 0, jPatch);
+    } else {
+        env->SetIntField(jPatchHandle, gAudioHandleFields.mId, handle);
+    }
+
+exit:
+    if (jPatchHandle != NULL) {
+        env->DeleteLocalRef(jPatchHandle);
+    }
+    if (jPatch != NULL) {
+        env->DeleteLocalRef(jPatch);
+    }
+    if (jSource != NULL) {
+        env->DeleteLocalRef(jSource);
+    }
+    if (jSink != NULL) {
+        env->DeleteLocalRef(jSink);
+    }
+    return jStatus;
+}
+
+static int
+android_media_AudioSystem_releaseAudioPatch(JNIEnv *env, jobject clazz,
+                                               jobject jPatch)
+{
+    ALOGV("releaseAudioPatch");
+    if (jPatch == NULL) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    audio_patch_handle_t handle = (audio_patch_handle_t)0;
+    jobject jPatchHandle = NULL;
+    if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle);
+    handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId);
+    env->DeleteLocalRef(jPatchHandle);
+
+    ALOGV("AudioSystem::releaseAudioPatch");
+    status_t status = AudioSystem::releaseAudioPatch(handle);
+    ALOGV("AudioSystem::releaseAudioPatch() returned %d", status);
+    jint jStatus = nativeToJavaStatus(status);
+    return status;
+}
+
+static jint
+android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz,
+                                           jobject jPatches, jintArray jGeneration)
+{
+    ALOGV("listAudioPatches");
+    if (jPatches == NULL) {
+        ALOGE("listAudioPatches NULL AudioPatch ArrayList");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jPatches, gArrayListClass)) {
+        ALOGE("listAudioPatches not an arraylist");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    status_t status;
+    unsigned int generation1;
+    unsigned int generation;
+    unsigned int numPatches;
+    jint *nGeneration;
+    struct audio_patch *nPatches = NULL;
+    jobjectArray jSources = NULL;
+    jobject jSource = NULL;
+    jobjectArray jSinks = NULL;
+    jobject jSink = NULL;
+    jobject jPatch = NULL;
+    int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS;
+
+    // get the patch count and all the patches until they both return the same generation
+    do {
+        if (attempts-- < 0) {
+            status = TIMED_OUT;
+            break;
+        }
+
+        numPatches = 0;
+        status = AudioSystem::listAudioPatches(&numPatches,
+                                               NULL,
+                                               &generation1);
+        if (status != NO_ERROR || numPatches == 0) {
+            ALOGE_IF(status != NO_ERROR, "listAudioPatches AudioSystem::listAudioPatches error %d",
+                                      status);
+            break;
+        }
+        nPatches = (struct audio_patch *)realloc(nPatches, numPatches * sizeof(struct audio_patch));
+
+        status = AudioSystem::listAudioPatches(&numPatches,
+                                               nPatches,
+                                               &generation);
+        ALOGV("listAudioPatches AudioSystem::listAudioPatches numPatches %d generation %d generation1 %d",
+              numPatches, generation, generation1);
+
+    } while (generation1 != generation && status == NO_ERROR);
+
+    jint jStatus = nativeToJavaStatus(status);
+    if (jStatus != AUDIO_JAVA_SUCCESS) {
+        goto exit;
+    }
+
+    nGeneration = env->GetIntArrayElements(jGeneration, NULL);
+    if (nGeneration == NULL) {
+        jStatus = AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    nGeneration[0] = generation1;
+    env->ReleaseIntArrayElements(jGeneration, nGeneration, 0);
+
+    for (size_t i = 0; i < numPatches; i++) {
+        jobject patchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
+                                                 nPatches[i].id);
+        if (patchHandle == NULL) {
+            jStatus = AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+        ALOGV("listAudioPatches patch %d num_sources %d num_sinks %d",
+              i, nPatches[i].num_sources, nPatches[i].num_sinks);
+
+        env->SetIntField(patchHandle, gAudioHandleFields.mId, nPatches[i].id);
+
+        // load sources
+        jSources = env->NewObjectArray(nPatches[i].num_sources,
+                                       gAudioPortConfigClass, NULL);
+        if (jSources == NULL) {
+            jStatus = AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+
+        for (size_t j = 0; j < nPatches[i].num_sources; j++) {
+            jStatus = convertAudioPortConfigFromNative(env,
+                                                      NULL,
+                                                      &jSource,
+                                                      &nPatches[i].sources[j]);
+            if (jStatus != AUDIO_JAVA_SUCCESS) {
+                goto exit;
+            }
+            env->SetObjectArrayElement(jSources, j, jSource);
+            env->DeleteLocalRef(jSource);
+            jSource = NULL;
+            ALOGV("listAudioPatches patch %d source %d is a %s handle %d",
+                  i, j,
+                  nPatches[i].sources[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix",
+                  nPatches[i].sources[j].id);
+        }
+        // load sinks
+        jSinks = env->NewObjectArray(nPatches[i].num_sinks,
+                                     gAudioPortConfigClass, NULL);
+        if (jSinks == NULL) {
+            jStatus = AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+
+        for (size_t j = 0; j < nPatches[i].num_sinks; j++) {
+            jStatus = convertAudioPortConfigFromNative(env,
+                                                      NULL,
+                                                      &jSink,
+                                                      &nPatches[i].sinks[j]);
+
+            if (jStatus != AUDIO_JAVA_SUCCESS) {
+                goto exit;
+            }
+            env->SetObjectArrayElement(jSinks, j, jSink);
+            env->DeleteLocalRef(jSink);
+            jSink = NULL;
+            ALOGV("listAudioPatches patch %d sink %d is a %s handle %d",
+                  i, j,
+                  nPatches[i].sinks[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix",
+                  nPatches[i].sinks[j].id);
+        }
+
+        jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor,
+                                       patchHandle, jSources, jSinks);
+        env->DeleteLocalRef(jSources);
+        jSources = NULL;
+        env->DeleteLocalRef(jSinks);
+        jSinks = NULL;
+        if (jPatch == NULL) {
+            jStatus = AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+        env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch);
+        env->DeleteLocalRef(jPatch);
+        jPatch = NULL;
+    }
+
+exit:
+    if (jSources != NULL) {
+        env->DeleteLocalRef(jSources);
+    }
+    if (jSource != NULL) {
+        env->DeleteLocalRef(jSource);
+    }
+    if (jSinks != NULL) {
+        env->DeleteLocalRef(jSinks);
+    }
+    if (jSink != NULL) {
+        env->DeleteLocalRef(jSink);
+    }
+    if (jPatch != NULL) {
+        env->DeleteLocalRef(jPatch);
+    }
+    free(nPatches);
+    return jStatus;
+}
+
+static jint
+android_media_AudioSystem_setAudioPortConfig(JNIEnv *env, jobject clazz,
+                                 jobject jAudioPortConfig)
+{
+    ALOGV("setAudioPortConfig");
+    if (jAudioPortConfig == NULL) {
+        return AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jAudioPortConfig, gAudioPortConfigClass)) {
+        return AUDIO_JAVA_BAD_VALUE;
+    }
+    struct audio_port_config nAudioPortConfig;
+    jint jStatus = convertAudioPortConfigToNative(env, &nAudioPortConfig, jAudioPortConfig);
+    if (jStatus != AUDIO_JAVA_SUCCESS) {
+        return jStatus;
+    }
+    status_t status = AudioSystem::setAudioPortConfig(&nAudioPortConfig);
+    ALOGV("AudioSystem::setAudioPortConfig() returned %d", status);
+    jStatus = nativeToJavaStatus(status);
+    return jStatus;
+}
+
+static void
+android_media_AudioSystem_eventHandlerSetup(JNIEnv *env, jobject thiz, jobject weak_this)
+{
+    ALOGV("eventHandlerSetup");
+
+    sp<JNIAudioPortCallback> callback = new JNIAudioPortCallback(env, thiz, weak_this);
+
+    AudioSystem::setAudioPortCallback(callback);
+}
+
+static void
+android_media_AudioSystem_eventHandlerFinalize(JNIEnv *env, jobject thiz)
+{
+    ALOGV("eventHandlerFinalize");
+
+    sp<JNIAudioPortCallback> callback;
+
+    AudioSystem::setAudioPortCallback(callback);
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
@@ -309,12 +1309,123 @@
     {"getOutputLatency",    "(I)I",     (void *)android_media_AudioSystem_getOutputLatency},
     {"setLowRamDevice",     "(Z)I",     (void *)android_media_AudioSystem_setLowRamDevice},
     {"checkAudioFlinger",    "()I",     (void *)android_media_AudioSystem_checkAudioFlinger},
+    {"listAudioPorts",      "(Ljava/util/ArrayList;[I)I",
+                                                (void *)android_media_AudioSystem_listAudioPorts},
+    {"createAudioPatch",    "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)I",
+                                            (void *)android_media_AudioSystem_createAudioPatch},
+    {"releaseAudioPatch",   "(Landroid/media/AudioPatch;)I",
+                                            (void *)android_media_AudioSystem_releaseAudioPatch},
+    {"listAudioPatches",    "(Ljava/util/ArrayList;[I)I",
+                                                (void *)android_media_AudioSystem_listAudioPatches},
+    {"setAudioPortConfig",   "(Landroid/media/AudioPortConfig;)I",
+                                            (void *)android_media_AudioSystem_setAudioPortConfig},
+};
+
+
+static JNINativeMethod gEventHandlerMethods[] = {
+    {"native_setup",
+        "(Ljava/lang/Object;)V",
+        (void *)android_media_AudioSystem_eventHandlerSetup},
+    {"native_finalize",
+        "()V",
+        (void *)android_media_AudioSystem_eventHandlerFinalize},
 };
 
 int register_android_media_AudioSystem(JNIEnv *env)
 {
+
+    jclass arrayListClass = env->FindClass("java/util/ArrayList");
+    gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass);
+    gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
+
+    jclass audioHandleClass = env->FindClass("android/media/AudioHandle");
+    gAudioHandleClass = (jclass) env->NewGlobalRef(audioHandleClass);
+    gAudioHandleCstor = env->GetMethodID(audioHandleClass, "<init>", "(I)V");
+    gAudioHandleFields.mId = env->GetFieldID(audioHandleClass, "mId", "I");
+
+    jclass audioPortClass = env->FindClass("android/media/AudioPort");
+    gAudioPortClass = (jclass) env->NewGlobalRef(audioPortClass);
+    gAudioPortCstor = env->GetMethodID(audioPortClass, "<init>",
+                               "(Landroid/media/AudioHandle;I[I[I[I[Landroid/media/AudioGain;)V");
+    gAudioPortFields.mHandle = env->GetFieldID(audioPortClass, "mHandle",
+                                               "Landroid/media/AudioHandle;");
+    gAudioPortFields.mRole = env->GetFieldID(audioPortClass, "mRole", "I");
+    gAudioPortFields.mGains = env->GetFieldID(audioPortClass, "mGains",
+                                              "[Landroid/media/AudioGain;");
+    gAudioPortFields.mActiveConfig = env->GetFieldID(audioPortClass, "mActiveConfig",
+                                              "Landroid/media/AudioPortConfig;");
+
+    jclass audioPortConfigClass = env->FindClass("android/media/AudioPortConfig");
+    gAudioPortConfigClass = (jclass) env->NewGlobalRef(audioPortConfigClass);
+    gAudioPortConfigCstor = env->GetMethodID(audioPortConfigClass, "<init>",
+                                 "(Landroid/media/AudioPort;IIILandroid/media/AudioGainConfig;)V");
+    gAudioPortConfigFields.mPort = env->GetFieldID(audioPortConfigClass, "mPort",
+                                                   "Landroid/media/AudioPort;");
+    gAudioPortConfigFields.mSamplingRate = env->GetFieldID(audioPortConfigClass,
+                                                           "mSamplingRate", "I");
+    gAudioPortConfigFields.mChannelMask = env->GetFieldID(audioPortConfigClass,
+                                                          "mChannelMask", "I");
+    gAudioPortConfigFields.mFormat = env->GetFieldID(audioPortConfigClass, "mFormat", "I");
+    gAudioPortConfigFields.mGain = env->GetFieldID(audioPortConfigClass, "mGain",
+                                                   "Landroid/media/AudioGainConfig;");
+    gAudioPortConfigFields.mConfigMask = env->GetFieldID(audioPortConfigClass, "mConfigMask", "I");
+
+    jclass audioDevicePortConfigClass = env->FindClass("android/media/AudioDevicePortConfig");
+    gAudioDevicePortConfigClass = (jclass) env->NewGlobalRef(audioDevicePortConfigClass);
+    gAudioDevicePortConfigCstor = env->GetMethodID(audioDevicePortConfigClass, "<init>",
+                         "(Landroid/media/AudioDevicePort;IIILandroid/media/AudioGainConfig;)V");
+
+    jclass audioMixPortConfigClass = env->FindClass("android/media/AudioMixPortConfig");
+    gAudioMixPortConfigClass = (jclass) env->NewGlobalRef(audioMixPortConfigClass);
+    gAudioMixPortConfigCstor = env->GetMethodID(audioMixPortConfigClass, "<init>",
+                         "(Landroid/media/AudioMixPort;IIILandroid/media/AudioGainConfig;)V");
+
+    jclass audioDevicePortClass = env->FindClass("android/media/AudioDevicePort");
+    gAudioDevicePortClass = (jclass) env->NewGlobalRef(audioDevicePortClass);
+    gAudioDevicePortCstor = env->GetMethodID(audioDevicePortClass, "<init>",
+             "(Landroid/media/AudioHandle;[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V");
+
+    jclass audioMixPortClass = env->FindClass("android/media/AudioMixPort");
+    gAudioMixPortClass = (jclass) env->NewGlobalRef(audioMixPortClass);
+    gAudioMixPortCstor = env->GetMethodID(audioMixPortClass, "<init>",
+                              "(Landroid/media/AudioHandle;I[I[I[I[Landroid/media/AudioGain;)V");
+
+    jclass audioGainClass = env->FindClass("android/media/AudioGain");
+    gAudioGainClass = (jclass) env->NewGlobalRef(audioGainClass);
+    gAudioGainCstor = env->GetMethodID(audioGainClass, "<init>", "(IIIIIIIII)V");
+
+    jclass audioGainConfigClass = env->FindClass("android/media/AudioGainConfig");
+    gAudioGainConfigClass = (jclass) env->NewGlobalRef(audioGainConfigClass);
+    gAudioGainConfigCstor = env->GetMethodID(audioGainConfigClass, "<init>",
+                                             "(ILandroid/media/AudioGain;II[II)V");
+    gAudioGainConfigFields.mIndex = env->GetFieldID(gAudioGainConfigClass, "mIndex", "I");
+    gAudioGainConfigFields.mMode = env->GetFieldID(audioGainConfigClass, "mMode", "I");
+    gAudioGainConfigFields.mChannelMask = env->GetFieldID(audioGainConfigClass, "mChannelMask",
+                                                          "I");
+    gAudioGainConfigFields.mValues = env->GetFieldID(audioGainConfigClass, "mValues", "[I");
+    gAudioGainConfigFields.mRampDurationMs = env->GetFieldID(audioGainConfigClass,
+                                                             "mRampDurationMs", "I");
+
+    jclass audioPatchClass = env->FindClass("android/media/AudioPatch");
+    gAudioPatchClass = (jclass) env->NewGlobalRef(audioPatchClass);
+    gAudioPatchCstor = env->GetMethodID(audioPatchClass, "<init>",
+"(Landroid/media/AudioHandle;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)V");
+    gAudioPatchFields.mHandle = env->GetFieldID(audioPatchClass, "mHandle",
+                                                "Landroid/media/AudioHandle;");
+
+    jclass eventHandlerClass = env->FindClass(kEventHandlerClassPathName);
+    gPostEventFromNative = env->GetStaticMethodID(eventHandlerClass, "postEventFromNative",
+                                            "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+
+
     AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback);
 
-    return AndroidRuntime::registerNativeMethods(env,
+    int status = AndroidRuntime::registerNativeMethods(env,
                 kClassPathName, gMethods, NELEM(gMethods));
+
+    if (status == 0) {
+        status = AndroidRuntime::registerNativeMethods(env,
+                kEventHandlerClassPathName, gEventHandlerMethods, NELEM(gEventHandlerMethods));
+    }
+    return status;
 }
diff --git a/core/res/res/anim/input_method_exit.xml b/core/res/res/anim/input_method_exit.xml
index 4c4f6a4..117774a 100644
--- a/core/res/res/anim/input_method_exit.xml
+++ b/core/res/res/anim/input_method_exit.xml
@@ -17,7 +17,7 @@
 -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
 	android:shareInterpolator="false">
-    <translate android:fromYDelta="0" android:toYDelta="-20%"
+    <translate android:fromYDelta="0" android:toYDelta="10%"
 			android:interpolator="@interpolator/accelerate_quint"
             android:duration="@android:integer/config_shortAnimTime"/>
     <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
diff --git a/core/res/res/anim/lock_screen_behind_enter.xml b/core/res/res/anim/lock_screen_behind_enter.xml
index 4a956d7..7e212be 100644
--- a/core/res/res/anim/lock_screen_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_behind_enter.xml
@@ -18,10 +18,17 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:background="#ff000000" android:shareInterpolator="false">
+    android:detachWallpaper="true" android:shareInterpolator="false" android:startOffset="60">
     <alpha
-        android:fromAlpha="1.0" android:toAlpha="1.0"
+        android:fromAlpha="0.0" android:toAlpha="1.0"
         android:fillEnabled="true" android:fillBefore="true"
-        android:interpolator="@interpolator/decelerate_quint"
-        android:duration="0"/>
+        android:interpolator="@interpolator/linear_out_slow_in"
+        android:duration="@integer/config_shortAnimTime"/>
+    <scale
+        android:fromXScale="0.95" android:toXScale="1.0"
+        android:fromYScale="0.95" android:toYScale="1.0"
+        android:pivotX="50%" android:pivotY="50%"
+        android:fillEnabled="true" android:fillBefore="true"
+        android:interpolator="@interpolator/linear_out_slow_in"
+        android:duration="@integer/config_shortAnimTime" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml b/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
deleted file mode 100644
index f7a6a65..0000000
--- a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:detachWallpaper="true" android:shareInterpolator="false">
-    <alpha
-        android:fromAlpha="0.0" android:toAlpha="1.0"
-        android:fillEnabled="true" android:fillBefore="true"
-        android:interpolator="@interpolator/decelerate_quad"
-        android:startOffset="@android:integer/config_mediumAnimTime"
-        android:duration="@android:integer/config_shortAnimTime"/>
-</set>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e9d8ccc..f6732d3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1538,6 +1538,7 @@
          -->
     <string-array translatable="false" name="config_globalActionsList">
         <item>power</item>
+        <item>bugreport</item>
         <item>users</item>
     </string-array>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6e1629b..6cd7cd2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1679,7 +1679,6 @@
   <java-symbol type="anim" name="push_down_out" />
   <java-symbol type="anim" name="push_up_in" />
   <java-symbol type="anim" name="push_up_out" />
-  <java-symbol type="anim" name="lock_screen_wallpaper_behind_enter" />
   <java-symbol type="anim" name="lock_screen_behind_enter" />
 
   <java-symbol type="bool" name="config_alwaysUseCdmaRssi" />
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index f4affa0..2f8a17b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -41,6 +41,7 @@
 import java.util.HashMap;
 import java.util.ArrayList;
 
+
 /**
  * AudioManager provides access to volume and ringer mode control.
  * <p>
@@ -61,6 +62,7 @@
     private final boolean mUseVolumeKeySounds;
     private final Binder mToken = new Binder();
     private static String TAG = "AudioManager";
+    AudioPortEventHandler mAudioPortEventHandler;
 
     /**
      * Broadcast intent, a hint for applications that audio is about to become
@@ -438,6 +440,7 @@
                 com.android.internal.R.bool.config_useMasterVolume);
         mUseVolumeKeySounds = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_useVolumeKeySounds);
+        mAudioPortEventHandler = new AudioPortEventHandler(this);
     }
 
     private static IAudioService getService()
@@ -3003,7 +3006,7 @@
      * @hide
      */
     public int listAudioPorts(ArrayList<AudioPort> ports) {
-        return ERROR_INVALID_OPERATION;
+        return updateAudioPortCache(ports, null);
     }
 
     /**
@@ -3012,7 +3015,17 @@
      * @hide
      */
     public int listAudioDevicePorts(ArrayList<AudioPort> devices) {
-        return ERROR_INVALID_OPERATION;
+        ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
+        int status = updateAudioPortCache(ports, null);
+        if (status == SUCCESS) {
+            devices.clear();
+            for (int i = 0; i < ports.size(); i++) {
+                if (ports.get(i) instanceof AudioDevicePort) {
+                    devices.add(ports.get(i));
+                }
+            }
+        }
+        return status;
     }
 
     /**
@@ -3041,7 +3054,7 @@
     public int createAudioPatch(AudioPatch[] patch,
                                  AudioPortConfig[] sources,
                                  AudioPortConfig[] sinks) {
-        return ERROR_INVALID_OPERATION;
+        return AudioSystem.createAudioPatch(patch, sources, sinks);
     }
 
     /**
@@ -3056,7 +3069,7 @@
      * @hide
      */
     public int releaseAudioPatch(AudioPatch patch) {
-        return  ERROR_INVALID_OPERATION;
+        return AudioSystem.releaseAudioPatch(patch);
     }
 
     /**
@@ -3065,7 +3078,7 @@
      * @hide
      */
     public int listAudioPatches(ArrayList<AudioPatch> patches) {
-        return ERROR_INVALID_OPERATION;
+        return updateAudioPortCache(null, patches);
     }
 
     /**
@@ -3074,7 +3087,14 @@
      * @hide
      */
     public int setAudioPortGain(AudioPort port, AudioGainConfig gain) {
-        return ERROR_INVALID_OPERATION;
+        if (port == null || gain == null) {
+            return ERROR_BAD_VALUE;
+        }
+        AudioPortConfig activeConfig = port.activeConfig();
+        AudioPortConfig config = new AudioPortConfig(port, activeConfig.samplingRate(),
+                                        activeConfig.channelMask(), activeConfig.format(), gain);
+        config.mConfigMask = AudioPortConfig.GAIN;
+        return AudioSystem.setAudioPortConfig(config);
     }
 
     /**
@@ -3102,16 +3122,128 @@
     }
 
     /**
-     * Register an audio port update listener.
+     * Register an audio port list update listener.
      * @hide
      */
     public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+        mAudioPortEventHandler.registerListener(l);
     }
 
     /**
-     * Unregister an audio port update listener.
+     * Unregister an audio port list update listener.
      * @hide
      */
     public void unregisterAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+        mAudioPortEventHandler.unregisterListener(l);
+    }
+
+    //
+    // AudioPort implementation
+    //
+
+    static final int AUDIOPORT_GENERATION_INIT = 0;
+    Integer mAudioPortGeneration = new Integer(AUDIOPORT_GENERATION_INIT);
+    ArrayList<AudioPort> mAudioPortsCached = new ArrayList<AudioPort>();
+    ArrayList<AudioPatch> mAudioPatchesCached = new ArrayList<AudioPatch>();
+
+    int resetAudioPortGeneration() {
+        int generation;
+        synchronized (mAudioPortGeneration) {
+            generation = mAudioPortGeneration;
+            mAudioPortGeneration = AUDIOPORT_GENERATION_INIT;
+        }
+        return generation;
+    }
+
+    int updateAudioPortCache(ArrayList<AudioPort> ports, ArrayList<AudioPatch> patches) {
+        synchronized (mAudioPortGeneration) {
+
+            if (mAudioPortGeneration == AUDIOPORT_GENERATION_INIT) {
+                int[] patchGeneration = new int[1];
+                int[] portGeneration = new int[1];
+                int status;
+                ArrayList<AudioPort> newPorts = new ArrayList<AudioPort>();
+                ArrayList<AudioPatch> newPatches = new ArrayList<AudioPatch>();
+
+                do {
+                    newPorts.clear();
+                    status = AudioSystem.listAudioPorts(newPorts, portGeneration);
+                    Log.i(TAG, "updateAudioPortCache AudioSystem.listAudioPorts() status: "+
+                                    status+" num ports: "+ newPorts.size() +" portGeneration: "+portGeneration[0]);
+                    if (status != SUCCESS) {
+                        return status;
+                    }
+                    newPatches.clear();
+                    status = AudioSystem.listAudioPatches(newPatches, patchGeneration);
+                    Log.i(TAG, "updateAudioPortCache AudioSystem.listAudioPatches() status: "+
+                            status+" num patches: "+ newPatches.size() +" patchGeneration: "+patchGeneration[0]);
+                    if (status != SUCCESS) {
+                        return status;
+                    }
+                } while (patchGeneration[0] != portGeneration[0]);
+
+                for (int i = 0; i < newPatches.size(); i++) {
+                    for (int j = 0; j < newPatches.get(i).sources().length; j++) {
+                        AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j], newPorts);
+                        if (portCfg == null) {
+                            return ERROR;
+                        }
+                        newPatches.get(i).sources()[j] = portCfg;
+                    }
+                    for (int j = 0; j < newPatches.get(i).sinks().length; j++) {
+                        AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j], newPorts);
+                        if (portCfg == null) {
+                            return ERROR;
+                        }
+                        newPatches.get(i).sinks()[j] = portCfg;
+                    }
+                }
+
+                mAudioPortsCached = newPorts;
+                mAudioPatchesCached = newPatches;
+                mAudioPortGeneration = portGeneration[0];
+            }
+            if (ports != null) {
+                ports.clear();
+                ports.addAll(mAudioPortsCached);
+            }
+            if (patches != null) {
+                patches.clear();
+                patches.addAll(mAudioPatchesCached);
+            }
+        }
+        return SUCCESS;
+    }
+
+    AudioPortConfig updatePortConfig(AudioPortConfig portCfg, ArrayList<AudioPort> ports) {
+        AudioPort port = portCfg.port();
+        int k;
+        for (k = 0; k < ports.size(); k++) {
+            // compare handles because the port returned by JNI is not of the correct
+            // subclass
+            if (ports.get(k).handle().equals(port.handle())) {
+                Log.i(TAG, "updatePortConfig match found for port handle: "+
+                            port.handle().id()+" port: "+ k);
+                port = ports.get(k);
+                break;
+            }
+        }
+        if (k == ports.size()) {
+            // this hould never happen
+            Log.e(TAG, "updatePortConfig port not found for handle: "+port.handle().id());
+            return null;
+        }
+        AudioGainConfig gainCfg = portCfg.gain();
+        if (gainCfg != null) {
+            AudioGain gain = port.gain(gainCfg.index());
+            gainCfg = gain.buildConfig(gainCfg.mode(),
+                                       gainCfg.channelMask(),
+                                       gainCfg.values(),
+                                       gainCfg.rampDurationMs());
+        }
+        return port.buildConfig(portCfg.samplingRate(),
+                                                 portCfg.channelMask(),
+                                                 portCfg.format(),
+                                                 gainCfg);
     }
 }
diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java
index 9aeddef..fbd5022 100644
--- a/media/java/android/media/AudioPort.java
+++ b/media/java/android/media/AudioPort.java
@@ -133,6 +133,9 @@
      * Get the gain descriptor at a given index
      */
     AudioGain gain(int index) {
+        if (index < mGains.length) {
+            return null;
+        }
         return mGains[index];
     }
 
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
new file mode 100644
index 0000000..cd9a4de
--- /dev/null
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.lang.ref.WeakReference;
+
+/**
+ * The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks
+ * posted from JNI
+ * @hide
+ */
+
+class AudioPortEventHandler {
+    private final Handler mHandler;
+    private ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners;
+    private AudioManager mAudioManager;
+
+    private static String TAG = "AudioPortEventHandler";
+
+    private static final int AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1;
+    private static final int AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2;
+    private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3;
+    private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4;
+
+    AudioPortEventHandler(AudioManager audioManager) {
+        mAudioManager = audioManager;
+        mListeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
+
+        // find the looper for our new event handler
+        Looper looper = Looper.myLooper();
+        if (looper == null) {
+            throw new IllegalArgumentException("Calling thread not associated with a looper");
+        }
+
+        mHandler = new Handler(looper) {
+            @Override
+            public void handleMessage(Message msg) {
+                Log.i(TAG, "handleMessage: "+msg.what);
+                ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
+                synchronized (this) {
+                    if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
+                        listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
+                        if (mListeners.contains(msg.obj)) {
+                            listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj);
+                        }
+                    } else {
+                        listeners = mListeners;
+                    }
+                }
+                if (listeners.isEmpty()) {
+                    return;
+                }
+                // reset audio port cache if the event corresponds to a change coming
+                // from audio policy service or if mediaserver process died.
+                if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED ||
+                        msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED ||
+                        msg.what == AUDIOPORT_EVENT_SERVICE_DIED) {
+                    mAudioManager.resetAudioPortGeneration();
+                }
+                ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
+                ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
+                if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
+                    int status = mAudioManager.updateAudioPortCache(ports, patches);
+                    if (status != AudioManager.SUCCESS) {
+                        return;
+                    }
+                }
+
+                switch (msg.what) {
+                case AUDIOPORT_EVENT_NEW_LISTENER:
+                case AUDIOPORT_EVENT_PORT_LIST_UPDATED:
+                    AudioPort[] portList = ports.toArray(new AudioPort[0]);
+                    for (int i = 0; i < listeners.size(); i++) {
+                        listeners.get(i).OnAudioPortListUpdate(portList);
+                    }
+                    if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) {
+                        break;
+                    }
+                    // FALL THROUGH
+
+                case AUDIOPORT_EVENT_PATCH_LIST_UPDATED:
+                    AudioPatch[] patchList = patches.toArray(new AudioPatch[0]);
+                    for (int i = 0; i < listeners.size(); i++) {
+                        listeners.get(i).OnAudioPatchListUpdate(patchList);
+                    }
+                    break;
+
+                case AUDIOPORT_EVENT_SERVICE_DIED:
+                    for (int i = 0; i < listeners.size(); i++) {
+                        listeners.get(i).OnServiceDied();
+                    }
+                    break;
+
+                default:
+                    break;
+                }
+            }
+        };
+
+        native_setup(new WeakReference<AudioPortEventHandler>(this));
+    }
+    private native void native_setup(Object module_this);
+
+    @Override
+    protected void finalize() {
+        native_finalize();
+    }
+    private native void native_finalize();
+
+    void registerListener(AudioManager.OnAudioPortUpdateListener l) {
+        synchronized (this) {
+            mListeners.add(l);
+        }
+        if (mHandler != null) {
+            Message m = mHandler.obtainMessage(AUDIOPORT_EVENT_NEW_LISTENER, 0, 0, l);
+            mHandler.sendMessage(m);
+        }
+    }
+
+    void unregisterListener(AudioManager.OnAudioPortUpdateListener l) {
+        synchronized (this) {
+            mListeners.remove(l);
+        }
+    }
+
+    Handler handler() {
+        return mHandler;
+    }
+
+    @SuppressWarnings("unused")
+    private static void postEventFromNative(Object module_ref,
+                                            int what, int arg1, int arg2, Object obj) {
+        AudioPortEventHandler eventHandler =
+                (AudioPortEventHandler)((WeakReference)module_ref).get();
+        if (eventHandler == null) {
+            return;
+        }
+
+        if (eventHandler != null) {
+            Handler handler = eventHandler.handler();
+            if (handler != null) {
+                Message m = handler.obtainMessage(what, arg1, arg2, obj);
+                handler.sendMessage(m);
+            }
+        }
+    }
+
+}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 0c45443..af7a3e1 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import java.util.ArrayList;
 
 /* IF YOU CHANGE ANY OF THE CONSTANTS IN THIS FILE, DO NOT FORGET
  * TO UPDATE THE CORRESPONDING NATIVE GLUE AND AudioManager.java.
@@ -458,4 +459,12 @@
 
     public static native int setLowRamDevice(boolean isLowRamDevice);
     public static native int checkAudioFlinger();
+
+    public static native int listAudioPorts(ArrayList<AudioPort> ports, int[] generation);
+    public static native int createAudioPatch(AudioPatch[] patch,
+                                            AudioPortConfig[] sources, AudioPortConfig[] sinks);
+    public static native int releaseAudioPatch(AudioPatch patch);
+    public static native int listAudioPatches(ArrayList<AudioPatch> patches, int[] generation);
+    public static native int setAudioPortConfig(AudioPortConfig config);
 }
+
diff --git a/media/lib/signer/Android.mk b/media/lib/signer/Android.mk
index bca643a..b0d3177 100644
--- a/media/lib/signer/Android.mk
+++ b/media/lib/signer/Android.mk
@@ -23,7 +23,8 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := \
-            $(call all-java-files-under, java)
+            $(call all-java-files-under, java) \
+            $(call all-aidl-files-under, java)
 
 include $(BUILD_JAVA_LIBRARY)
 
diff --git a/media/lib/signer/com.android.mediadrm.signer.xml b/media/lib/signer/com.android.mediadrm.signer.xml
index b5b1f09..fd3a115 100644
--- a/media/lib/signer/com.android.mediadrm.signer.xml
+++ b/media/lib/signer/com.android.mediadrm.signer.xml
@@ -15,6 +15,6 @@
 -->
 
 <permissions>
-    <library name="com.android.media.drm.signer"
-            file="/system/framework/com.android.media.drm.signer.jar" />
+    <library name="com.android.mediadrm.signer"
+            file="/system/framework/com.android.mediadrm.signer.jar" />
 </permissions>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 4c7f3df..b280ab7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -204,9 +204,9 @@
         }
 
         @Override
-        public void startKeyguardExitAnimation(long fadeoutDuration) {
+        public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
             checkPermission();
-            mKeyguardViewMediator.startKeyguardExitAnimation(fadeoutDuration);
+            mKeyguardViewMediator.startKeyguardExitAnimation(startTime, fadeoutDuration);
         }
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7110d8d..f7b4994 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1083,7 +1083,8 @@
                     handleDismiss();
                     break;
                 case START_KEYGUARD_EXIT_ANIM:
-                    handleStartKeyguardExitAnimation((Long) msg.obj);
+                    StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
+                    handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
                     break;
             }
         }
@@ -1227,7 +1228,7 @@
         }
     }
 
-    private void handleStartKeyguardExitAnimation(long fadeoutDuration) {
+    private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
         synchronized (KeyguardViewMediator.this) {
 
             // only play "unlock" noises if not on a call (since the incall UI
@@ -1236,7 +1237,7 @@
                 playSounds(false);
             }
 
-            mStatusBarKeyguardViewManager.hide();
+            mStatusBarKeyguardViewManager.hide(startTime, fadeoutDuration);
             mShowing = false;
             mKeyguardDonePending = false;
             updateActivityLockScreenState();
@@ -1346,12 +1347,24 @@
         return mStatusBarKeyguardViewManager;
     }
 
-    public void startKeyguardExitAnimation(long fadeoutDuration) {
-        Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM, fadeoutDuration);
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+        Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,
+                new StartKeyguardExitAnimParams(startTime, fadeoutDuration));
         mHandler.sendMessage(msg);
     }
 
     public ViewMediatorCallback getViewMediatorCallback() {
         return mViewMediatorCallback;
     }
+
+    private static class StartKeyguardExitAnimParams {
+
+        long startTime;
+        long fadeoutDuration;
+
+        private StartKeyguardExitAnimParams(long startTime, long fadeoutDuration) {
+            this.startTime = startTime;
+            this.fadeoutDuration = fadeoutDuration;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index b7a7b0a..3aaace4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -17,10 +17,14 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.os.SystemClock;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardViewBase;
@@ -43,6 +47,8 @@
     private StatusBarWindowManager mWindowManager;
     private KeyguardViewBase mKeyguardView;
     private ViewGroup mRoot;
+    private Interpolator mFadeOutInterpolator = new LinearInterpolator();
+    private boolean mFadingOut;
 
     public KeyguardBouncer(Context context, ViewMediatorCallback callback,
             LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager,
@@ -86,6 +92,29 @@
         }
     }
 
+    public void animateHide(long delay, long duration) {
+        if (isShowing()) {
+            mFadingOut = true;
+            mKeyguardView.animate()
+                    .alpha(0)
+                    .withLayer()
+
+                    // Make it disappear faster, as the focus should be on the activity behind.
+                    .setDuration(duration / 3)
+                    .setInterpolator(mFadeOutInterpolator)
+                    .setStartDelay(delay)
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mFadingOut = false;
+                            hide(true /* destroyView */);
+                        }
+                    });
+        } else {
+            hide(true /* destroyView */);
+        }
+    }
+
     /**
      * Reset the state of the view.
      */
@@ -110,7 +139,7 @@
     }
 
     public boolean isShowing() {
-        return mRoot != null && mRoot.getVisibility() == View.VISIBLE;
+        return mRoot != null && mRoot.getVisibility() == View.VISIBLE && !mFadingOut;
     }
 
     public void prepare() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index c5a9b85..220b691 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -200,8 +200,10 @@
             case MotionEvent.ACTION_CANCEL:
                 mTrackingPointer = -1;
                 trackMovement(event);
-                boolean expand = flingWithCurrentVelocity();
+                float vel = getCurrentVelocity();
+                boolean expand = flingExpands(vel);
                 onTrackingStopped(expand);
+                fling(vel, expand);
                 if (mVelocityTracker != null) {
                     mVelocityTracker.recycle();
                     mVelocityTracker = null;
@@ -323,18 +325,15 @@
     }
 
     /**
-     * @return whether the panel will be expanded after the animation
+     * @param vel the current velocity of the motion
+     * @return whether a fling should expands the panel; contracts otherwise
      */
-    private boolean flingWithCurrentVelocity() {
-        float vel = getCurrentVelocity();
-        boolean expand;
+    private boolean flingExpands(float vel) {
         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
-            expand = getExpandedFraction() > 0.5f;
+            return getExpandedFraction() > 0.5f;
         } else {
-            expand = vel > 0;
+            return vel > 0;
         }
-        fling(vel, expand);
-        return expand;
     }
 
     protected void fling(float vel, boolean expand) {
@@ -342,6 +341,7 @@
         float target = expand ? getMaxPanelHeight() : 0.0f;
         if (target == mExpandedHeight) {
             onExpandingFinished();
+            mBar.panelExpansionChanged(this, mExpandedFraction);
             return;
         }
         ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, target);
@@ -430,7 +430,7 @@
 
     public void setExpandedHeightInternal(float h) {
         float fh = getMaxPanelHeight();
-        mExpandedHeight = Math.min(fh, h);
+        mExpandedHeight = Math.max(0, Math.min(fh, h));
         float overExpansion = h - fh;
         overExpansion = Math.max(0, overExpansion);
         if (overExpansion != mOverExpansion) {
@@ -442,7 +442,7 @@
         }
 
         onHeightUpdated(mExpandedHeight);
-        mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh);
+        mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : mExpandedHeight / fh);
     }
 
     protected void onOverExpansionChanged(float overExpansion) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 6156fc3..1264d75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -19,16 +19,12 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
-import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.view.View;
 import android.view.ViewTreeObserver;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
 
 /**
  * Controls both the scrim behind the notifications and in front of the notifications (when a
@@ -53,6 +49,11 @@
     private boolean mAnimateChange;
     private boolean mUpdatePending;
     private boolean mExpanding;
+    private boolean mAnimateKeyguardFadingOut;
+    private long mDurationOverride = -1;
+    private long mAnimationDelay;
+    private Runnable mOnAnimationFinished;
+    private boolean mAnimationStarted;
 
     private final Interpolator mInterpolator = new DecelerateInterpolator();
 
@@ -87,14 +88,26 @@
         scheduleUpdate();
     }
 
+    public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) {
+        mAnimateKeyguardFadingOut = true;
+        mDurationOverride = duration;
+        mAnimationDelay = delay;
+        mAnimateChange = true;
+        mOnAnimationFinished = onAnimationFinished;
+        scheduleUpdate();
+    }
+
     private void scheduleUpdate() {
         if (mUpdatePending) return;
+
+        // Make sure that a frame gets scheduled.
+        mScrimBehind.invalidate();
         mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this);
         mUpdatePending = true;
     }
 
     private void updateScrims() {
-        if (!mKeyguardShowing) {
+        if (!mKeyguardShowing || mAnimateKeyguardFadingOut) {
             updateScrimNormal();
             setScrimInFrontColor(0);
         } else {
@@ -170,8 +183,20 @@
             }
         });
         anim.setInterpolator(mInterpolator);
-        anim.setDuration(ANIMATION_DURATION);
+        anim.setStartDelay(mAnimationDelay);
+        anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION);
+        anim.addListener(new AnimatorListenerAdapter() {
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mOnAnimationFinished != null) {
+                    mOnAnimationFinished.run();
+                    mOnAnimationFinished = null;
+                }
+            }
+        });
         anim.start();
+        mAnimationStarted = true;
     }
 
     private int getBackgroundAlpha(View scrim) {
@@ -188,6 +213,16 @@
         mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
         mUpdatePending = false;
         updateScrims();
+        mAnimateKeyguardFadingOut = false;
+        mDurationOverride = -1;
+        mAnimationDelay = 0;
+
+        // Make sure that we always call the listener even if we didn't start an animation.
+        if (!mAnimationStarted && mOnAnimationFinished != null) {
+            mOnAnimationFinished.run();
+            mOnAnimationFinished = null;
+        }
+        mAnimationStarted = false;
         return true;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index d5551b8..e3145a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.Slog;
 import android.view.KeyEvent;
 import android.view.View;
@@ -183,11 +184,23 @@
     /**
      * Hides the keyguard view
      */
-    public void hide() {
+    public void hide(long startTime, long fadeoutDuration) {
         mShowing = false;
         mPhoneStatusBar.hideKeyguard();
+        mStatusBarWindowManager.setKeyguardFadingAway(true);
         mStatusBarWindowManager.setKeyguardShowing(false);
-        mBouncer.hide(true /* destroyView */);
+        long uptimeMillis = SystemClock.uptimeMillis();
+        long delay = startTime - uptimeMillis;
+        if (delay < 0) {
+            delay = 0;
+        }
+        mBouncer.animateHide(delay, fadeoutDuration);
+        mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() {
+            @Override
+            public void run() {
+                mStatusBarWindowManager.setKeyguardFadingAway(false);
+            }
+        });
         mViewMediatorCallback.keyguardGone();
         updateStates();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index b7bf6cd..fe57cef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -124,7 +124,8 @@
     }
 
     private void applyHeight(State state) {
-        boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded;
+        boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded
+                || state.keyguardFadingAway;
         if (expanded) {
             mLp.height = ViewGroup.LayoutParams.MATCH_PARENT;
         } else {
@@ -201,6 +202,11 @@
         apply(mCurrentState);
     }
 
+    public void setKeyguardFadingAway(boolean keyguardFadingAway) {
+        mCurrentState.keyguardFadingAway = keyguardFadingAway;
+        apply(mCurrentState);
+    }
+
     /**
      * @param state The {@link StatusBarState} of the status bar.
      */
@@ -217,6 +223,7 @@
         boolean statusBarFocusable;
         long keyguardUserActivityTimeout;
         boolean bouncerShowing;
+        boolean keyguardFadingAway;
 
         /**
          * The {@link BaseStatusBar} state from the status bar.
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 673ce0b..762d3df 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -185,7 +185,8 @@
 
         // If we only have 1 item and it's a simple press action, just do this action.
         if (mAdapter.getCount() == 1
-                && mAdapter.getItem(0) instanceof SinglePressAction) {
+                && mAdapter.getItem(0) instanceof SinglePressAction
+                && !(mAdapter.getItem(0) instanceof LongPressAction)) {
             ((SinglePressAction) mAdapter.getItem(0)).onPress();
         } else {
             WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
@@ -262,7 +263,7 @@
                 continue;
             }
             if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
-                mItems.add(getPowerAction());
+                mItems.add(new PowerAction());
             } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
                 mItems.add(mAirplaneModeOn);
             } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)
@@ -300,7 +301,11 @@
                     @Override
                     public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
                             long id) {
-                        return mAdapter.getItem(position).onLongPress();
+                        final Action action = mAdapter.getItem(position);
+                        if (action instanceof LongPressAction) {
+                            return ((LongPressAction) action).onLongPress();
+                        }
+                        return false;
                     }
         });
         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
@@ -310,29 +315,33 @@
         return dialog;
     }
 
-    private Action getPowerAction() {
-        return new SinglePressAction(
-                com.android.internal.R.drawable.ic_lock_power_off,
-                R.string.global_action_power_off) {
+    private final class PowerAction extends SinglePressAction implements LongPressAction {
+        private PowerAction() {
+            super(com.android.internal.R.drawable.ic_lock_power_off,
+                R.string.global_action_power_off);
+        }
 
-            public void onPress() {
-                // shutdown by making sure radio and power are handled accordingly.
-                mWindowManagerFuncs.shutdown(true);
-            }
+        @Override
+        public boolean onLongPress() {
+            mWindowManagerFuncs.rebootSafeMode(true);
+            return true;
+        }
 
-            public boolean onLongPress() {
-                mWindowManagerFuncs.rebootSafeMode(true);
-                return true;
-            }
+        @Override
+        public boolean showDuringKeyguard() {
+            return true;
+        }
 
-            public boolean showDuringKeyguard() {
-                return true;
-            }
+        @Override
+        public boolean showBeforeProvisioning() {
+            return true;
+        }
 
-            public boolean showBeforeProvisioning() {
-                return true;
-            }
-        };
+        @Override
+        public void onPress() {
+            // shutdown by making sure radio and power are handled accordingly.
+            mWindowManagerFuncs.shutdown(false /* confirm */);
+        }
     }
 
     private Action getBugReportAction() {
@@ -367,10 +376,6 @@
                 dialog.show();
             }
 
-            public boolean onLongPress() {
-                return false;
-            }
-
             public boolean showDuringKeyguard() {
                 return true;
             }
@@ -393,11 +398,6 @@
             }
 
             @Override
-            public boolean onLongPress() {
-                return false;
-            }
-
-            @Override
             public boolean showDuringKeyguard() {
                 return true;
             }
@@ -583,8 +583,6 @@
 
         void onPress();
 
-        public boolean onLongPress();
-
         /**
          * @return whether this action should appear in the dialog when the keygaurd
          *    is showing.
@@ -601,6 +599,13 @@
     }
 
     /**
+     * An action that also supports long press.
+     */
+    private interface LongPressAction extends Action {
+        boolean onLongPress();
+    }
+
+    /**
      * A single press action maintains no state, just responds to a press
      * and takes an action.
      */
@@ -637,10 +642,6 @@
 
         abstract public void onPress();
 
-        public boolean onLongPress() {
-            return false;
-        }
-
         public View create(
                 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
             View v = inflater.inflate(R.layout.global_actions_item, parent, false);
@@ -769,10 +770,6 @@
             changeStateFromPress(nowOn);
         }
 
-        public boolean onLongPress() {
-            return false;
-        }
-
         public boolean isEnabled() {
             return !mState.inTransition();
         }
@@ -862,10 +859,6 @@
         public void onPress() {
         }
 
-        public boolean onLongPress() {
-            return false;
-        }
-
         public boolean showDuringKeyguard() {
             return true;
         }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index c483836..30282dd 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -1914,9 +1914,8 @@
 
     @Override
     public Animation createForceHideEnterAnimation(boolean onWallpaper) {
-        return AnimationUtils.loadAnimation(mContext, onWallpaper
-                ? com.android.internal.R.anim.lock_screen_wallpaper_behind_enter
-                : com.android.internal.R.anim.lock_screen_behind_enter);
+        return AnimationUtils.loadAnimation(mContext,
+                com.android.internal.R.anim.lock_screen_behind_enter);
     }
 
     private static void awakenDreams() {
@@ -4571,14 +4570,9 @@
     }
 
     @Override
-    public void startKeyguardExitAnimation(final long fadeoutDuration) {
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
         if (mKeyguardDelegate != null) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mKeyguardDelegate.startKeyguardExitAnimation(fadeoutDuration);
-                }
-            });
+            mKeyguardDelegate.startKeyguardExitAnimation(startTime, fadeoutDuration);
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
index faf7020..63a5850 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
@@ -274,9 +274,9 @@
         mKeyguardState.currentUser = newUserId;
     }
 
-    public void startKeyguardExitAnimation(long fadeoutDuration) {
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
         if (mKeyguardService != null) {
-            mKeyguardService.startKeyguardExitAnimation(fadeoutDuration);
+            mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
index f236ce7..5096bd3 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
@@ -190,9 +190,9 @@
         }
     }
 
-    public void startKeyguardExitAnimation(long fadeoutDuration) {
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
         try {
-            mService.startKeyguardExitAnimation(fadeoutDuration);
+            mService.startKeyguardExitAnimation(startTime, fadeoutDuration);
         } catch (RemoteException e) {
             Slog.w(TAG , "Remote Exception", e);
         }
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index d5f045e..d31fb60 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2372,6 +2372,18 @@
             }
         }
 
+        voldPath = maybeTranslatePathForVold(appPath,
+                userEnv.buildExternalStorageAppMediaDirs(callingPkg),
+                userEnv.buildExternalStorageAppMediaDirsForVold(callingPkg));
+        if (voldPath != null) {
+            try {
+                mConnector.execute("volume", "mkdirs", voldPath);
+                return 0;
+            } catch (NativeDaemonConnectorException e) {
+                return e.getCode();
+            }
+        }
+
         throw new SecurityException("Invalid mkdirs path: " + appPath);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 6fdd535..008d2fc 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -284,7 +284,7 @@
                     } else if (mKeyguardGoingAway && !nowAnimating) {
                         // Timeout!!
                         Slog.e(TAG, "Timeout waiting for animation to startup");
-                        mPolicy.startKeyguardExitAnimation(0);
+                        mPolicy.startKeyguardExitAnimation(0, 0);
                         mKeyguardGoingAway = false;
                     }
                     if (win.isReadyForDisplay()) {
@@ -392,7 +392,9 @@
                     winAnimator.mAnimationIsEntrance = true;
                     if (startKeyguardExit) {
                         // Do one time only.
-                        mPolicy.startKeyguardExitAnimation(a.getStartOffset());
+                        mPolicy.startKeyguardExitAnimation(mCurrentTime + a.getStartOffset(),
+                                a.getDuration());
+                        mKeyguardGoingAway = false;
                         startKeyguardExit = false;
                     }
                 }
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index c162bf28..a54936b 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -608,4 +608,9 @@
     public File[] getExternalCacheDirs() {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public File[] getExternalMediaDirs() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/tests/VoiceInteraction/res/layout/test_interaction.xml b/tests/VoiceInteraction/res/layout/test_interaction.xml
index 2abf65194..4c0c67a 100644
--- a/tests/VoiceInteraction/res/layout/test_interaction.xml
+++ b/tests/VoiceInteraction/res/layout/test_interaction.xml
@@ -18,6 +18,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
+    android:padding="8dp"
     >
 
     <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
@@ -29,9 +30,16 @@
         android:layout_width="match_parent"
         android:layout_height="0px"
         android:layout_weight="1"
-        android:layout_marginTop="10dp"
-        android:textSize="12sp"
+        android:layout_marginTop="16dp"
+        android:textAppearance="?android:attr/textAppearanceMedium"
         android:textColor="#ffffffff"
         />
 
+    <Button android:id="@+id/abort"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:text="@string/abortVoice"
+        />
+
 </LinearLayout>
diff --git a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
index 563fa44..142d781 100644
--- a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
+++ b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
@@ -14,26 +14,49 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:background="#ffffffff"
-    android:fitsSystemWindows="true"
-    >
+    android:fitsSystemWindows="true">
 
-    <TextView android:id="@+id/text"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="32dp"
-        />
+    <FrameLayout android:layout_width="fill_parent"
+        android:layout_height="match_parent"
+        android:padding="8dp">
 
-    <Button android:id="@+id/start"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/start"
-        />
+        <LinearLayout android:id="@+id/content"
+            android:layout_width="fill_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical"
+            android:background="#ffffffff"
+            android:elevation="8dp"
+            >
 
-</LinearLayout>
+            <TextView android:id="@+id/text"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="16dp"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                />
 
+            <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
+                    android:orientation="horizontal">
+                <Button android:id="@+id/start"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/start"
+                    />
+                <Button android:id="@+id/confirm"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/confirm"
+                    />
+                <Button android:id="@+id/abort"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/abort"
+                    />
+            </LinearLayout>
 
+        </LinearLayout>
+    </FrameLayout>
+</FrameLayout>
diff --git a/tests/VoiceInteraction/res/values/strings.xml b/tests/VoiceInteraction/res/values/strings.xml
index 12edb31..70baa52 100644
--- a/tests/VoiceInteraction/res/values/strings.xml
+++ b/tests/VoiceInteraction/res/values/strings.xml
@@ -16,7 +16,10 @@
 
 <resources>
 
-    <string name="start">Start!</string>
+    <string name="start">Start</string>
+    <string name="confirm">Confirm</string>
+    <string name="abort">Abort</string>
+    <string name="abortVoice">Abort Voice</string>
 
 </resources>
 
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index a3af284..c24a088 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -33,9 +33,17 @@
     View mContentView;
     TextView mText;
     Button mStartButton;
+    Button mConfirmButton;
+    Button mAbortButton;
 
+    static final int STATE_IDLE = 0;
+    static final int STATE_LAUNCHING = 1;
+    static final int STATE_CONFIRM = 2;
+    static final int STATE_COMMAND = 3;
+    static final int STATE_ABORT_VOICE = 4;
+
+    int mState = STATE_IDLE;
     Request mPendingRequest;
-    boolean mPendingConfirm;
 
     MainInteractionSession(Context context) {
         super(context);
@@ -54,21 +62,39 @@
         mText = (TextView)mContentView.findViewById(R.id.text);
         mStartButton = (Button)mContentView.findViewById(R.id.start);
         mStartButton.setOnClickListener(this);
+        mConfirmButton = (Button)mContentView.findViewById(R.id.confirm);
+        mConfirmButton.setOnClickListener(this);
+        mAbortButton = (Button)mContentView.findViewById(R.id.abort);
+        mAbortButton.setOnClickListener(this);
+        updateState();
         return mContentView;
     }
 
+    void updateState() {
+        mStartButton.setEnabled(mState == STATE_IDLE);
+        mConfirmButton.setEnabled(mState == STATE_CONFIRM || mState == STATE_COMMAND);
+        mAbortButton.setEnabled(mState == STATE_ABORT_VOICE);
+    }
+
     public void onClick(View v) {
-        if (mPendingRequest == null) {
-            mStartButton.setEnabled(false);
+        if (v == mStartButton) {
+            mState = STATE_LAUNCHING;
+            updateState();
             startVoiceActivity(mStartIntent);
-        } else {
-            if (mPendingConfirm) {
+        } else if (v == mConfirmButton) {
+            if (mState == STATE_CONFIRM) {
                 mPendingRequest.sendConfirmResult(true, null);
             } else {
                 mPendingRequest.sendCommandResult(true, null);
             }
             mPendingRequest = null;
-            mStartButton.setText("Start");
+            mState = STATE_IDLE;
+            updateState();
+        } else if (v == mAbortButton) {
+            mPendingRequest.sendAbortVoiceResult(null);
+            mPendingRequest = null;
+            mState = STATE_IDLE;
+            updateState();
         }
     }
 
@@ -78,23 +104,32 @@
     }
 
     @Override
-    public void onConfirm(Caller caller, Request request, String prompt, Bundle extras) {
+    public void onConfirm(Caller caller, Request request, CharSequence prompt, Bundle extras) {
         Log.i(TAG, "onConfirm: prompt=" + prompt + " extras=" + extras);
         mText.setText(prompt);
-        mStartButton.setEnabled(true);
         mStartButton.setText("Confirm");
         mPendingRequest = request;
-        mPendingConfirm = true;
+        mState = STATE_CONFIRM;
+        updateState();
+    }
+
+    @Override
+    public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) {
+        Log.i(TAG, "onAbortVoice: message=" + message + " extras=" + extras);
+        mText.setText(message);
+        mPendingRequest = request;
+        mState = STATE_ABORT_VOICE;
+        updateState();
     }
 
     @Override
     public void onCommand(Caller caller, Request request, String command, Bundle extras) {
         Log.i(TAG, "onCommand: command=" + command + " extras=" + extras);
         mText.setText("Command: " + command);
-        mStartButton.setEnabled(true);
         mStartButton.setText("Finish Command");
         mPendingRequest = request;
-        mPendingConfirm = false;
+        mState = STATE_COMMAND;
+        updateState();
     }
 
     @Override
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
index a61e0da..3ae6a36 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -21,12 +21,15 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.view.Gravity;
+import android.view.View;
 import android.view.ViewGroup;
+import android.widget.Button;
 
-public class TestInteractionActivity extends Activity {
+public class TestInteractionActivity extends Activity implements View.OnClickListener {
     static final String TAG = "TestInteractionActivity";
 
     VoiceInteractor mInteractor;
+    Button mAbortButton;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -39,6 +42,8 @@
         }
 
         setContentView(R.layout.test_interaction);
+        mAbortButton = (Button)findViewById(R.id.abort);
+        mAbortButton.setOnClickListener(this);
 
         // Framework should take care of these.
         getWindow().setGravity(Gravity.TOP);
@@ -69,6 +74,26 @@
     }
 
     @Override
+    public void onClick(View v) {
+        if (v == mAbortButton) {
+            VoiceInteractor.AbortVoiceRequest req = new VoiceInteractor.AbortVoiceRequest(
+                    "Dammit, we suck :(", null) {
+                @Override
+                public void onCancel() {
+                    Log.i(TAG, "Canceled!");
+                }
+
+                @Override
+                public void onAbortResult(Bundle result) {
+                    Log.i(TAG, "Abort result: result=" + result);
+                    getActivity().finish();
+                }
+            };
+            mInteractor.submitRequest(req);
+        }
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index d31239b..5c51c63 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1466,4 +1466,10 @@
         // pass
         return new File[0];
     }
+
+    @Override
+    public File[] getExternalMediaDirs() {
+        // pass
+        return new File[0];
+    }
 }