Merge "Accessibility node provider getting invalid virtual view id." into lmp-dev
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index b9507d7..68926d0 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -633,7 +633,11 @@
         @Override
         public void onDrained() {
             if (VERBOSE) Log.v(TAG, mIdString + "onIdleDrained");
-            synchronized (CameraCaptureSessionImpl.this) {
+
+            // Take device lock before session lock so that we can call back into device
+            // without causing a deadlock
+            synchronized (mDeviceImpl.mInterfaceLock) {
+                synchronized (CameraCaptureSessionImpl.this) {
                 /*
                  * The device is now IDLE, and has settled. It will not transition to
                  * ACTIVE or BUSY again by itself.
@@ -642,28 +646,31 @@
                  *
                  * This operation is idempotent; a session will not be closed twice.
                  */
-                if (VERBOSE) Log.v(TAG, mIdString + "Session drain complete, skip unconfigure: " +
-                        mSkipUnconfigure);
+                    if (VERBOSE)
+                        Log.v(TAG, mIdString + "Session drain complete, skip unconfigure: " +
+                                mSkipUnconfigure);
 
-                // Fast path: A new capture session has replaced this one; don't unconfigure.
-                if (mSkipUnconfigure) {
-                    mStateCallback.onClosed(CameraCaptureSessionImpl.this);
-                    return;
+                    // Fast path: A new capture session has replaced this one; don't unconfigure.
+                    if (mSkipUnconfigure) {
+                        mStateCallback.onClosed(CameraCaptureSessionImpl.this);
+                        return;
+                    }
+
+                    // Slow path: #close was called explicitly on this session; unconfigure first
+
+                    try {
+                        mUnconfigureDrainer.taskStarted();
+                        mDeviceImpl
+                                .configureOutputsChecked(null); // begin transition to unconfigured
+                    } catch (CameraAccessException e) {
+                        // OK: do not throw checked exceptions.
+                        Log.e(TAG, mIdString + "Exception while configuring outputs: ", e);
+
+                        // TODO: call onError instead of onClosed if this happens
+                    }
+
+                    mUnconfigureDrainer.beginDrain();
                 }
-
-                // Slow path: #close was called explicitly on this session; unconfigure first
-
-                try {
-                    mUnconfigureDrainer.taskStarted();
-                    mDeviceImpl.configureOutputsChecked(null); // begin transition to unconfigured
-                } catch (CameraAccessException e) {
-                    // OK: do not throw checked exceptions.
-                    Log.e(TAG, mIdString + "Exception while configuring outputs: ", e);
-
-                    // TODO: call onError instead of onClosed if this happens
-                }
-
-                mUnconfigureDrainer.beginDrain();
             }
         }
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 2578093..ec450bd1 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -60,7 +60,7 @@
     private ICameraDeviceUser mRemoteDevice;
 
     // Lock to synchronize cross-thread access to device public interface
-    private final Object mInterfaceLock = new Object();
+    final Object mInterfaceLock = new Object(); // access from this class and Session only!
     private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
 
     private final StateCallback mDeviceCallback;
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index e96c15f7..89e2d98 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -73,6 +73,7 @@
         void onError(int errorCode, RequestHolder holder);
         void onConfiguring();
         void onIdle();
+        void onBusy();
         void onCaptureStarted(RequestHolder holder, long timestamp);
         void onCaptureResult(CameraMetadataNative result, RequestHolder holder);
     }
@@ -217,6 +218,20 @@
             }
             Log.i(TAG, "Legacy camera service transitioning to state " + stateName);
         }
+
+        // If we transitioned into a non-IDLE/non-ERROR state then mark the device as busy
+        if(newState != STATE_ERROR && newState != STATE_IDLE) {
+            if (mCurrentState != newState && mCurrentHandler != null &&
+                    mCurrentListener != null) {
+                mCurrentHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mCurrentListener.onBusy();
+                    }
+                });
+            }
+        }
+
         switch(newState) {
             case STATE_ERROR:
                 if (mCurrentState != STATE_ERROR && mCurrentHandler != null &&
diff --git a/core/java/android/hardware/camera2/legacy/GLThreadManager.java b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
index c8e0147..64c532b 100644
--- a/core/java/android/hardware/camera2/legacy/GLThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
@@ -113,6 +113,9 @@
                     case MSG_ALLOW_FRAMES:
                         mDroppingFrames = false;
                         break;
+                    case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
+                        // OK: Ignore message.
+                        break;
                     default:
                         Log.e(TAG, "Unhandled message " + msg.what + " on GLThread.");
                         break;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 4587c6f..3a976ba 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -21,6 +21,7 @@
 import android.hardware.Camera;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.impl.CameraDeviceImpl;
 import android.hardware.camera2.impl.CaptureResultExtras;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.params.StreamConfiguration;
@@ -95,7 +96,25 @@
             new CameraDeviceState.CameraDeviceStateListener() {
         @Override
         public void onError(final int errorCode, final RequestHolder holder) {
-            mIdle.open();
+            if (DEBUG) {
+                Log.d(TAG, "onError called, errorCode = " + errorCode);
+            }
+            switch (errorCode) {
+                /*
+                 * Only be considered idle if we hit a fatal error
+                 * and no further requests can be processed.
+                 */
+                case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED:
+                case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_SERVICE:
+                case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE: {
+                    mIdle.open();
+
+                    if (DEBUG) {
+                        Log.d(TAG, "onError - opening idle");
+                    }
+                }
+            }
+
             final CaptureResultExtras extras = getExtrasFromRequest(holder);
             mResultHandler.post(new Runnable() {
                 @Override
@@ -124,6 +143,10 @@
 
         @Override
         public void onIdle() {
+            if (DEBUG) {
+                Log.d(TAG, "onIdle called");
+            }
+
             mIdle.open();
 
             mResultHandler.post(new Runnable() {
@@ -143,6 +166,15 @@
         }
 
         @Override
+        public void onBusy() {
+            mIdle.close();
+
+            if (DEBUG) {
+                Log.d(TAG, "onBusy called");
+            }
+        }
+
+        @Override
         public void onCaptureStarted(final RequestHolder holder, final long timestamp) {
             final CaptureResultExtras extras = getExtrasFromRequest(holder);
 
diff --git a/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java b/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
index 36cd907..0699ffb 100644
--- a/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
+++ b/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
@@ -23,6 +23,15 @@
 import android.os.MessageQueue;
 
 public class RequestHandlerThread extends HandlerThread {
+
+    /**
+     * Ensure that the MessageQueue's idle handler gets run by poking the message queue;
+     * normally if the message queue is already idle, the idle handler won't get invoked.
+     *
+     * <p>Users of this handler thread should ignore this message.</p>
+     */
+    public final static int MSG_POKE_IDLE_HANDLER = -1;
+
     private final ConditionVariable mStarted = new ConditionVariable(false);
     private final ConditionVariable mIdle = new ConditionVariable(true);
     private Handler.Callback mCallback;
@@ -86,12 +95,15 @@
 
     // Blocks until thread is idling
     public void waitUntilIdle() {
-        Looper looper = waitAndGetHandler().getLooper();
+        Handler handler = waitAndGetHandler();
+        Looper looper = handler.getLooper();
         if (looper.isIdling()) {
             return;
         }
         mIdle.close();
         looper.getQueue().addIdleHandler(mIdleHandler);
+        // Ensure that the idle handler gets run even if the looper already went idle
+        handler.sendEmptyMessage(MSG_POKE_IDLE_HANDLER);
         if (looper.isIdling()) {
             return;
         }
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index a7ea89c..17ce248 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -864,6 +864,9 @@
                     }
                     resetJpegSurfaceFormats(mCallbackOutputs);
                     break;
+                case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
+                    // OK: Ignore message.
+                    break;
                 default:
                     throw new AssertionError("Unhandled message " + msg.what +
                             " on RequestThread.");
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4cdafe1..2785ee8 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -665,4 +665,12 @@
     public static final int CRYPT_TYPE_PATTERN = 2;
     /** @hide */
     public static final int CRYPT_TYPE_PIN = 3;
+
+    // Constants for the data available via MountService.getField.
+    /** @hide */
+    public static final String SYSTEM_LOCALE_KEY = "SystemLocale";
+    /** @hide */
+    public static final String OWNER_INFO_KEY = "OwnerInfo";
+    /** @hide */
+    public static final String PATTERN_VISIBLE_KEY = "PatternVisible";
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 85b58aa..16fa88e 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -604,7 +604,7 @@
         IMountService mountService = IMountService.Stub.asInterface(service);
         try {
             Log.d(TAG, "Setting owner info");
-            mountService.setField("OwnerInfo", ownerInfo);
+            mountService.setField(StorageManager.OWNER_INFO_KEY, ownerInfo);
         } catch (RemoteException e) {
             Log.e(TAG, "Error changing user info", e);
         }
@@ -1144,7 +1144,7 @@
 
         IMountService mountService = IMountService.Stub.asInterface(service);
         try {
-            mountService.setField("PatternVisible", enabled ? "1" : "0");
+            mountService.setField(StorageManager.PATTERN_VISIBLE_KEY, enabled ? "1" : "0");
         } catch (RemoteException e) {
             Log.e(TAG, "Error changing pattern visible state", e);
         }
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 95c2d61..bb59e5b 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -38,4 +38,7 @@
 
     // This is for the system volume UI only
     void setRemoteVolumeController(in IRemoteVolumeController rvc);
+
+    // For PhoneWindowManager to precheck media keys
+    boolean isGlobalPriorityActive();
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 5ce7f9f..b37ee6e 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -232,6 +232,10 @@
         }
     }
 
+    public boolean isGlobalPriorityActive() {
+        return mSessionManager.isGlobalPriorityActive();
+    }
+
     public void addRccListener(PendingIntent pi, MediaSession.Callback listener) {
         if (pi == null) {
             Log.w(TAG, "Pending intent was null, can't add rcc listener.");
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 185c6d8..b4fff8f 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -296,6 +296,21 @@
     }
 
     /**
+     * Check if the global priority session is currently active. This can be
+     * used to decide if media keys should be sent to the session or to the app.
+     *
+     * @hide
+     */
+    public boolean isGlobalPriorityActive() {
+        try {
+            return mService.isGlobalPriorityActive();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to check if the global priority is active.", e);
+        }
+        return false;
+    }
+
+    /**
      * Listens for changes to the list of active sessions. This can be added
      * using {@link #addOnActiveSessionsChangedListener}.
      */
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 6ea4497..f7ed364 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -4248,8 +4248,8 @@
         boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
                 || event.isWakeKey();
         if (interactive || (isInjected && !isWakeKey)) {
-            // When the device is interactive or the key is injected pass the key to the
-            // application.
+            // When the device is interactive or the key is injected pass the
+            // key to the application.
             result = ACTION_PASS_TO_USER;
             isWakeKey = false;
         } else if (!interactive && shouldDispatchInputWhenNonInteractive()) {
@@ -4449,16 +4449,6 @@
             case KeyEvent.KEYCODE_MEDIA_PLAY:
             case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                if (down) {
-                    TelecomManager telecomManager = getTelecommService();
-                    if (telecomManager != null) {
-                        if (telecomManager.isInCall()) {
-                            // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
-                            // to avoid music playback.
-                            break;
-                        }
-                    }
-                }
             case KeyEvent.KEYCODE_HEADSETHOOK:
             case KeyEvent.KEYCODE_MUTE:
             case KeyEvent.KEYCODE_MEDIA_STOP:
@@ -4468,6 +4458,11 @@
             case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
             case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
+                if (MediaSessionLegacyHelper.getHelper(mContext).isGlobalPriorityActive()) {
+                    // If the global session is active pass all media keys to it
+                    // instead of the active window.
+                    result &= ~ACTION_PASS_TO_USER;
+                }
                 if ((result & ACTION_PASS_TO_USER) == 0) {
                     // Only do this if we would otherwise not pass it to the user. In that
                     // case, the PhoneWindow class will do the same thing, except it will
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index ea24d7c..7f24d07 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import android.Manifest;
+import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -28,6 +29,7 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.res.Configuration;
 import android.content.res.ObbInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -94,6 +96,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -821,6 +824,10 @@
                  */
                 mConnectedSignal.countDown();
 
+                // On an encrypted device we can't see system properties yet, so pull
+                // the system locale out of the mount service.
+                copyLocaleFromMountService();
+
                 // Let package manager load internal ASECs.
                 mPms.scanAvailableAsecs();
 
@@ -830,6 +837,28 @@
         }.start();
     }
 
+    private void copyLocaleFromMountService() {
+        String systemLocale;
+        try {
+            systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
+        } catch (RemoteException e) {
+            return;
+        }
+        if (TextUtils.isEmpty(systemLocale)) {
+            return;
+        }
+
+        Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
+        Locale locale = Locale.forLanguageTag(systemLocale);
+        Configuration config = new Configuration();
+        config.setLocale(locale);
+        try {
+            ActivityManagerNative.getDefault().updateConfiguration(config);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error setting system locale from mount service", e);
+        }
+    }
+
     /**
      * Callback from NativeDaemonConnector
      */
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a30775b..d0463b7 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -48,6 +48,8 @@
 import android.graphics.Rect;
 import android.os.BatteryStats;
 import android.os.PersistableBundle;
+import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -1192,6 +1194,7 @@
     static final int ENTER_ANIMATION_COMPLETE_MSG = 44;
     static final int ENABLE_SCREEN_AFTER_BOOT_MSG = 45;
     static final int START_USER_SWITCH_MSG = 46;
+    static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1878,6 +1881,18 @@
                 enableScreenAfterBoot();
                 break;
             }
+            case SEND_LOCALE_TO_MOUNT_DAEMON_MSG: {
+                try {
+                    Locale l = (Locale) msg.obj;
+                    IBinder service = ServiceManager.getService("mount");
+                    IMountService mountService = IMountService.Stub.asInterface(service);
+                    Log.d(TAG, "Storing locale " + l.toLanguageTag() + " for decryption UI");
+                    mountService.setField(StorageManager.SYSTEM_LOCALE_KEY, l.toLanguageTag());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error storing locale for decryption UI", e);
+                }
+                break;
+            }
             }
         }
     };
@@ -16258,6 +16273,8 @@
             SystemProperties.set("persist.sys.language", l.getLanguage());
             SystemProperties.set("persist.sys.country", l.getCountry());
             SystemProperties.set("persist.sys.localevar", l.getVariant());
+
+            mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG, l));
         }
     }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index aaa29fc..02c9fcb5 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -743,6 +743,11 @@
         }
 
         @Override
+        public boolean isGlobalPriorityActive() {
+            return mPriorityStack.isGlobalPriorityActive();
+        }
+
+        @Override
         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index e464be7..c48a075 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -217,6 +217,10 @@
         return null;
     }
 
+    public boolean isGlobalPriorityActive() {
+        return mGlobalPrioritySession == null ? false : mGlobalPrioritySession.isActive();
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0,
                 UserHandle.USER_ALL);