Merge "Add projection info to RenderNode dumps" into mnc-dev
diff --git a/Android.mk b/Android.mk
index 6bfed8e..68f471a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -343,6 +343,7 @@
 	media/java/android/media/IVolumeController.aidl \
 	media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \
 	media/java/android/media/midi/IMidiDeviceListener.aidl \
+	media/java/android/media/midi/IMidiDeviceOpenCallback.aidl \
 	media/java/android/media/midi/IMidiDeviceServer.aidl \
 	media/java/android/media/midi/IMidiManager.aidl \
 	media/java/android/media/projection/IMediaProjection.aidl \
diff --git a/api/current.txt b/api/current.txt
index 8fbd593..989a732 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28,6 +28,7 @@
     field public static final java.lang.String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
     field public static final java.lang.String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
     field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
+    field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE";
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
     field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
@@ -3761,8 +3762,8 @@
     method public void requestUsageTimeReport(android.app.PendingIntent);
     method public android.os.Bundle toBundle();
     method public void update(android.app.ActivityOptions);
-    field public static final java.lang.String EXTRA_USAGE_REPORT_PACKAGES = "android.package";
-    field public static final java.lang.String EXTRA_USAGE_REPORT_TIME = "android.time";
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.usage_time";
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
   }
 
   public class AlarmManager {
@@ -17360,6 +17361,7 @@
     method public final android.media.midi.MidiDeviceInfo getDeviceInfo();
     method public final android.media.midi.MidiReceiver[] getOutputPortReceivers();
     method public android.os.IBinder onBind(android.content.Intent);
+    method public void onClose();
     method public void onDeviceStatusChanged(android.media.midi.MidiDeviceStatus);
     method public abstract android.media.midi.MidiReceiver[] onGetInputPortReceivers();
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.midi.MidiDeviceService";
diff --git a/api/system-current.txt b/api/system-current.txt
index f7f335d..424da48 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -40,6 +40,7 @@
     field public static final java.lang.String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
     field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
     field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
+    field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE";
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
     field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
@@ -3854,8 +3855,8 @@
     method public void requestUsageTimeReport(android.app.PendingIntent);
     method public android.os.Bundle toBundle();
     method public void update(android.app.ActivityOptions);
-    field public static final java.lang.String EXTRA_USAGE_REPORT_PACKAGES = "android.package";
-    field public static final java.lang.String EXTRA_USAGE_REPORT_TIME = "android.time";
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.usage_time";
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
   }
 
   public class AlarmManager {
@@ -18672,6 +18673,7 @@
     method public final android.media.midi.MidiDeviceInfo getDeviceInfo();
     method public final android.media.midi.MidiReceiver[] getOutputPortReceivers();
     method public android.os.IBinder onBind(android.content.Intent);
+    method public void onClose();
     method public void onDeviceStatusChanged(android.media.midi.MidiDeviceStatus);
     method public abstract android.media.midi.MidiReceiver[] onGetInputPortReceivers();
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.midi.MidiDeviceService";
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 9f23b43..6fb997e 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -41,16 +41,16 @@
 
     /**
      * A long in the extras delivered by {@link #requestUsageTimeReport} that contains
-     * the total time (in ms) the user spent in the app.
+     * the total time (in ms) the user spent in the app flow.
      */
-    public static final String EXTRA_USAGE_REPORT_TIME = "android.time";
+    public static final String EXTRA_USAGE_TIME_REPORT = "android.usage_time";
 
     /**
      * A Bundle in the extras delivered by {@link #requestUsageTimeReport} that contains
      * detailed information about the time spent in each package associated with the app;
      * each key is a package name, whose value is a long containing the time (in ms).
      */
-    public static final String EXTRA_USAGE_REPORT_PACKAGES = "android.package";
+    public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
 
     /**
      * The package name that created the options.
@@ -915,7 +915,7 @@
     /**
      * Ask the the system track that time the user spends in the app being launched, and
      * report it back once done.  The report will be sent to the given receiver, with
-     * the extras {@link #EXTRA_USAGE_REPORT_TIME} and {@link #EXTRA_USAGE_REPORT_PACKAGES}
+     * the extras {@link #EXTRA_USAGE_TIME_REPORT} and {@link #EXTRA_USAGE_TIME_REPORT_PACKAGES}
      * filled in.
      *
      * <p>The time interval tracked is from launching this activity until the user leaves
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 0d00908..10e8a53 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -691,7 +691,7 @@
             @Override
             public MidiManager createService(ContextImpl ctx) {
                 IBinder b = ServiceManager.getService(Context.MIDI_SERVICE);
-                return new MidiManager(ctx, IMidiManager.Stub.asInterface(b));
+                return new MidiManager(IMidiManager.Stub.asInterface(b));
             }});
 
         registerService(Context.RADIO_SERVICE, RadioManager.class,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f32918d..709de9e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1827,6 +1827,11 @@
     <permission android:name="android.permission.BIND_INPUT_METHOD"
         android:protectionLevel="signature" />
 
+    <!-- Must be required by an {@link android.media.midi.MidiDeviceService},
+         to ensure that only the system can bind to it. -->
+    <permission android:name="android.permission.BIND_MIDI_DEVICE_SERVICE"
+        android:protectionLevel="signature" />
+
     <!-- Must be required by an {@link android.accessibilityservice.AccessibilityService},
          to ensure that only the system can bind to it. -->
     <permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 1a05104..367257a 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -231,14 +231,6 @@
         return list(prefix, UID_SELF);
     }
 
-    public String[] saw(String prefix, int uid) {
-        return list(prefix, uid);
-    }
-
-    public String[] saw(String prefix) {
-        return saw(prefix, UID_SELF);
-    }
-
     public boolean reset() {
         try {
             return mBinder.reset() == NO_ERROR;
@@ -328,23 +320,6 @@
         }
     }
 
-    public byte[] getPubkey(String key) {
-        try {
-            return mBinder.get_pubkey(key);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Cannot connect to keystore", e);
-            return null;
-        }
-    }
-
-    public boolean delKey(String key, int uid) {
-        return delete(key, uid);
-    }
-
-    public boolean delKey(String key) {
-        return delKey(key, UID_SELF);
-    }
-
     public byte[] sign(String key, byte[] data) {
         try {
             return mBinder.sign(key, data);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
index fd9bdb8..0e8d03e 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
@@ -56,6 +56,7 @@
     // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
     // doFinal finishes.
     private boolean mEncrypting;
+    private int mKeymasterPurposeOverride = -1;
     private AndroidKeyStoreKey mKey;
     private SecureRandom mRng;
 
@@ -165,6 +166,7 @@
             mKeyStore.abort(operationToken);
         }
         mEncrypting = false;
+        mKeymasterPurposeOverride = -1;
         mKey = null;
         mRng = null;
         mOperationToken = null;
@@ -210,9 +212,16 @@
         byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
                 mRng, getAdditionalEntropyAmountForBegin());
 
+        int purpose;
+        if (mKeymasterPurposeOverride != -1) {
+            purpose = mKeymasterPurposeOverride;
+        } else {
+            purpose = mEncrypting
+                    ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT;
+        }
         OperationResult opResult = mKeyStore.begin(
                 mKey.getAlias(),
-                mEncrypting ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT,
+                purpose,
                 true, // permit aborting this operation if keystore runs out of resources
                 keymasterInputArgs,
                 additionalEntropy);
@@ -346,11 +355,11 @@
         } catch (KeyStoreException e) {
             switch (e.getErrorCode()) {
                 case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH:
-                    throw new IllegalBlockSizeException();
+                    throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
                 case KeymasterDefs.KM_ERROR_INVALID_ARGUMENT:
-                    throw new BadPaddingException();
+                    throw (BadPaddingException) new BadPaddingException().initCause(e);
                 case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
-                    throw new AEADBadTagException();
+                    throw (AEADBadTagException) new AEADBadTagException().initCause(e);
                 default:
                     throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
             }
@@ -437,6 +446,17 @@
     }
 
     /**
+     * Overrides the default purpose/type of the crypto operation.
+     */
+    protected final void setKeymasterPurposeOverride(int keymasterPurpose) {
+        mKeymasterPurposeOverride = keymasterPurpose;
+    }
+
+    protected final int getKeymasterPurposeOverride() {
+        return mKeymasterPurposeOverride;
+    }
+
+    /**
      * Returns {@code true} if this cipher is initialized for encryption, {@code false} if this
      * cipher is initialized for decryption.
      */
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 7b5ca3a..c5ea0f7 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -20,6 +20,8 @@
 import android.security.Credentials;
 import android.security.KeyPairGeneratorSpec;
 import android.security.KeyStore;
+import android.security.keymaster.ExportResult;
+import android.security.keymaster.KeymasterDefs;
 
 import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
 import com.android.org.conscrypt.NativeConstants;
@@ -33,6 +35,7 @@
 import java.security.KeyPairGeneratorSpi;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
+import java.security.ProviderException;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.cert.CertificateEncodingException;
@@ -153,7 +156,18 @@
                 throw new RuntimeException("Can't get key", e);
             }
 
-            final byte[] pubKeyBytes = mKeyStore.getPubkey(privateKeyAlias);
+            ExportResult exportResult =
+                    mKeyStore.exportKey(
+                            privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null);
+            if (exportResult == null) {
+                throw new KeyStoreConnectException();
+            } else if (exportResult.resultCode != KeyStore.NO_ERROR) {
+                throw new ProviderException(
+                        "Failed to obtain public key in X.509 format",
+                        KeyStore.getKeyStoreException(exportResult.resultCode));
+            }
+            final byte[] pubKeyBytes = exportResult.exportData;
+
 
             final PublicKey pubKey;
             try {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
index f70c323..5643caf 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
@@ -60,6 +60,13 @@
         }
 
         @Override
+        protected boolean isEncryptingUsingPrivateKeyPermitted() {
+            // RSA encryption with no padding using private key is is a way to implement raw RSA
+            // signatures. We have to support this.
+            return true;
+        }
+
+        @Override
         protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
 
         @Override
@@ -152,8 +159,11 @@
                             paddedInput.length - bufferedInput.length,
                             bufferedInput.length);
                 } else {
-                    // No need to pad input
-                    paddedInput = bufferedInput;
+                    // RI throws BadPaddingException in this scenario. INVALID_ARGUMENT below will
+                    // be translated into BadPaddingException.
+                    throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_ARGUMENT,
+                            "Message size (" + bufferedInput.length + " bytes) must be smaller than"
+                            + " modulus (" + mModulusSizeBytes + " bytes)");
                 }
                 return mDelegate.doFinal(paddedInput, 0, paddedInput.length);
             }
@@ -412,14 +422,46 @@
         }
 
         if (keystoreKey instanceof PrivateKey) {
-            if ((opmode != Cipher.DECRYPT_MODE) && (opmode != Cipher.UNWRAP_MODE)) {
-                throw new InvalidKeyException("Private key cannot be used with opmode: " + opmode
-                        + ". Only DECRYPT_MODE and UNWRAP_MODE supported");
+            // Private key
+            switch (opmode) {
+                case Cipher.DECRYPT_MODE:
+                case Cipher.UNWRAP_MODE:
+                    // Permitted
+                    break;
+                case Cipher.ENCRYPT_MODE:
+                    if (!isEncryptingUsingPrivateKeyPermitted()) {
+                        throw new InvalidKeyException(
+                                "RSA private keys cannot be used with Cipher.ENCRYPT_MODE"
+                                + ". Only RSA public keys supported for this mode");
+                    }
+                    // JCA doesn't provide a way to generate raw RSA signatures (with arbitrary
+                    // padding). Thus, encrypting with private key is used instead.
+                    setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
+                    break;
+                case Cipher.WRAP_MODE:
+                    throw new InvalidKeyException(
+                            "RSA private keys cannot be used with Cipher.WRAP_MODE"
+                            + ". Only RSA public keys supported for this mode");
+                    // break;
+                default:
+                    throw new InvalidKeyException(
+                            "RSA private keys cannot be used with opmode: " + opmode);
             }
         } else {
-            if ((opmode != Cipher.ENCRYPT_MODE) && (opmode != Cipher.WRAP_MODE)) {
-                throw new InvalidKeyException("Public key cannot be used with opmode: " + opmode
-                        + ". Only ENCRYPT_MODE and WRAP_MODE supported");
+            // Public key
+            switch (opmode) {
+                case Cipher.ENCRYPT_MODE:
+                case Cipher.WRAP_MODE:
+                    // Permitted
+                    return;
+                case Cipher.DECRYPT_MODE:
+                case Cipher.UNWRAP_MODE:
+                    throw new InvalidKeyException("RSA public keys cannot be used with opmode: "
+                            + opmode + ". Only RSA private keys supported for this opmode.");
+                    // break;
+                default:
+                    throw new InvalidKeyException(
+                            "RSA public keys cannot be used with opmode: " + opmode);
             }
         }
 
@@ -438,6 +480,10 @@
         setKey(keystoreKey);
     }
 
+    protected boolean isEncryptingUsingPrivateKeyPermitted() {
+        return false;
+    }
+
     @Override
     protected final void resetAll() {
         mModulusSizeBytes = -1;
@@ -454,6 +500,13 @@
             @NonNull KeymasterArguments keymasterArgs) {
         keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
         keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+        int purposeOverride = getKeymasterPurposeOverride();
+        if ((purposeOverride != -1)
+                && ((purposeOverride == KeymasterDefs.KM_PURPOSE_SIGN)
+                || (purposeOverride == KeymasterDefs.KM_PURPOSE_VERIFY))) {
+            // Keymaster sign/verify requires digest to be specified. For raw sign/verify it's NONE.
+            keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
+        }
     }
 
     @Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 05ddef6..7c9c0cf 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -685,7 +685,7 @@
     }
 
     private Set<String> getUniqueAliases() {
-        final String[] rawAliases = mKeyStore.saw("");
+        final String[] rawAliases = mKeyStore.list("");
         if (rawAliases == null) {
             return new HashSet<String>();
         }
@@ -778,7 +778,7 @@
          * equivalent to the USER_CERTIFICATE prefix for the Android keystore
          * convention.
          */
-        final String[] certAliases = mKeyStore.saw(Credentials.USER_CERTIFICATE);
+        final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE);
         if (certAliases != null) {
             for (String alias : certAliases) {
                 final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
@@ -799,7 +799,7 @@
          * Look at all the TrustedCertificateEntry types. Skip all the
          * PrivateKeyEntry we looked at above.
          */
-        final String[] caAliases = mKeyStore.saw(Credentials.CA_CERTIFICATE);
+        final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE);
         if (certAliases != null) {
             for (String alias : caAliases) {
                 if (nonCaEntries.contains(alias)) {
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index e048ec9..44fb826 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -276,8 +276,8 @@
         assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
     }
 
-    public void testSaw() throws Exception {
-        String[] emptyResult = mKeyStore.saw(TEST_KEYNAME);
+    public void testList() throws Exception {
+        String[] emptyResult = mKeyStore.list(TEST_KEYNAME);
         assertNotNull(emptyResult);
         assertEquals(0, emptyResult.length);
 
@@ -285,26 +285,26 @@
         mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
         mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
 
-        String[] results = mKeyStore.saw(TEST_KEYNAME);
+        String[] results = mKeyStore.list(TEST_KEYNAME);
         assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
                                                TEST_KEYNAME2.substring(TEST_KEYNAME.length()))),
                      new HashSet(Arrays.asList(results)));
     }
 
-    public void testSaw_ungrantedUid_Bluetooth() throws Exception {
-        String[] results1 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID);
+    public void testList_ungrantedUid_Bluetooth() throws Exception {
+        String[] results1 = mKeyStore.list(TEST_KEYNAME, Process.BLUETOOTH_UID);
         assertEquals(0, results1.length);
 
         mKeyStore.onUserPasswordChanged(TEST_PASSWD);
         mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
         mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
 
-        String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID);
+        String[] results2 = mKeyStore.list(TEST_KEYNAME, Process.BLUETOOTH_UID);
         assertEquals(0, results2.length);
     }
 
-    public void testSaw_grantedUid_Wifi() throws Exception {
-        String[] results1 = mKeyStore.saw(TEST_KEYNAME, Process.WIFI_UID);
+    public void testList_grantedUid_Wifi() throws Exception {
+        String[] results1 = mKeyStore.list(TEST_KEYNAME, Process.WIFI_UID);
         assertNotNull(results1);
         assertEquals(0, results1.length);
 
@@ -312,14 +312,14 @@
         mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, Process.WIFI_UID, KeyStore.FLAG_ENCRYPTED);
         mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, Process.WIFI_UID, KeyStore.FLAG_ENCRYPTED);
 
-        String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.WIFI_UID);
+        String[] results2 = mKeyStore.list(TEST_KEYNAME, Process.WIFI_UID);
         assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
                                                TEST_KEYNAME2.substring(TEST_KEYNAME.length()))),
                      new HashSet(Arrays.asList(results2)));
     }
 
-    public void testSaw_grantedUid_Vpn() throws Exception {
-        String[] results1 = mKeyStore.saw(TEST_KEYNAME, Process.VPN_UID);
+    public void testList_grantedUid_Vpn() throws Exception {
+        String[] results1 = mKeyStore.list(TEST_KEYNAME, Process.VPN_UID);
         assertNotNull(results1);
         assertEquals(0, results1.length);
 
@@ -327,7 +327,7 @@
         mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, Process.VPN_UID, KeyStore.FLAG_ENCRYPTED);
         mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, Process.VPN_UID, KeyStore.FLAG_ENCRYPTED);
 
-        String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.VPN_UID);
+        String[] results2 = mKeyStore.list(TEST_KEYNAME, Process.VPN_UID);
         assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
                                                TEST_KEYNAME2.substring(TEST_KEYNAME.length()))),
                      new HashSet(Arrays.asList(results2)));
diff --git a/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java b/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java
index cad4e54..8488acd 100644
--- a/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java
+++ b/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java
@@ -18,6 +18,9 @@
 
 import android.security.Credentials;
 import android.security.KeyPairGeneratorSpec;
+import android.security.KeyStore;
+import android.security.keymaster.ExportResult;
+import android.security.keymaster.KeymasterDefs;
 import android.test.AndroidTestCase;
 
 import java.io.ByteArrayInputStream;
@@ -78,7 +81,7 @@
         assertTrue(mAndroidKeyStore.onUserPasswordChanged("1111"));
         assertTrue(mAndroidKeyStore.isUnlocked());
 
-        String[] aliases = mAndroidKeyStore.saw("");
+        String[] aliases = mAndroidKeyStore.list("");
         assertNotNull(aliases);
         assertEquals(0, aliases.length);
     }
@@ -359,7 +362,10 @@
         final byte[] caCerts = mAndroidKeyStore.get(Credentials.CA_CERTIFICATE + alias);
         assertNull("A list of CA certificates should not exist for the generated entry", caCerts);
 
-        final byte[] pubKeyBytes = mAndroidKeyStore.getPubkey(Credentials.USER_PRIVATE_KEY + alias);
+        ExportResult exportResult = mAndroidKeyStore.exportKey(
+                Credentials.USER_PRIVATE_KEY + alias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null);
+        assertEquals(KeyStore.NO_ERROR, exportResult.resultCode);
+        final byte[] pubKeyBytes = exportResult.exportData;
         assertNotNull("The keystore should return the public key for the generated key",
                 pubKeyBytes);
     }
diff --git a/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java
index 2d4e4a0..336fa40 100644
--- a/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java
+++ b/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java
@@ -24,6 +24,8 @@
 import android.security.Credentials;
 import android.security.KeyStore;
 import android.security.KeyStoreParameter;
+import android.security.keymaster.ExportResult;
+import android.security.keymaster.KeymasterDefs;
 import android.test.AndroidTestCase;
 
 import java.io.ByteArrayInputStream;
@@ -742,7 +744,7 @@
         assertTrue(mAndroidKeyStore.onUserPasswordChanged("1111"));
         assertTrue(mAndroidKeyStore.isUnlocked());
 
-        assertEquals(0, mAndroidKeyStore.saw("").length);
+        assertEquals(0, mAndroidKeyStore.list("").length);
     }
 
     private void assertAliases(final String[] expectedAliases) throws KeyStoreException {
@@ -1932,7 +1934,10 @@
             throw new RuntimeException("Can't get key", e);
         }
 
-        final byte[] pubKeyBytes = keyStore.getPubkey(privateKeyAlias);
+        ExportResult exportResult =
+                keyStore.exportKey(privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null);
+        assertEquals(KeyStore.NO_ERROR, exportResult.resultCode);
+        final byte[] pubKeyBytes = exportResult.exportData;
 
         final PublicKey pubKey;
         try {
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
index a5bfde7..9590595 100644
--- a/libs/hwui/Android.common.mk
+++ b/libs/hwui/Android.common.mk
@@ -118,5 +118,9 @@
 
 # Defaults for ATRACE_TAG and LOG_TAG for libhwui
 LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\"
+LOCAL_CFLAGS += -Wall -Wno-unused-parameter -Wunreachable-code
 
-LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter -Wunreachable-code
+# b/21698669
+ifneq ($(USE_CLANG_PLATFORM_BUILD),true)
+    LOCAL_CFLAGS += -Werror
+endif
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 5b81ac9..7f9d9b9 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -28,7 +28,6 @@
 #define PROFILE_DRAW_DP_PER_MS 7
 
 // Must be NUM_ELEMENTS in size
-static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d;
 static const SkColor THRESHOLD_COLOR = 0xff5faa4d;
 static const SkColor BAR_FAST_ALPHA = 0x8F000000;
 static const SkColor BAR_JANKY_ALPHA = 0xDF000000;
diff --git a/media/java/android/media/midi/IMidiDeviceOpenCallback.aidl b/media/java/android/media/midi/IMidiDeviceOpenCallback.aidl
new file mode 100644
index 0000000..6e3dbbf
--- /dev/null
+++ b/media/java/android/media/midi/IMidiDeviceOpenCallback.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 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.midi;
+
+import android.media.midi.IMidiDeviceServer;
+
+/** @hide */
+oneway interface IMidiDeviceOpenCallback
+{
+    void onDeviceOpened(in IMidiDeviceServer server, IBinder token);
+}
diff --git a/media/java/android/media/midi/IMidiDeviceServer.aidl b/media/java/android/media/midi/IMidiDeviceServer.aidl
index 96d12fd..e30796d 100644
--- a/media/java/android/media/midi/IMidiDeviceServer.aidl
+++ b/media/java/android/media/midi/IMidiDeviceServer.aidl
@@ -25,6 +25,7 @@
     ParcelFileDescriptor openInputPort(IBinder token, int portNumber);
     ParcelFileDescriptor openOutputPort(IBinder token, int portNumber);
     void closePort(IBinder token);
+    void closeDevice();
 
     // connects the input port pfd to the specified output port
     void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);
diff --git a/media/java/android/media/midi/IMidiManager.aidl b/media/java/android/media/midi/IMidiManager.aidl
index fcd4aff..d3c8e0a 100644
--- a/media/java/android/media/midi/IMidiManager.aidl
+++ b/media/java/android/media/midi/IMidiManager.aidl
@@ -16,7 +16,10 @@
 
 package android.media.midi;
 
+import android.bluetooth.BluetoothDevice;
 import android.media.midi.IMidiDeviceListener;
+import android.media.midi.IMidiDeviceOpenCallback;
+import android.media.midi.IMidiDeviceServer;
 import android.media.midi.IMidiDeviceServer;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceStatus;
@@ -29,11 +32,13 @@
     MidiDeviceInfo[] getDevices();
 
     // for device creation & removal notifications
-    void registerListener(IBinder token, in IMidiDeviceListener listener);
-    void unregisterListener(IBinder token, in IMidiDeviceListener listener);
+    void registerListener(IBinder clientToken, in IMidiDeviceListener listener);
+    void unregisterListener(IBinder clientToken, in IMidiDeviceListener listener);
 
-    // for opening built-in MIDI devices
-    IMidiDeviceServer openDevice(IBinder token, in MidiDeviceInfo device);
+    void openDevice(IBinder clientToken, in MidiDeviceInfo device, in IMidiDeviceOpenCallback callback);
+    void openBluetoothDevice(IBinder clientToken, in BluetoothDevice bluetoothDevice,
+            in IMidiDeviceOpenCallback callback);
+    void closeDevice(IBinder clientToken, IBinder deviceToken);
 
     // for registering built-in MIDI devices
     MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts,
@@ -52,5 +57,5 @@
 
     // used by MIDI devices to report their status
     // the token is used by MidiService for death notification
-    void setDeviceStatus(IBinder token, in MidiDeviceStatus status);
+    void setDeviceStatus(in IMidiDeviceServer server, in MidiDeviceStatus status);
 }
diff --git a/media/java/android/media/midi/MidiDevice.java b/media/java/android/media/midi/MidiDevice.java
index 6526adc..7998a92 100644
--- a/media/java/android/media/midi/MidiDevice.java
+++ b/media/java/android/media/midi/MidiDevice.java
@@ -16,8 +16,6 @@
 
 package android.media.midi;
 
-import android.content.Context;
-import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
@@ -40,9 +38,9 @@
 
     private final MidiDeviceInfo mDeviceInfo;
     private final IMidiDeviceServer mDeviceServer;
-    private Context mContext;
-    private ServiceConnection mServiceConnection;
-
+    private final IMidiManager mMidiManager;
+    private final IBinder mClientToken;
+    private final IBinder mDeviceToken;
 
     private final CloseGuard mGuard = CloseGuard.get();
 
@@ -71,16 +69,13 @@
         }
     }
 
-    /* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server) {
-        this(deviceInfo, server, null, null);
-    }
-
     /* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server,
-            Context context, ServiceConnection serviceConnection) {
+            IMidiManager midiManager, IBinder clientToken, IBinder deviceToken) {
         mDeviceInfo = deviceInfo;
         mDeviceServer = server;
-        mContext = context;
-        mServiceConnection = serviceConnection;
+        mMidiManager = midiManager;
+        mClientToken = clientToken;
+        mDeviceToken = deviceToken;
         mGuard.open("close");
     }
 
@@ -170,10 +165,10 @@
     public void close() throws IOException {
         synchronized (mGuard) {
             mGuard.close();
-            if (mContext != null && mServiceConnection != null) {
-                mContext.unbindService(mServiceConnection);
-                mContext = null;
-                mServiceConnection = null;
+            try {
+                mMidiManager.closeDevice(mClientToken, mDeviceToken);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException in closeDevice");
             }
         }
     }
diff --git a/media/java/android/media/midi/MidiDeviceServer.java b/media/java/android/media/midi/MidiDeviceServer.java
index a316a44..1b56e1c 100644
--- a/media/java/android/media/midi/MidiDeviceServer.java
+++ b/media/java/android/media/midi/MidiDeviceServer.java
@@ -65,7 +65,6 @@
 
 
     // for reporting device status
-    private final IBinder mDeviceStatusToken = new Binder();
     private final boolean[] mInputPortOpen;
     private final int[] mOutputPortOpenCount;
 
@@ -81,6 +80,11 @@
          * @param status the {@link MidiDeviceStatus} for the device
          */
         public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status);
+
+        /**
+         * Called to notify when the device is closed
+         */
+        public void onClose();
     }
 
     abstract private class PortClient implements IBinder.DeathRecipient {
@@ -242,6 +246,14 @@
         }
 
         @Override
+        public void closeDevice() {
+            if (mCallback != null) {
+                mCallback.onClose();
+            }
+            IoUtils.closeQuietly(MidiDeviceServer.this);
+        }
+
+        @Override
         public void connectPorts(IBinder token, ParcelFileDescriptor pfd,
                 int outputPortNumber) {
             MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber);
@@ -305,7 +317,7 @@
             mCallback.onDeviceStatusChanged(this, status);
         }
         try {
-            mMidiManager.setDeviceStatus(mDeviceStatusToken, status);
+            mMidiManager.setDeviceStatus(mServer, status);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in updateDeviceStatus");
         } finally {
diff --git a/media/java/android/media/midi/MidiDeviceService.java b/media/java/android/media/midi/MidiDeviceService.java
index ce12a4f..d897ad2 100644
--- a/media/java/android/media/midi/MidiDeviceService.java
+++ b/media/java/android/media/midi/MidiDeviceService.java
@@ -59,6 +59,11 @@
         public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
             MidiDeviceService.this.onDeviceStatusChanged(status);
         }
+
+        @Override
+        public void onClose() {
+            MidiDeviceService.this.onClose();
+        }
     };
 
     @Override
@@ -125,6 +130,12 @@
     public void onDeviceStatusChanged(MidiDeviceStatus status) {
     }
 
+    /**
+     * Called to notify when our device has been closed by all its clients
+     */
+    public void onClose() {
+    }
+
     @Override
     public IBinder onBind(Intent intent) {
         if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index d19cf36..0beb9a4 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -17,11 +17,6 @@
 package android.media.midi;
 
 import android.bluetooth.BluetoothDevice;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ServiceInfo;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Bundle;
@@ -52,16 +47,17 @@
 
     /**
      * BluetoothMidiService package name
+     * @hide
      */
-    private static final String BLUETOOTH_MIDI_SERVICE_PACKAGE = "com.android.bluetoothmidiservice";
+    public static final String BLUETOOTH_MIDI_SERVICE_PACKAGE = "com.android.bluetoothmidiservice";
 
     /**
      * BluetoothMidiService class name
+     * @hide
      */
-    private static final String BLUETOOTH_MIDI_SERVICE_CLASS =
+    public static final String BLUETOOTH_MIDI_SERVICE_CLASS =
                 "com.android.bluetoothmidiservice.BluetoothMidiService";
 
-    private final Context mContext;
     private final IMidiManager mService;
     private final IBinder mToken = new Binder();
 
@@ -166,8 +162,7 @@
     /**
      * @hide
      */
-    public MidiManager(Context context, IMidiManager service) {
-        mContext = context;
+    public MidiManager(IMidiManager service) {
         mService = service;
     }
 
@@ -237,7 +232,7 @@
      * Opens a MIDI device for reading and writing.
      *
      * @param deviceInfo a {@link android.media.midi.MidiDeviceInfo} to open
-     * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called 
+     * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called
      *                 to receive the result
      * @param handler the {@link android.os.Handler Handler} that will be used for delivering
      *                the result. If handler is null, then the thread used for the
@@ -245,52 +240,28 @@
      */
     public void openDevice(MidiDeviceInfo deviceInfo, OnDeviceOpenedListener listener,
             Handler handler) {
-        MidiDevice device = null;
-        try {
-            IMidiDeviceServer server = mService.openDevice(mToken, deviceInfo);
-            if (server == null) {
-                ServiceInfo serviceInfo = (ServiceInfo)deviceInfo.getProperties().getParcelable(
-                        MidiDeviceInfo.PROPERTY_SERVICE_INFO);
-                if (serviceInfo == null) {
-                    Log.e(TAG, "no ServiceInfo for " + deviceInfo);
-                } else {
-                    Intent intent = new Intent(MidiDeviceService.SERVICE_INTERFACE);
-                    intent.setComponent(new ComponentName(serviceInfo.packageName,
-                            serviceInfo.name));
-                    final MidiDeviceInfo deviceInfoF = deviceInfo;
-                    final OnDeviceOpenedListener listenerF = listener;
-                    final Handler handlerF = handler;
-                    if (mContext.bindService(intent,
-                        new ServiceConnection() {
-                            @Override
-                            public void onServiceConnected(ComponentName name, IBinder binder) {
-                                IMidiDeviceServer server =
-                                        IMidiDeviceServer.Stub.asInterface(binder);
-                                MidiDevice device = new MidiDevice(deviceInfoF, server, mContext,
-                                        this);
-                                sendOpenDeviceResponse(device, listenerF, handlerF);
-                            }
+        final MidiDeviceInfo deviceInfoF = deviceInfo;
+        final OnDeviceOpenedListener listenerF = listener;
+        final Handler handlerF = handler;
 
-                            @Override
-                            public void onServiceDisconnected(ComponentName name) {
-                                // FIXME - anything to do here?
-                            }
-                        },
-                        Context.BIND_AUTO_CREATE))
-                    {
-                        // return immediately to avoid calling sendOpenDeviceResponse below
-                        return;
-                    } else {
-                        Log.e(TAG, "Unable to bind service: " + intent);
-                    }
+        IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
+            @Override
+            public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
+                MidiDevice device;
+                if (server != null) {
+                    device = new MidiDevice(deviceInfoF, server, mService, mToken, deviceToken);
+                } else {
+                    device = null;
                 }
-            } else {
-                device = new MidiDevice(deviceInfo, server);
+                sendOpenDeviceResponse(device, listenerF, handlerF);
             }
+        };
+
+        try {
+            mService.openDevice(mToken, deviceInfo, callback);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in openDevice");
         }
-        sendOpenDeviceResponse(device, listener, handler);
     }
 
     /**
@@ -303,38 +274,33 @@
      *                the result. If handler is null, then the thread used for the
      *                listener is unspecified.
      */
-    public void openBluetoothDevice(final BluetoothDevice bluetoothDevice,
-            final OnDeviceOpenedListener listener, final Handler handler) {
-        Intent intent = new Intent(BLUETOOTH_MIDI_SERVICE_INTENT);
-        intent.setComponent(new ComponentName(BLUETOOTH_MIDI_SERVICE_PACKAGE,
-                BLUETOOTH_MIDI_SERVICE_CLASS));
-        intent.putExtra("device", bluetoothDevice);
-        if (!mContext.bindService(intent,
-            new ServiceConnection() {
-                @Override
-                public void onServiceConnected(ComponentName name, IBinder binder) {
-                    IMidiDeviceServer server =
-                            IMidiDeviceServer.Stub.asInterface(binder);
+    public void openBluetoothDevice(BluetoothDevice bluetoothDevice,
+            OnDeviceOpenedListener listener, Handler handler) {
+        final OnDeviceOpenedListener listenerF = listener;
+        final Handler handlerF = handler;
+
+        IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
+            @Override
+            public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
+                MidiDevice device = null;
+                if (server != null) {
                     try {
                         // fetch MidiDeviceInfo from the server
                         MidiDeviceInfo deviceInfo = server.getDeviceInfo();
-                        MidiDevice device = new MidiDevice(deviceInfo, server, mContext, this);
-                        sendOpenDeviceResponse(device, listener, handler);
+                        device = new MidiDevice(deviceInfo, server, mService, mToken, deviceToken);
+                        sendOpenDeviceResponse(device, listenerF, handlerF);
                     } catch (RemoteException e) {
-                        Log.e(TAG, "remote exception in onServiceConnected");
-                        sendOpenDeviceResponse(null, listener, handler);
+                        Log.e(TAG, "remote exception in getDeviceInfo()");
                     }
                 }
+                sendOpenDeviceResponse(device, listenerF, handlerF);
+            }
+        };
 
-                @Override
-                public void onServiceDisconnected(ComponentName name) {
-                    // FIXME - anything to do here?
-                }
-            },
-            Context.BIND_AUTO_CREATE))
-        {
-            Log.e(TAG, "Unable to bind service: " + intent);
-            sendOpenDeviceResponse(null, listener, handler);
+        try {
+            mService.openBluetoothDevice(mToken, bluetoothDevice, callback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in openDevice");
         }
     }
 
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index bd589a9..8b2bd16 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -241,7 +241,8 @@
 
 
 <p>An app can provide a MIDI Service that can be used by other apps. For example,
-an app can provide a custom synthesizer that other apps can send messages to. </p>
+an app can provide a custom synthesizer that other apps can send messages to.
+The service must be guarded with permission &quot;android.permission.BIND_MIDI_DEVICE_SERVICE&quot;.</p>
 
 <h2 id=manifest_files>Manifest Files</h2>
 
@@ -250,7 +251,8 @@
 AndroidManifest.xml file.</p>
 
 <pre class=prettyprint>
-&lt;service android:name="<strong>MySynthDeviceService</strong>">
+&lt;service android:name="<strong>MySynthDeviceService</strong>"
+  android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
   &lt;intent-filter>
     &lt;action android:name="android.media.midi.MidiDeviceService" />
   &lt;/intent-filter>
diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml
index 15aa581..b0b389a 100644
--- a/media/packages/BluetoothMidiService/AndroidManifest.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifest.xml
@@ -8,7 +8,8 @@
 
     <application
         android:label="@string/app_name">
-        <service android:name="BluetoothMidiService">
+        <service android:name="BluetoothMidiService"
+            android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
             <intent-filter>
                 <action android:name="android.media.midi.BluetoothMidiService" />
             </intent-filter>
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 60c6570..e6d59e4 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -24,10 +24,11 @@
 import android.bluetooth.BluetoothGattService;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
-import android.media.midi.MidiReceiver;
-import android.media.midi.MidiManager;
-import android.media.midi.MidiDeviceServer;
 import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceServer;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiReceiver;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.util.Log;
@@ -81,6 +82,18 @@
     private final BluetoothPacketDecoder mPacketDecoder
             = new BluetoothPacketDecoder(MAX_PACKET_SIZE);
 
+    private final MidiDeviceServer.Callback mDeviceServerCallback
+            = new MidiDeviceServer.Callback() {
+        @Override
+        public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
+        }
+
+        @Override
+        public void onClose() {
+            close();
+        }
+    };
+
     private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
         @Override
         public void onConnectionStateChange(BluetoothGatt gatt, int status,
@@ -213,7 +226,7 @@
         inputPortReceivers[0] = mEventScheduler.getReceiver();
 
         mDeviceServer = mMidiManager.createDeviceServer(inputPortReceivers, 1,
-                null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, null);
+                null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, mDeviceServerCallback);
 
         mOutputReceiver = mDeviceServer.getOutputPortReceivers()[0];
 
@@ -248,11 +261,12 @@
 
     private void close() {
         synchronized (mBluetoothDevice) {
-        mEventScheduler.close();
+            mEventScheduler.close();
+            mService.deviceClosed(mBluetoothDevice);
+
             if (mDeviceServer != null) {
                 IoUtils.closeQuietly(mDeviceServer);
                 mDeviceServer = null;
-                mService.deviceClosed(mBluetoothDevice);
             }
             if (mBluetoothGatt != null) {
                 mBluetoothGatt.close();
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index a8451f5..e92f74c 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -40,7 +40,7 @@
     <!-- Label of the color mode widget. [CHAR LIMIT=20] -->
     <string name="label_color">Color</string>
 
-    <!-- Label of the duplex mode widget. [CHAR LIMIT=20] -->
+    <!-- Label of the printer mode to print on both sides of paper. [CHAR LIMIT=20] -->
     <string name="label_duplex">Two-sided</string>
 
     <!-- Label of the orientation widget. [CHAR LIMIT=20] -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 4bc317a..39dc480 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -998,6 +998,9 @@
                 mAfforanceHelper.updatePreviews();
             }
         }
+        if (keyguardShowing) {
+            updateDozingVisibilities(false /* animate */);
+        }
         resetVerticalPanelPosition();
         updateQsState();
     }
@@ -2109,6 +2112,10 @@
     public void setDozing(boolean dozing, boolean animate) {
         if (dozing == mDozing) return;
         mDozing = dozing;
+        updateDozingVisibilities(animate);
+    }
+
+    private void updateDozingVisibilities(boolean animate) {
         if (mDozing) {
             mKeyguardStatusBar.setVisibility(View.INVISIBLE);
             mKeyguardBottomArea.setVisibility(View.INVISIBLE);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b6cc1c7..1046b29 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -10216,6 +10216,7 @@
     void finishRunningVoiceLocked() {
         if (mRunningVoice != null) {
             mRunningVoice = null;
+            mVoiceWakeLock.release();
             updateSleepIfNeededLocked();
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 3765a3c..d16eab6 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -982,7 +982,7 @@
         final long totalTime = stack.mLaunchStartTime != 0
                 ? (curTime - stack.mLaunchStartTime) : thisTime;
         if (SHOW_ACTIVITY_START_TIME) {
-            Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching", 0);
+            Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: " + packageName, 0);
             EventLog.writeEvent(EventLogTags.AM_ACTIVITY_LAUNCH_TIME,
                     userId, System.identityHashCode(this), shortComponentName,
                     thisTime, totalTime);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 30ee214..eb5af9e5 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -679,11 +679,11 @@
                 "Launch completed; removing icicle of " + r.icicle);
     }
 
-    private void startLaunchTraces() {
+    private void startLaunchTraces(String packageName) {
         if (mFullyDrawnStartTime != 0)  {
             Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
         }
-        Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching", 0);
+        Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: " + packageName, 0);
         Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
     }
 
@@ -698,11 +698,11 @@
         if (r.displayStartTime == 0) {
             r.fullyDrawnStartTime = r.displayStartTime = SystemClock.uptimeMillis();
             if (mLaunchStartTime == 0) {
-                startLaunchTraces();
+                startLaunchTraces(r.packageName);
                 mLaunchStartTime = mFullyDrawnStartTime = r.displayStartTime;
             }
         } else if (mLaunchStartTime == 0) {
-            startLaunchTraces();
+            startLaunchTraces(r.packageName);
             mLaunchStartTime = mFullyDrawnStartTime = SystemClock.uptimeMillis();
         }
     }
diff --git a/services/core/java/com/android/server/am/AppTimeTracker.java b/services/core/java/com/android/server/am/AppTimeTracker.java
index bddd66f..910f33d 100644
--- a/services/core/java/com/android/server/am/AppTimeTracker.java
+++ b/services/core/java/com/android/server/am/AppTimeTracker.java
@@ -80,12 +80,12 @@
     public void deliverResult(Context context) {
         stop();
         Bundle extras = new Bundle();
-        extras.putLong(ActivityOptions.EXTRA_USAGE_REPORT_TIME, mTotalTime);
+        extras.putLong(ActivityOptions.EXTRA_USAGE_TIME_REPORT, mTotalTime);
         Bundle pkgs = new Bundle();
         for (int i=mPackageTimes.size()-1; i>=0; i--) {
             pkgs.putLong(mPackageTimes.keyAt(i), mPackageTimes.valueAt(i).value);
         }
-        extras.putBundle(ActivityOptions.EXTRA_USAGE_REPORT_PACKAGES, pkgs);
+        extras.putBundle(ActivityOptions.EXTRA_USAGE_TIME_REPORT_PACKAGES, pkgs);
         Intent fillinIntent = new Intent();
         fillinIntent.putExtras(extras);
         try {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e370afc..604ac97 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5031,7 +5031,7 @@
                 scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                         scanFlags, currentTime, null);
             } catch (PackageManagerException e) {
-                Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage(), e);
+                Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
 
                 // Delete invalid userdata apps
                 if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
@@ -6166,9 +6166,16 @@
                 }
             } else {
                 if (!checkUpgradeKeySetLP(pkgSetting, pkg)) {
-                    throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
-                            + pkg.packageName + " upgrade keys do not match the "
-                            + "previously installed version");
+                    if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+                        throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                                "Package " + pkg.packageName + " upgrade keys do not match the "
+                                + "previously installed version");
+                    } else {
+                        pkgSetting.signatures.mSignatures = pkg.mSignatures;
+                        String msg = "System package " + pkg.packageName
+                            + " signature changed; retaining data.";
+                        reportSettingsProblem(Log.WARN, msg);
+                    }
                 } else {
                     // We just determined the app is signed correctly, so bring
                     // over the latest parsed certs.
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index da4cc48..3b9cc9d 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -62,8 +62,6 @@
 static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jintArray outIrqs,
         jobjectArray outReasons)
 {
-    bool first_time = false;
-
     if (outIrqs == NULL || outReasons == NULL) {
         jniThrowException(env, "java/lang/NullPointerException", "null argument");
         return -1;
@@ -83,19 +81,17 @@
         }
         ALOGV("Registering callback...");
         set_wakeup_callback(&wakeup_callback);
-        // First time through, we will just drain the current wakeup reasons.
-        first_time = true;
-    } else {
-        // On following calls, we need to wait for wakeup.
-        ALOGV("Waiting for wakeup...");
-        int ret = sem_wait(&wakeup_sem);
-        if (ret < 0) {
-            char buf[80];
-            strerror_r(errno, buf, sizeof(buf));
-            ALOGE("Error waiting on semaphore: %s\n", buf);
-            // Return 0 here to let it continue looping but not return results.
-            return 0;
-        }
+    }
+
+    // Wait for wakeup.
+    ALOGV("Waiting for wakeup...");
+    int ret = sem_wait(&wakeup_sem);
+    if (ret < 0) {
+        char buf[80];
+        strerror_r(errno, buf, sizeof(buf));
+        ALOGE("Error waiting on semaphore: %s\n", buf);
+        // Return 0 here to let it continue looping but not return results.
+        return 0;
     }
 
     FILE *fp = fopen(LAST_RESUME_REASON, "r");
@@ -169,9 +165,6 @@
     }
 
     ALOGV("Got %d reasons", i);
-    if (first_time) {
-        i = 0;
-    }
     if (i > 0) {
         irqs[0] = firstirq;
         *mergedreasonpos = 0;
@@ -185,7 +178,7 @@
         return -1;
     }
 
-    return first_time ? 0 : i;
+    return i;
 }
 
 static JNINativeMethod method_table[] = {
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 370f125..d1bbbfc 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -16,8 +16,11 @@
 
 package com.android.server.midi;
 
+import android.bluetooth.BluetoothDevice;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -25,11 +28,13 @@
 import android.content.pm.ServiceInfo;
 import android.content.res.XmlResourceParser;
 import android.media.midi.IMidiDeviceListener;
+import android.media.midi.IMidiDeviceOpenCallback;
 import android.media.midi.IMidiDeviceServer;
 import android.media.midi.IMidiManager;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceService;
 import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiManager;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -78,6 +83,10 @@
     private final HashMap<MidiDeviceInfo, Device> mDevicesByInfo
             = new HashMap<MidiDeviceInfo, Device>();
 
+    // list of all Bluetooth devices, keyed by BluetoothDevice
+     private final HashMap<BluetoothDevice, Device> mBluetoothDevices
+            = new HashMap<BluetoothDevice, Device>();
+
     // list of all devices, keyed by IMidiDeviceServer
     private final HashMap<IBinder, Device> mDevicesByServer = new HashMap<IBinder, Device>();
 
@@ -86,6 +95,9 @@
 
     private final PackageManager mPackageManager;
 
+    // UID of BluetoothMidiService
+    private final int mBluetoothServiceUid;
+
     // PackageMonitor for listening to package changes
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
         @Override
@@ -115,6 +127,9 @@
         // List of all receivers for this client
         private final ArrayList<IMidiDeviceListener> mListeners
                 = new ArrayList<IMidiDeviceListener>();
+        // List of all device connections for this client
+        private final HashMap<IBinder, DeviceConnection> mDeviceConnections
+                = new HashMap<IBinder, DeviceConnection>();
 
         public Client(IBinder token) {
             mToken = token;
@@ -132,8 +147,33 @@
 
         public void removeListener(IMidiDeviceListener listener) {
             mListeners.remove(listener);
-            if (mListeners.size() == 0) {
-                removeClient(mToken);
+            if (mListeners.size() == 0 && mDeviceConnections.size() == 0) {
+                close();
+            }
+        }
+
+        public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback) {
+            DeviceConnection connection = new DeviceConnection(device, this, callback);
+            mDeviceConnections.put(connection.getToken(), connection);
+            device.addDeviceConnection(connection);
+        }
+
+        // called from MidiService.closeDevice()
+        public void removeDeviceConnection(IBinder token) {
+            DeviceConnection connection = mDeviceConnections.remove(token);
+            if (connection != null) {
+                connection.getDevice().removeDeviceConnection(connection);
+            }
+            if (mListeners.size() == 0 && mDeviceConnections.size() == 0) {
+                close();
+            }
+        }
+
+        // called from Device.close()
+        public void removeDeviceConnection(DeviceConnection connection) {
+            mDeviceConnections.remove(connection.getToken());
+            if (mListeners.size() == 0 && mDeviceConnections.size() == 0) {
+                close();
             }
         }
 
@@ -178,8 +218,21 @@
             }
         }
 
+        private void close() {
+            synchronized (mClients) {
+                mClients.remove(mToken);
+                mToken.unlinkToDeath(this, 0);
+            }
+
+            for (DeviceConnection connection : mDeviceConnections.values()) {
+                connection.getDevice().removeDeviceConnection(connection);
+            }
+        }
+
+        @Override
         public void binderDied() {
-            removeClient(mToken);
+            Log.d(TAG, "Client died: " + this);
+            close();
         }
 
         @Override
@@ -190,6 +243,12 @@
             sb.append(mPid);
             sb.append(" listener count: ");
             sb.append(mListeners.size());
+            sb.append(" Device Connections:");
+            for (DeviceConnection connection : mDeviceConnections.values()) {
+                sb.append(" <device ");
+                sb.append(connection.getDevice().getDeviceInfo().getId());
+                sb.append(">");
+            }
             return sb.toString();
         }
     }
@@ -211,57 +270,96 @@
         }
     }
 
-    private void removeClient(IBinder token) {
-        mClients.remove(token);
-    }
-
     private final class Device implements IBinder.DeathRecipient {
-        private final IMidiDeviceServer mServer;
-        private final MidiDeviceInfo mDeviceInfo;
+        private IMidiDeviceServer mServer;
+        private MidiDeviceInfo mDeviceInfo;
+        private final BluetoothDevice mBluetoothDevice;
         private MidiDeviceStatus mDeviceStatus;
-        private IBinder mDeviceStatusToken;
+
         // ServiceInfo for the device's MidiDeviceServer implementation (virtual devices only)
         private final ServiceInfo mServiceInfo;
         // UID of device implementation
         private final int mUid;
 
+        // ServiceConnection for implementing Service (virtual devices only)
+        // mServiceConnection is non-null when connected or attempting to connect to the service
+        private ServiceConnection mServiceConnection;
+
+        // List of all device connections for this device
+        private final ArrayList<DeviceConnection> mDeviceConnections
+                = new ArrayList<DeviceConnection>();
+
         public Device(IMidiDeviceServer server, MidiDeviceInfo deviceInfo,
                 ServiceInfo serviceInfo, int uid) {
-            mServer = server;
             mDeviceInfo = deviceInfo;
             mServiceInfo = serviceInfo;
             mUid = uid;
+            mBluetoothDevice = (BluetoothDevice)deviceInfo.getProperties().getParcelable(
+                    MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE);;
+            setDeviceServer(server);
+        }
+
+        public Device(BluetoothDevice bluetoothDevice) {
+            mBluetoothDevice = bluetoothDevice;
+            mServiceInfo = null;
+            mUid = mBluetoothServiceUid;
+        }
+
+        private void setDeviceServer(IMidiDeviceServer server) {
+            if (server != null) {
+                if (mServer != null) {
+                    Log.e(TAG, "mServer already set in setDeviceServer");
+                    return;
+                }
+                IBinder binder = server.asBinder();
+                try {
+                    if (mDeviceInfo == null) {
+                        mDeviceInfo = server.getDeviceInfo();
+                    }
+                    binder.linkToDeath(this, 0);
+                    mServer = server;
+                } catch (RemoteException e) {
+                    mServer = null;
+                    return;
+                }
+                mDevicesByServer.put(binder, this);
+            } else if (mServer != null) {
+                server = mServer;
+                mServer = null;
+
+                IBinder binder = server.asBinder();
+                mDevicesByServer.remove(binder);
+
+                try {
+                    server.closeDevice();
+                    binder.unlinkToDeath(this, 0);
+                } catch (RemoteException e) {
+                    // nothing to do here
+                }
+            }
+
+            if (mDeviceConnections != null) {
+                for (DeviceConnection connection : mDeviceConnections) {
+                    connection.notifyClient(server);
+                }
+            }
         }
 
         public MidiDeviceInfo getDeviceInfo() {
             return mDeviceInfo;
         }
 
+        // only used for bluetooth devices, which are created before we have a MidiDeviceInfo
+        public void setDeviceInfo(MidiDeviceInfo deviceInfo) {
+            mDeviceInfo = deviceInfo;
+        }
+
         public MidiDeviceStatus getDeviceStatus() {
             return mDeviceStatus;
         }
 
-        public void setDeviceStatus(IBinder token, MidiDeviceStatus status) {
+        public void setDeviceStatus(MidiDeviceStatus status) {
             mDeviceStatus = status;
-
-            if (mDeviceStatusToken == null && token != null) {
-                // register a death recipient so we can clear the status when the device dies
-                try {
-                    token.linkToDeath(new IBinder.DeathRecipient() {
-                        @Override
-                        public void binderDied() {
-                            // reset to default status and clear the token
-                            mDeviceStatus = new MidiDeviceStatus(mDeviceInfo);
-                            mDeviceStatusToken = null;
-                            notifyDeviceStatusChanged(Device.this, mDeviceStatus);
-                        }
-                    }, 0);
-                    mDeviceStatusToken = token;
-                } catch (RemoteException e) {
-                    // reset to default status
-                    mDeviceStatus = new MidiDeviceStatus(mDeviceInfo);
-                }
-            }
         }
 
         public IMidiDeviceServer getDeviceServer() {
@@ -284,14 +382,105 @@
             return (!mDeviceInfo.isPrivate() || mUid == uid);
         }
 
-        public void binderDied() {
-            synchronized (mDevicesByInfo) {
-                if (mDevicesByInfo.remove(mDeviceInfo) != null) {
-                    removeDeviceLocked(this);
+        public void addDeviceConnection(DeviceConnection connection) {
+            synchronized (mDeviceConnections) {
+                if (mServer != null) {
+                    mDeviceConnections.add(connection);
+                    connection.notifyClient(mServer);
+                } else if (mServiceConnection == null &&
+                    (mServiceInfo != null || mBluetoothDevice != null)) {
+                    mDeviceConnections.add(connection);
+
+                    mServiceConnection = new ServiceConnection() {
+                        @Override
+                        public void onServiceConnected(ComponentName name, IBinder service) {
+                            IMidiDeviceServer server = IMidiDeviceServer.Stub.asInterface(service);
+                            setDeviceServer(server);
+                        }
+
+                        @Override
+                        public void onServiceDisconnected(ComponentName name) {
+                            setDeviceServer(null);
+                            mServiceConnection = null;
+                        }
+                    };
+
+                    Intent intent;
+                    if (mBluetoothDevice != null) {
+                        intent = new Intent(MidiManager.BLUETOOTH_MIDI_SERVICE_INTENT);
+                        intent.setComponent(new ComponentName(
+                                MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE,
+                                MidiManager.BLUETOOTH_MIDI_SERVICE_CLASS));
+                        intent.putExtra("device", mBluetoothDevice);
+                    } else {
+                        intent = new Intent(MidiDeviceService.SERVICE_INTERFACE);
+                        intent.setComponent(
+                                new ComponentName(mServiceInfo.packageName, mServiceInfo.name));
+                    }
+
+                    if (!mContext.bindService(intent, mServiceConnection,
+                            Context.BIND_AUTO_CREATE)) {
+                        Log.e(TAG, "Unable to bind service: " + intent);
+                        setDeviceServer(null);
+                        mServiceConnection = null;
+                    }
+                } else {
+                    Log.e(TAG, "No way to connect to device in addDeviceConnection");
+                    connection.notifyClient(null);
                 }
             }
         }
 
+        public void removeDeviceConnection(DeviceConnection connection) {
+            synchronized (mDeviceConnections) {
+                mDeviceConnections.remove(connection);
+
+                if (mDeviceConnections.size() == 0 && mServiceConnection != null) {
+                    mContext.unbindService(mServiceConnection);
+                    mServiceConnection = null;
+                    if (mBluetoothDevice != null) {
+                        // Bluetooth devices are ephemeral - remove when no clients exist
+                        synchronized (mDevicesByInfo) {
+                            closeLocked();
+                        }
+                    } else {
+                        setDeviceServer(null);
+                    }
+                }
+            }
+        }
+
+        // synchronize on mDevicesByInfo
+        public void closeLocked() {
+            synchronized (mDeviceConnections) {
+                for (DeviceConnection connection : mDeviceConnections) {
+                    connection.getClient().removeDeviceConnection(connection);
+                }
+                mDeviceConnections.clear();
+            }
+            setDeviceServer(null);
+
+            // closed virtual devices should not be removed from mDevicesByInfo
+            // since they can be restarted on demand
+            if (mServiceInfo == null) {
+                removeDeviceLocked(this);
+            } else {
+                mDeviceStatus = new MidiDeviceStatus(mDeviceInfo);
+            }
+
+            if (mBluetoothDevice != null) {
+                mBluetoothDevices.remove(mBluetoothDevice);
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            Log.d(TAG, "Device died: " + this);
+            synchronized (mDevicesByInfo) {
+                closeLocked();
+            }
+        }
+
         @Override
         public String toString() {
             StringBuilder sb = new StringBuilder("Device Info: ");
@@ -300,10 +489,56 @@
             sb.append(mDeviceStatus);
             sb.append(" UID: ");
             sb.append(mUid);
+            sb.append(" DeviceConnection count: ");
+            sb.append(mDeviceConnections.size());
+            sb.append(" mServiceConnection: ");
+            sb.append(mServiceConnection);
             return sb.toString();
         }
     }
 
+    // Represents a connection between a client and a device
+    private final class DeviceConnection {
+        private final IBinder mToken = new Binder();
+        private final Device mDevice;
+        private final Client mClient;
+        private IMidiDeviceOpenCallback mCallback;
+
+        public DeviceConnection(Device device, Client client, IMidiDeviceOpenCallback callback) {
+            mDevice = device;
+            mClient = client;
+            mCallback = callback;
+        }
+
+        public Device getDevice() {
+            return mDevice;
+        }
+
+        public Client getClient() {
+            return mClient;
+        }
+
+        public IBinder getToken() {
+            return mToken;
+        }
+
+        public void notifyClient(IMidiDeviceServer deviceServer) {
+            if (mCallback != null) {
+                try {
+                    mCallback.onDeviceOpened(deviceServer, (deviceServer == null ? null : mToken));
+                } catch (RemoteException e) {
+                    // Client binderDied() method will do necessary cleanup, so nothing to do here
+                }
+                mCallback = null;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "DeviceConnection Device ID: " + mDevice.getDeviceInfo().getId();
+        }
+    }
+
     public MidiService(Context context) {
         mContext = context;
         mPackageManager = context.getPackageManager();
@@ -321,6 +556,18 @@
                 }
             }
         }
+
+        PackageInfo info;
+        try {
+            info = mPackageManager.getPackageInfo(MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            info = null;
+        }
+        if (info != null && info.applicationInfo != null) {
+            mBluetoothServiceUid = info.applicationInfo.uid;
+        } else {
+            mBluetoothServiceUid = -1;
+        }
    }
 
     @Override
@@ -355,18 +602,61 @@
     }
 
     @Override
-    public IMidiDeviceServer openDevice(IBinder token, MidiDeviceInfo deviceInfo) {
-        Device device = mDevicesByInfo.get(deviceInfo);
-        if (device == null) {
-            Log.e(TAG, "device not found in openDevice: " + deviceInfo);
-            return null;
+    public void openDevice(IBinder token, MidiDeviceInfo deviceInfo,
+            IMidiDeviceOpenCallback callback) {
+        Client client = getClient(token);
+        if (client == null) return;
+
+        Device device;
+        synchronized (mDevicesByInfo) {
+            device = mDevicesByInfo.get(deviceInfo);
+            if (device == null) {
+                throw new IllegalArgumentException("device does not exist: " + deviceInfo);
+            }
+            if (!device.isUidAllowed(Binder.getCallingUid())) {
+                throw new SecurityException("Attempt to open private device with wrong UID");
+            }
         }
 
-        if (!device.isUidAllowed(Binder.getCallingUid())) {
-            throw new SecurityException("Attempt to open private device with wrong UID");
+        // clear calling identity so bindService does not fail
+        long identity = Binder.clearCallingIdentity();
+        try {
+            client.addDeviceConnection(device, callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void openBluetoothDevice(IBinder token, BluetoothDevice bluetoothDevice,
+            IMidiDeviceOpenCallback callback) {
+        Client client = getClient(token);
+        if (client == null) return;
+
+        // Bluetooth devices are created on demand
+        Device device;
+        synchronized (mDevicesByInfo) {
+            device = mBluetoothDevices.get(bluetoothDevice);
+            if (device == null) {
+                device = new Device(bluetoothDevice);
+                mBluetoothDevices.put(bluetoothDevice, device);
+            }
         }
 
-        return device.getDeviceServer();
+        // clear calling identity so bindService does not fail
+        long identity = Binder.clearCallingIdentity();
+        try {
+            client.addDeviceConnection(device, callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void closeDevice(IBinder clientToken, IBinder deviceToken) {
+        Client client = getClient(clientToken);
+        if (client == null) return;
+        client.removeDeviceConnection(deviceToken);
     }
 
     @Override
@@ -376,6 +666,8 @@
         int uid = Binder.getCallingUid();
         if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) {
             throw new SecurityException("only system can create USB devices");
+        } else if (type == MidiDeviceInfo.TYPE_BLUETOOTH && uid != mBluetoothServiceUid) {
+            throw new SecurityException("only MidiBluetoothService can create Bluetooth devices");
         }
 
         synchronized (mDevicesByInfo) {
@@ -389,8 +681,7 @@
         synchronized (mDevicesByInfo) {
             Device device = mDevicesByServer.get(server.asBinder());
             if (device != null) {
-                mDevicesByInfo.remove(device.getDeviceInfo());
-                removeDeviceLocked(device);
+                device.closeLocked();
             }
         }
     }
@@ -420,19 +711,16 @@
     }
 
     @Override
-    public void setDeviceStatus(IBinder token, MidiDeviceStatus status) {
-        MidiDeviceInfo deviceInfo = status.getDeviceInfo();
-        Device device = mDevicesByInfo.get(deviceInfo);
-        if (device == null) {
-            // Just return quietly here if device no longer exists
-            return;
+    public void setDeviceStatus(IMidiDeviceServer server, MidiDeviceStatus status) {
+        Device device = mDevicesByServer.get(server.asBinder());
+        if (device != null) {
+            if (Binder.getCallingUid() != device.getUid()) {
+                throw new SecurityException("setDeviceStatus() caller UID " + Binder.getCallingUid()
+                        + " does not match device's UID " + device.getUid());
+            }
+            device.setDeviceStatus(status);
+            notifyDeviceStatusChanged(device, status);
         }
-        if (Binder.getCallingUid() != device.getUid()) {
-            throw new SecurityException("setDeviceStatus() caller UID " + Binder.getCallingUid()
-                    + " does not match device's UID " + device.getUid());
-        }
-        device.setDeviceStatus(token, status);
-        notifyDeviceStatusChanged(device, status);
     }
 
     private void notifyDeviceStatusChanged(Device device, MidiDeviceStatus status) {
@@ -452,18 +740,24 @@
         int id = mNextDeviceId++;
         MidiDeviceInfo deviceInfo = new MidiDeviceInfo(type, id, numInputPorts, numOutputPorts,
                 inputPortNames, outputPortNames, properties, isPrivate);
-        Device device = new Device(server, deviceInfo, serviceInfo, uid);
 
-        if (server != null) {
-            IBinder binder = server.asBinder();
-            try {
-                binder.linkToDeath(device, 0);
-            } catch (RemoteException e) {
-                return null;
+        Device device = null;
+        BluetoothDevice bluetoothDevice = null;
+        if (type == MidiDeviceInfo.TYPE_BLUETOOTH) {
+            bluetoothDevice = (BluetoothDevice)properties.getParcelable(
+                    MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE);
+            device = mBluetoothDevices.get(bluetoothDevice);
+            if (device != null) {
+                device.setDeviceInfo(deviceInfo);
             }
-            mDevicesByServer.put(binder, device);
+        }
+        if (device == null) {
+            device = new Device(server, deviceInfo, serviceInfo, uid);
         }
         mDevicesByInfo.put(deviceInfo, device);
+        if (bluetoothDevice != null) {
+            mBluetoothDevices.put(bluetoothDevice, device);
+        }
 
         synchronized (mClients) {
             for (Client c : mClients.values()) {
@@ -478,8 +772,9 @@
     private void removeDeviceLocked(Device device) {
         IMidiDeviceServer server = device.getDeviceServer();
         if (server != null) {
-            mDevicesByServer.remove(server);
+            mDevicesByServer.remove(server.asBinder());
         }
+        mDevicesByInfo.remove(device.getDeviceInfo());
 
         synchronized (mClients) {
             for (Client c : mClients.values()) {
@@ -516,6 +811,15 @@
                     MidiDeviceService.SERVICE_INTERFACE);
             if (parser == null) return;
 
+            // ignore virtual device servers that do not require the correct permission
+            if (!android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE.equals(
+                    serviceInfo.permission)) {
+                Log.w(TAG, "Skipping MIDI device service " + serviceInfo.packageName
+                        + ": it does not require the permission "
+                        + android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE);
+                return;
+            }
+
             Bundle properties = null;
             int numInputPorts = 0;
             int numOutputPorts = 0;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 193285f..e085d89 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1107,9 +1107,9 @@
     }
 
     /**
-     * Returns a constant indicating the state of sim for the subscription.
+     * Returns a constant indicating the state of sim for the slot idx.
      *
-     * @param subId
+     * @param slotIdx
      *
      * {@See TelephonyManager#SIM_STATE_UNKNOWN}
      * {@See TelephonyManager#SIM_STATE_ABSENT}
@@ -1123,16 +1123,16 @@
      *
      * {@hide}
      */
-    public static int getSimStateForSubscriber(int subId) {
+    public static int getSimStateForSlotIdx(int slotIdx) {
         int simState;
 
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
-            simState = iSub.getSimStateForSubscriber(subId);
+            simState = iSub.getSimStateForSlotIdx(slotIdx);
         } catch (RemoteException ex) {
             simState = TelephonyManager.SIM_STATE_UNKNOWN;
         }
-        logd("getSimStateForSubscriber: simState=" + simState + " subId=" + subId);
+        logd("getSimStateForSubscriber: simState=" + simState + " slotIdx=" + slotIdx);
         return simState;
     }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2445bf6e..f5a7c0f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -803,17 +803,26 @@
     public CellLocation getCellLocation() {
         try {
             ITelephony telephony = getITelephony();
-            if (telephony == null)
+            if (telephony == null) {
+                Rlog.d(TAG, "getCellLocation returning null because telephony is null");
                 return null;
+            }
             Bundle bundle = telephony.getCellLocation(mContext.getOpPackageName());
-            if (bundle.isEmpty()) return null;
-            CellLocation cl = CellLocation.newFromBundle(bundle);
-            if (cl.isEmpty())
+            if (bundle.isEmpty()) {
+                Rlog.d(TAG, "getCellLocation returning null because bundle is empty");
                 return null;
+            }
+            CellLocation cl = CellLocation.newFromBundle(bundle);
+            if (cl.isEmpty()) {
+                Rlog.d(TAG, "getCellLocation returning null because CellLocation is empty");
+                return null;
+            }
             return cl;
         } catch (RemoteException ex) {
+            Rlog.d(TAG, "getCellLocation returning null due to RemoteException " + ex);
             return null;
         } catch (NullPointerException ex) {
+            Rlog.d(TAG, "getCellLocation returning null due to NullPointerException " + ex);
             return null;
         }
     }
@@ -1625,7 +1634,25 @@
      * @see #SIM_STATE_CARD_IO_ERROR
      */
     public int getSimState() {
-        return getSimState(getDefaultSim());
+        int slotIdx = getDefaultSim();
+        // slotIdx may be invalid due to sim being absent. In that case query all slots to get
+        // sim state
+        if (slotIdx < 0) {
+            // query for all slots and return absent if all sim states are absent, otherwise
+            // return unknown
+            for (int i = 0; i < getPhoneCount(); i++) {
+                int simState = getSimState(i);
+                if (simState != SIM_STATE_ABSENT) {
+                    Rlog.d(TAG, "getSimState: default sim:" + slotIdx + ", sim state for " +
+                            "slotIdx=" + i + " is " + simState + ", return state as unknown");
+                    return SIM_STATE_UNKNOWN;
+                }
+            }
+            Rlog.d(TAG, "getSimState: default sim:" + slotIdx + ", all SIMs absent, return " +
+                    "state as absent");
+            return SIM_STATE_ABSENT;
+        }
+        return getSimState(slotIdx);
     }
 
     /**
@@ -1645,12 +1672,7 @@
      */
     /** {@hide} */
     public int getSimState(int slotIdx) {
-        int[] subId = SubscriptionManager.getSubId(slotIdx);
-        if (subId == null || subId.length == 0) {
-            Rlog.d(TAG, "getSimState:- empty subId return SIM_STATE_ABSENT");
-            return SIM_STATE_UNKNOWN;
-        }
-        int simState = SubscriptionManager.getSimStateForSubscriber(subId[0]);
+        int simState = SubscriptionManager.getSimStateForSlotIdx(slotIdx);
         return simState;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 1037f2d..918a2eb 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -180,10 +180,10 @@
     int[] getActiveSubIdList();
 
     /**
-     * Get the SIM state for the subscriber
+     * Get the SIM state for the slot idx
      * @return SIM state as the ordinal of IccCardConstants.State
      */
-    int getSimStateForSubscriber(int subId);
+    int getSimStateForSlotIdx(int slotIdx);
 
     boolean isActiveSubId(int subId);
 }
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/TrackTimeReceiver.java b/tests/ActivityTests/src/com/google/android/test/activity/TrackTimeReceiver.java
index c30d33a..0cc0090 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/TrackTimeReceiver.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/TrackTimeReceiver.java
@@ -27,7 +27,7 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         Bundle data = intent.getExtras();
-        data.getLong(ActivityOptions.EXTRA_USAGE_REPORT_TIME);
+        data.getLong(ActivityOptions.EXTRA_USAGE_TIME_REPORT);
         Log.i("ActivityTest", "Received time: " + data);
     }
 }