Merge "Adjust and update dumpsys meminfo output."
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 0daf30f25..638d81b 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -76,6 +76,9 @@
     // Registers a tablet mode change listener
     void registerTabletModeChangedListener(ITabletModeChangedListener listener);
 
+    // Queries whether the device's microphone is muted by switch
+    int isMicMuted();
+
     // Input device vibrator control.
     void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token);
     void cancelVibrate(int deviceId, IBinder token);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 2a59be2..0c0f248 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -520,6 +520,22 @@
     }
 
     /**
+     * Queries whether the device's microphone is muted
+     *
+     * @return The mic mute switch state which is one of {@link #SWITCH_STATE_UNKNOWN},
+     * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}.
+     * @hide
+     */
+    @SwitchState
+    public int isMicMuted() {
+        try {
+            return mIm.isMicMuted();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets information about all supported keyboard layouts.
      * <p>
      * The input manager consults the built-in keyboard layouts as well
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 2d6cd24..f797da7 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1857,12 +1857,37 @@
     }
 
     /**
+     * @hide
+     * Sets the microphone from switch mute on or off.
+     * <p>
+     * This method should only be used by InputManager to notify
+     * Audio Subsystem about Microphone Mute switch state.
+     *
+     * @param on set <var>true</var> to mute the microphone;
+     *           <var>false</var> to turn mute off
+     */
+    @UnsupportedAppUsage
+    public void setMicrophoneMuteFromSwitch(boolean on) {
+        final IAudioService service = getService();
+        try {
+            service.setMicrophoneMuteFromSwitch(on);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Checks whether the microphone mute is on or off.
      *
      * @return true if microphone is muted, false if it's not
      */
     public boolean isMicrophoneMute() {
-        return AudioSystem.isMicrophoneMuted();
+        final IAudioService service = getService();
+        try {
+            return service.isMicrophoneMuted();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 71f52a1..fc05610 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -106,8 +106,12 @@
 
     List<AudioProductStrategy> getAudioProductStrategies();
 
+    boolean isMicrophoneMuted();
+
     void setMicrophoneMute(boolean on, String callingPackage, int userId);
 
+    oneway void setMicrophoneMuteFromSwitch(boolean on);
+
     void setRingerModeExternal(int ringerMode, String caller);
 
     void setRingerModeInternal(int ringerMode, String caller);
diff --git a/mime/Android.bp b/mime/Android.bp
index 51290f6..8b2b059 100644
--- a/mime/Android.bp
+++ b/mime/Android.bp
@@ -43,11 +43,10 @@
 
 // The mimemap-res.jar and mimemap-testing-res.jar genrules produce a .jar that
 // has the resource file in a subdirectory res/ and testres/, respectively.
-// Those paths need to They need to be in different paths because one of them
-// ends up on a bootclasspath jar whereas the other one ends up in a test jar.
-// Bootclasspath resources hide test or application resources under the same
-// path because ClassLoader.getResource(String) consults the parent ClassLoader
-// first.
+// They need to be in different paths because one of them ends up in a
+// bootclasspath jar whereas the other one ends up in a test jar. Bootclasspath
+// resources hide test or application resources under the same path because
+// ClassLoader.getResource(String) consults the parent ClassLoader first.
 //
 // Further notes:
 //  - the "cp" command will flatten any directory paths that occur in $(in),
diff --git a/mime/TEST_MAPPING b/mime/TEST_MAPPING
new file mode 100644
index 0000000..8daab75
--- /dev/null
+++ b/mime/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsMimeMapTestCases"
+    }
+  ]
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index d7411260..7277e92 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -22,6 +22,7 @@
 import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
 import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY;
 import static android.opengl.EGL14.EGL_DEPTH_SIZE;
+import static android.opengl.EGL14.EGL_EXTENSIONS;
 import static android.opengl.EGL14.EGL_GREEN_SIZE;
 import static android.opengl.EGL14.EGL_NONE;
 import static android.opengl.EGL14.EGL_NO_CONTEXT;
@@ -41,6 +42,7 @@
 import static android.opengl.EGL14.eglGetError;
 import static android.opengl.EGL14.eglInitialize;
 import static android.opengl.EGL14.eglMakeCurrent;
+import static android.opengl.EGL14.eglQueryString;
 import static android.opengl.EGL14.eglSwapBuffers;
 import static android.opengl.EGL14.eglTerminate;
 
@@ -63,6 +65,7 @@
     // Below two constants make drawing at low priority, so other things can preempt our drawing.
     private static final int EGL_CONTEXT_PRIORITY_LEVEL_IMG = 0x3100;
     private static final int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
+    private static final String EGL_IMG_CONTEXT_PRIORITY = "EGL_IMG_context_priority";
 
     private EGLDisplay mEglDisplay;
     private EGLConfig mEglConfig;
@@ -70,6 +73,7 @@
     private EGLSurface mEglSurface;
     private final int[] mEglVersion = new int[2];
     private boolean mEglReady;
+    private boolean mContextPrioritySupported;
 
     /**
      * Initialize EGL and prepare EglSurface.
@@ -105,10 +109,22 @@
             return false;
         }
 
+        mContextPrioritySupported = isContextPrioritySuppported();
+
         mEglReady = true;
         return true;
     }
 
+    private boolean isContextPrioritySuppported() {
+        String[] extensions = eglQueryString(mEglDisplay, EGL_EXTENSIONS).split(" ");
+        for (String extension : extensions) {
+            if (extension.equals(EGL_IMG_CONTEXT_PRIORITY)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private EGLConfig chooseEglConfig() {
         int[] configsCount = new int[1];
         EGLConfig[] configs = new EGLConfig[1];
@@ -184,8 +200,15 @@
      * @return true if EglContext is ready.
      */
     public boolean createEglContext() {
-        int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2,
-                EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_NONE};
+        int[] attrib_list = new int[5];
+        int idx = 0;
+        attrib_list[idx++] = EGL_CONTEXT_CLIENT_VERSION;
+        attrib_list[idx++] = 2;
+        if (mContextPrioritySupported) {
+            attrib_list[idx++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG;
+            attrib_list[idx++] = EGL_CONTEXT_PRIORITY_LOW_IMG;
+        }
+        attrib_list[idx++] = EGL_NONE;
         mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
         if (mEglContext == EGL_NO_CONTEXT) {
             Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 6db9702..75419e1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2868,7 +2868,21 @@
         return 0;
     }
 
-    private int runCompat(PrintWriter pw) {
+    private void killPackage(String packageName, PrintWriter pw) throws RemoteException {
+        int uid = mPm.getPackageUid(packageName, 0, mUserId);
+        if (uid < 0) {
+            // uid is negative if the package wasn't found.
+            pw.println("Didn't find package " + packageName + " on device.");
+        } else {
+            pw.println("Killing package " + packageName + " (UID " + uid + ").");
+            final long origId = Binder.clearCallingIdentity();
+            mInterface.killUid(UserHandle.getAppId(uid),
+                    UserHandle.USER_ALL, "killPackage");
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    private int runCompat(PrintWriter pw) throws RemoteException {
         final CompatConfig config = CompatConfig.get();
         String toggleValue = getNextArgRequired();
         long changeId;
@@ -2882,13 +2896,14 @@
             pw.println("Unknown or invalid change: '" + changeIdString + "'.");
         }
         String packageName = getNextArgRequired();
-        switch(toggleValue) {
+        switch (toggleValue) {
             case "enable":
                 if (!config.addOverride(changeId, packageName, true)) {
                     pw.println("Warning! Change " + changeId + " is not known yet. Enabling it"
                             + " could have no effect.");
                 }
                 pw.println("Enabled change " + changeId + " for " + packageName + ".");
+                killPackage(packageName, pw);
                 return 0;
             case "disable":
                 if (!config.addOverride(changeId, packageName, false)) {
@@ -2896,11 +2911,13 @@
                             + " could have no effect.");
                 }
                 pw.println("Disabled change " + changeId + " for " + packageName + ".");
+                killPackage(packageName, pw);
                 return 0;
             case "reset":
                 if (config.removeOverride(changeId, packageName)) {
                     pw.println("Reset change " + changeId + " for " + packageName
                             + " to default value.");
+                    killPackage(packageName, pw);
                 } else {
                     pw.println("No override exists for changeId " + changeId + ".");
                 }
@@ -3219,6 +3236,7 @@
             pw.println("      Write all pending state to storage.");
             pw.println("  compat enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
             pw.println("      Toggles a change either by id or by name for <PACKAGE_NAME>.");
+            pw.println("      It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
             pw.println();
             Intent.printIntentArgsHelp(pw, "");
         }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 77b3fee..7f7ef17 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -59,6 +59,7 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiTvClient;
+import android.hardware.input.InputManager;
 import android.hardware.usb.UsbManager;
 import android.media.AudioAttributes;
 import android.media.AudioFocusInfo;
@@ -545,6 +546,10 @@
     private String mEnabledSurroundFormats;
     private boolean mSurroundModeChanged;
 
+    private boolean mMicMuteFromSwitch;
+    private boolean mMicMuteFromApi;
+    private boolean mMicMuteFromRestrictions;
+
     @GuardedBy("mSettingsLock")
     private int mAssistantUid;
 
@@ -882,6 +887,8 @@
         mRoleObserver.register();
 
         onIndicateSystemReady();
+
+        setMicMuteFromSwitchInput();
     }
 
     RoleObserver mRoleObserver;
@@ -1021,6 +1028,8 @@
 
         sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
                 SENDMSG_QUEUE, 1, 0, null, 0);
+
+        setMicMuteFromSwitchInput();
     }
 
     private void onDispatchAudioServerStateChange(boolean state) {
@@ -2837,20 +2846,45 @@
                 != PackageManager.PERMISSION_GRANTED) {
             return;
         }
-        setMicrophoneMuteNoCallerCheck(on, userId);
+        mMicMuteFromApi = on;
+        setMicrophoneMuteNoCallerCheck(userId);
     }
 
-    private void setMicrophoneMuteNoCallerCheck(boolean on, int userId) {
+    /** @see AudioManager#setMicrophoneMuteFromSwitch(boolean) */
+    public void setMicrophoneMuteFromSwitch(boolean on) {
+        int userId = Binder.getCallingUid();
+        if (userId != android.os.Process.SYSTEM_UID) {
+            Log.e(TAG, "setMicrophoneMuteFromSwitch() called from non system user!");
+            return;
+        }
+        mMicMuteFromSwitch = on;
+        setMicrophoneMuteNoCallerCheck(userId);
+    }
+
+    private void setMicMuteFromSwitchInput() {
+        InputManager im = mContext.getSystemService(InputManager.class);
+        final int isMicMuted = im.isMicMuted();
+        if (isMicMuted != InputManager.SWITCH_STATE_UNKNOWN) {
+            setMicrophoneMuteFromSwitch(im.isMicMuted() != InputManager.SWITCH_STATE_OFF);
+        }
+    }
+
+    public boolean isMicrophoneMuted() {
+        return mMicMuteFromSwitch || mMicMuteFromRestrictions || mMicMuteFromApi;
+    }
+
+    private void setMicrophoneMuteNoCallerCheck(int userId) {
+        final boolean muted = isMicrophoneMuted();
         if (DEBUG_VOL) {
-            Log.d(TAG, String.format("Mic mute %s, user=%d", on, userId));
+            Log.d(TAG, String.format("Mic mute %d, user=%d", muted, userId));
         }
         // only mute for the current user
-        if (getCurrentUserId() == userId) {
+        if (getCurrentUserId() == userId || userId == android.os.Process.SYSTEM_UID) {
             final boolean currentMute = AudioSystem.isMicrophoneMuted();
             final long identity = Binder.clearCallingIdentity();
-            AudioSystem.muteMicrophone(on);
+            AudioSystem.muteMicrophone(muted);
             Binder.restoreCallingIdentity(identity);
-            if (on != currentMute) {
+            if (muted != currentMute) {
                 mContext.sendBroadcastAsUser(
                         new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)
                                 .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
@@ -5378,7 +5412,8 @@
                 final boolean isRestricted =
                         newRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE);
                 if (wasRestricted != isRestricted) {
-                    setMicrophoneMuteNoCallerCheck(isRestricted, userId);
+                    mMicMuteFromRestrictions = isRestricted;
+                    setMicrophoneMuteNoCallerCheck(userId);
                 }
             }
 
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 75b9705..b338705 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -47,6 +47,7 @@
 import android.hardware.input.InputManagerInternal;
 import android.hardware.input.KeyboardLayout;
 import android.hardware.input.TouchCalibration;
+import android.media.AudioManager;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -294,6 +295,9 @@
     /** Switch code: Camera lens cover. When set the lens is covered. */
     public static final int SW_CAMERA_LENS_COVER = 0x09;
 
+    /** Switch code: Microphone. When set it is off. */
+    public static final int SW_MUTE_DEVICE = 0x0e;
+
     public static final int SW_LID_BIT = 1 << SW_LID;
     public static final int SW_TABLET_MODE_BIT = 1 << SW_TABLET_MODE;
     public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE;
@@ -304,6 +308,7 @@
     public static final int SW_JACK_BITS =
             SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_JACK_PHYSICAL_INSERT_BIT | SW_LINEOUT_INSERT_BIT;
     public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
+    public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
 
     /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
     final boolean mUseDevInputEventForAudioJack;
@@ -970,6 +975,11 @@
     }
 
     @Override // Binder call
+    public int isMicMuted() {
+        return getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MUTE_DEVICE);
+    }
+
+    @Override // Binder call
     public void registerTabletModeChangedListener(ITabletModeChangedListener listener) {
         if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE,
                 "registerTabletModeChangedListener()")) {
@@ -1779,6 +1789,12 @@
             mHandler.obtainMessage(MSG_DELIVER_TABLET_MODE_CHANGED,
                     args).sendToTarget();
         }
+
+        if ((switchMask & SW_MUTE_DEVICE_BIT) != 0) {
+            final boolean micMute = ((switchValues & SW_MUTE_DEVICE_BIT) != 0);
+            AudioManager audioManager = mContext.getSystemService(AudioManager.class);
+            audioManager.setMicrophoneMuteFromSwitch(micMute);
+        }
     }
 
     // Native callback.
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4f6d2d4..3ef11f1 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -481,6 +481,14 @@
             "notify_handover_video_from_wifi_to_lte_bool";
 
     /**
+     * Flag specifying whether the carrier supports merging a RTT call with a voice call,
+     * downgrading the call in the process.
+     * @hide
+     */
+    public static final String KEY_ALLOW_MERGING_RTT_CALLS_BOOL =
+             "allow_merging_rtt_calls_bool";
+
+    /**
      * Flag specifying whether the carrier wants to notify the user when a VT call has been handed
      * over from LTE to WIFI.
      * <p>
@@ -3128,6 +3136,7 @@
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
+        sDefaults.putBoolean(KEY_ALLOW_MERGING_RTT_CALLS_BOOL, false);
         sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
         sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index da4da79..45deea2 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -243,6 +243,7 @@
     };
 
     private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
+    private ServiceConnection mServiceConnection;
     private final InternalDownloadSessionCallback mInternalCallback;
     private final Map<DownloadStatusListener, InternalDownloadStatusListener>
             mInternalDownloadStatusListeners = new HashMap<>();
@@ -318,56 +319,66 @@
     }
 
     private int bindAndInitialize() {
-        return MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
-                new ServiceConnection() {
-                    @Override
-                    public void onServiceConnected(ComponentName name, IBinder service) {
-                        IMbmsDownloadService downloadService =
-                                IMbmsDownloadService.Stub.asInterface(service);
-                        int result;
-                        try {
-                            result = downloadService.initialize(mSubscriptionId, mInternalCallback);
-                        } catch (RemoteException e) {
-                            Log.e(LOG_TAG, "Service died before initialization");
-                            sIsInitialized.set(false);
-                            return;
-                        } catch (RuntimeException e) {
-                            Log.e(LOG_TAG, "Runtime exception during initialization");
-                            sendErrorToApp(
-                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
-                                    e.toString());
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        if (result == MbmsErrors.UNKNOWN) {
-                            // Unbind and throw an obvious error
-                            close();
-                            throw new IllegalStateException("Middleware must not return an"
-                                    + " unknown error code");
-                        }
-                        if (result != MbmsErrors.SUCCESS) {
-                            sendErrorToApp(result, "Error returned during initialization");
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        try {
-                            downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
-                        } catch (RemoteException e) {
-                            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
-                                    "Middleware lost during initialization");
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        mService.set(downloadService);
-                    }
+        mServiceConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                IMbmsDownloadService downloadService =
+                        IMbmsDownloadService.Stub.asInterface(service);
+                int result;
+                try {
+                    result = downloadService.initialize(mSubscriptionId, mInternalCallback);
+                } catch (RemoteException e) {
+                    Log.e(LOG_TAG, "Service died before initialization");
+                    sIsInitialized.set(false);
+                    return;
+                } catch (RuntimeException e) {
+                    Log.e(LOG_TAG, "Runtime exception during initialization");
+                    sendErrorToApp(
+                            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                            e.toString());
+                    sIsInitialized.set(false);
+                    return;
+                }
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not return an"
+                            + " unknown error code");
+                }
+                if (result != MbmsErrors.SUCCESS) {
+                    sendErrorToApp(result, "Error returned during initialization");
+                    sIsInitialized.set(false);
+                    return;
+                }
+                try {
+                    downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
+                } catch (RemoteException e) {
+                    sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                            "Middleware lost during initialization");
+                    sIsInitialized.set(false);
+                    return;
+                }
+                mService.set(downloadService);
+            }
 
-                    @Override
-                    public void onServiceDisconnected(ComponentName name) {
-                        Log.w(LOG_TAG, "bindAndInitialize: Remote service disconnected");
-                        sIsInitialized.set(false);
-                        mService.set(null);
-                    }
-                });
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                Log.w(LOG_TAG, "bindAndInitialize: Remote service disconnected");
+                sIsInitialized.set(false);
+                mService.set(null);
+            }
+
+            @Override
+            public void onNullBinding(ComponentName name) {
+                Log.w(LOG_TAG, "bindAndInitialize: Remote service returned null");
+                sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                        "Middleware service binding returned null");
+                sIsInitialized.set(false);
+                mService.set(null);
+                mContext.unbindService(this);
+            }
+        };
+        return MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION, mServiceConnection);
     }
 
     /**
@@ -965,17 +976,19 @@
     public void close() {
         try {
             IMbmsDownloadService downloadService = mService.get();
-            if (downloadService == null) {
+            if (downloadService == null || mServiceConnection == null) {
                 Log.i(LOG_TAG, "Service already dead");
                 return;
             }
             downloadService.dispose(mSubscriptionId);
+            mContext.unbindService(mServiceConnection);
         } catch (RemoteException e) {
             // Ignore
             Log.i(LOG_TAG, "Remote exception while disposing of service");
         } finally {
             mService.set(null);
             sIsInitialized.set(false);
+            mServiceConnection = null;
             mInternalCallback.stop();
         }
     }
diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java
index f1be31f..d54071f 100644
--- a/telephony/java/android/telephony/MbmsGroupCallSession.java
+++ b/telephony/java/android/telephony/MbmsGroupCallSession.java
@@ -80,6 +80,7 @@
     };
 
     private InternalGroupCallSessionCallback mInternalCallback;
+    private ServiceConnection mServiceConnection;
     private Set<GroupCall> mKnownActiveGroupCalls = new ArraySet<>();
 
     private final Context mContext;
@@ -163,7 +164,7 @@
     public void close() {
         try {
             IMbmsGroupCallService groupCallService = mService.get();
-            if (groupCallService == null) {
+            if (groupCallService == null || mServiceConnection == null) {
                 // Ignore and return, assume already disposed.
                 return;
             }
@@ -172,11 +173,13 @@
                 s.getCallback().stop();
             }
             mKnownActiveGroupCalls.clear();
+            mContext.unbindService(mServiceConnection);
         } catch (RemoteException e) {
             // Ignore for now
         } finally {
             mService.set(null);
             sIsInitialized.set(false);
+            mServiceConnection = null;
             mInternalCallback.stop();
         }
     }
@@ -244,59 +247,69 @@
     }
 
     private int bindAndInitialize() {
-        return MbmsUtils.startBinding(mContext, MBMS_GROUP_CALL_SERVICE_ACTION,
-                new ServiceConnection() {
-                    @Override
-                    public void onServiceConnected(ComponentName name, IBinder service) {
-                        IMbmsGroupCallService groupCallService =
-                                IMbmsGroupCallService.Stub.asInterface(service);
-                        int result;
-                        try {
-                            result = groupCallService.initialize(mInternalCallback,
-                                    mSubscriptionId);
-                        } catch (RemoteException e) {
-                            Log.e(LOG_TAG, "Service died before initialization");
-                            mInternalCallback.onError(
-                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
-                                    e.toString());
-                            sIsInitialized.set(false);
-                            return;
-                        } catch (RuntimeException e) {
-                            Log.e(LOG_TAG, "Runtime exception during initialization");
-                            mInternalCallback.onError(
-                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
-                                    e.toString());
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        if (result == MbmsErrors.UNKNOWN) {
-                            // Unbind and throw an obvious error
-                            close();
-                            throw new IllegalStateException("Middleware must not return"
-                                    + " an unknown error code");
-                        }
-                        if (result != MbmsErrors.SUCCESS) {
-                            mInternalCallback.onError(result,
-                                    "Error returned during initialization");
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        try {
-                            groupCallService.asBinder().linkToDeath(mDeathRecipient, 0);
-                        } catch (RemoteException e) {
-                            mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
-                                    "Middleware lost during initialization");
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        mService.set(groupCallService);
-                    }
+        mServiceConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                IMbmsGroupCallService groupCallService =
+                        IMbmsGroupCallService.Stub.asInterface(service);
+                int result;
+                try {
+                    result = groupCallService.initialize(mInternalCallback,
+                            mSubscriptionId);
+                } catch (RemoteException e) {
+                    Log.e(LOG_TAG, "Service died before initialization");
+                    mInternalCallback.onError(
+                            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                            e.toString());
+                    sIsInitialized.set(false);
+                    return;
+                } catch (RuntimeException e) {
+                    Log.e(LOG_TAG, "Runtime exception during initialization");
+                    mInternalCallback.onError(
+                            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                            e.toString());
+                    sIsInitialized.set(false);
+                    return;
+                }
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not return"
+                            + " an unknown error code");
+                }
+                if (result != MbmsErrors.SUCCESS) {
+                    mInternalCallback.onError(result,
+                            "Error returned during initialization");
+                    sIsInitialized.set(false);
+                    return;
+                }
+                try {
+                    groupCallService.asBinder().linkToDeath(mDeathRecipient, 0);
+                } catch (RemoteException e) {
+                    mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                            "Middleware lost during initialization");
+                    sIsInitialized.set(false);
+                    return;
+                }
+                mService.set(groupCallService);
+            }
 
-                    @Override
-                    public void onServiceDisconnected(ComponentName name) {
-                        sIsInitialized.set(false);
-                        mService.set(null);
-                    }
-                });
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                sIsInitialized.set(false);
+                mService.set(null);
+            }
+
+            @Override
+            public void onNullBinding(ComponentName name) {
+                Log.w(LOG_TAG, "bindAndInitialize: Remote service returned null");
+                mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                        "Middleware service binding returned null");
+                sIsInitialized.set(false);
+                mService.set(null);
+                mContext.unbindService(this);
+            }
+        };
+        return MbmsUtils.startBinding(mContext, MBMS_GROUP_CALL_SERVICE_ACTION, mServiceConnection);
     }
 }
diff --git a/telephony/java/android/telephony/MbmsStreamingSession.java b/telephony/java/android/telephony/MbmsStreamingSession.java
index cd465d2..3fbbc03 100644
--- a/telephony/java/android/telephony/MbmsStreamingSession.java
+++ b/telephony/java/android/telephony/MbmsStreamingSession.java
@@ -82,6 +82,7 @@
     };
 
     private InternalStreamingSessionCallback mInternalCallback;
+    private ServiceConnection mServiceConnection;
     private Set<StreamingService> mKnownActiveStreamingServices = new ArraySet<>();
 
     private final Context mContext;
@@ -168,7 +169,7 @@
     public void close() {
         try {
             IMbmsStreamingService streamingService = mService.get();
-            if (streamingService == null) {
+            if (streamingService == null || mServiceConnection == null) {
                 // Ignore and return, assume already disposed.
                 return;
             }
@@ -177,11 +178,13 @@
                 s.getCallback().stop();
             }
             mKnownActiveStreamingServices.clear();
+            mContext.unbindService(mServiceConnection);
         } catch (RemoteException e) {
             // Ignore for now
         } finally {
             mService.set(null);
             sIsInitialized.set(false);
+            mServiceConnection = null;
             mInternalCallback.stop();
         }
     }
@@ -286,59 +289,69 @@
     }
 
     private int bindAndInitialize() {
-        return MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION,
-                new ServiceConnection() {
-                    @Override
-                    public void onServiceConnected(ComponentName name, IBinder service) {
-                        IMbmsStreamingService streamingService =
-                                IMbmsStreamingService.Stub.asInterface(service);
-                        int result;
-                        try {
-                            result = streamingService.initialize(mInternalCallback,
-                                    mSubscriptionId);
-                        } catch (RemoteException e) {
-                            Log.e(LOG_TAG, "Service died before initialization");
-                            sendErrorToApp(
-                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
-                                    e.toString());
-                            sIsInitialized.set(false);
-                            return;
-                        } catch (RuntimeException e) {
-                            Log.e(LOG_TAG, "Runtime exception during initialization");
-                            sendErrorToApp(
-                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
-                                    e.toString());
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        if (result == MbmsErrors.UNKNOWN) {
-                            // Unbind and throw an obvious error
-                            close();
-                            throw new IllegalStateException("Middleware must not return"
-                                    + " an unknown error code");
-                        }
-                        if (result != MbmsErrors.SUCCESS) {
-                            sendErrorToApp(result, "Error returned during initialization");
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        try {
-                            streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
-                        } catch (RemoteException e) {
-                            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
-                                    "Middleware lost during initialization");
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        mService.set(streamingService);
-                    }
+        mServiceConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                IMbmsStreamingService streamingService =
+                        IMbmsStreamingService.Stub.asInterface(service);
+                int result;
+                try {
+                    result = streamingService.initialize(mInternalCallback,
+                            mSubscriptionId);
+                } catch (RemoteException e) {
+                    Log.e(LOG_TAG, "Service died before initialization");
+                    sendErrorToApp(
+                            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                            e.toString());
+                    sIsInitialized.set(false);
+                    return;
+                } catch (RuntimeException e) {
+                    Log.e(LOG_TAG, "Runtime exception during initialization");
+                    sendErrorToApp(
+                            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                            e.toString());
+                    sIsInitialized.set(false);
+                    return;
+                }
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not return"
+                            + " an unknown error code");
+                }
+                if (result != MbmsErrors.SUCCESS) {
+                    sendErrorToApp(result, "Error returned during initialization");
+                    sIsInitialized.set(false);
+                    return;
+                }
+                try {
+                    streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
+                } catch (RemoteException e) {
+                    sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                            "Middleware lost during initialization");
+                    sIsInitialized.set(false);
+                    return;
+                }
+                mService.set(streamingService);
+            }
 
-                    @Override
-                    public void onServiceDisconnected(ComponentName name) {
-                        sIsInitialized.set(false);
-                        mService.set(null);
-                    }
-                });
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                sIsInitialized.set(false);
+                mService.set(null);
+            }
+
+            @Override
+            public void onNullBinding(ComponentName name) {
+                Log.w(LOG_TAG, "bindAndInitialize: Remote service returned null");
+                sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                        "Middleware service binding returned null");
+                sIsInitialized.set(false);
+                mService.set(null);
+                mContext.unbindService(this);
+            }
+        };
+        return MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION, mServiceConnection);
     }
 
     private void sendErrorToApp(int errorCode, String message) {