diff options
13 files changed, 362 insertions, 167 deletions
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 0daf30f25d59..638d81b2f635 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -76,6 +76,9 @@ interface IInputManager { // 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 2a59be28a937..0c0f248e3222 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -520,6 +520,22 @@ public final class InputManager { } /** + * 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/core/proto/android/telecomm/enums.proto b/core/proto/android/telecomm/enums.proto index 7a2ba624c021..5ca4a85f7c6a 100644 --- a/core/proto/android/telecomm/enums.proto +++ b/core/proto/android/telecomm/enums.proto @@ -110,6 +110,24 @@ enum CallStateEnum { * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL}. */ PULLING = 10; + + /** + * Indicates that an incoming call has been answered by the in-call UI, but Telephony hasn't yet + * set the call to active. + */ + ANSWERED = 11; + + /** + * Indicates that the call is undergoing audio processing by a different app in the background. + * @see android.telecom.Call#STATE_AUDIO_PROCESSING + */ + AUDIO_PROCESSING = 12; + + /** + * Indicates that the call is in a fake ringing state. + * @see android.telecom.Call#STATE_SIMULATED_RINGING + */ + SIMULATED_RINGING = 13; } // Disconnect causes for a call. Primarily used by android/telecom/DisconnectCause.java diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 2d6cd242c702..f797da70e7d1 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1857,12 +1857,37 @@ public class AudioManager { } /** + * @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 71f52a1b7d8e..fc056109baa4 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -106,8 +106,12 @@ interface IAudioService { 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/TEST_MAPPING b/mime/TEST_MAPPING new file mode 100644 index 000000000000..8daab754ea8a --- /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 d74112608491..7277e927b976 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_CONFIG_CAVEAT; 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.eglGetDisplay; 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 @@ public class EglHelper { // 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 @@ public class EglHelper { 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 @@ public class EglHelper { 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 @@ public class EglHelper { * @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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 77b3feec700e..7f7ef172f14d 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.HdmiAudioSystemClient; 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 @@ public class AudioService extends IAudioService.Stub private String mEnabledSurroundFormats; private boolean mSurroundModeChanged; + private boolean mMicMuteFromSwitch; + private boolean mMicMuteFromApi; + private boolean mMicMuteFromRestrictions; + @GuardedBy("mSettingsLock") private int mAssistantUid; @@ -882,6 +887,8 @@ public class AudioService extends IAudioService.Stub mRoleObserver.register(); onIndicateSystemReady(); + + setMicMuteFromSwitchInput(); } RoleObserver mRoleObserver; @@ -1021,6 +1028,8 @@ public class AudioService extends IAudioService.Stub sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE, SENDMSG_QUEUE, 1, 0, null, 0); + + setMicMuteFromSwitchInput(); } private void onDispatchAudioServerStateChange(boolean state) { @@ -2837,20 +2846,45 @@ public class AudioService extends IAudioService.Stub != PackageManager.PERMISSION_GRANTED) { return; } - setMicrophoneMuteNoCallerCheck(on, userId); + mMicMuteFromApi = on; + setMicrophoneMuteNoCallerCheck(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(boolean on, int userId) { + 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 @@ public class AudioService extends IAudioService.Stub 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 75b9705e1045..b33870559f59 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.InputManager; 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 @@ public class InputManagerService extends IInputManager.Stub /** 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 class InputManagerService extends IInputManager.Stub 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 @@ public class InputManagerService extends IInputManager.Stub } @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 @@ public class InputManagerService extends IInputManager.Stub 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 4f6d2d4e2e26..3ef11f163290 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -481,6 +481,14 @@ public class CarrierConfigManager { "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 @@ public class CarrierConfigManager { 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 da4da79a39df..45deea206cfc 100644 --- a/telephony/java/android/telephony/MbmsDownloadSession.java +++ b/telephony/java/android/telephony/MbmsDownloadSession.java @@ -243,6 +243,7 @@ public class MbmsDownloadSession implements AutoCloseable { }; 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 @@ public class MbmsDownloadSession implements AutoCloseable { } 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 class MbmsDownloadSession implements AutoCloseable { 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 f1be31fa5477..d54071f28be9 100644 --- a/telephony/java/android/telephony/MbmsGroupCallSession.java +++ b/telephony/java/android/telephony/MbmsGroupCallSession.java @@ -80,6 +80,7 @@ public class MbmsGroupCallSession implements AutoCloseable { }; private InternalGroupCallSessionCallback mInternalCallback; + private ServiceConnection mServiceConnection; private Set<GroupCall> mKnownActiveGroupCalls = new ArraySet<>(); private final Context mContext; @@ -163,7 +164,7 @@ public class MbmsGroupCallSession implements AutoCloseable { 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 @@ public class MbmsGroupCallSession implements AutoCloseable { 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 @@ public class MbmsGroupCallSession implements AutoCloseable { } 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 cd465d22d331..3fbbc03f0c67 100644 --- a/telephony/java/android/telephony/MbmsStreamingSession.java +++ b/telephony/java/android/telephony/MbmsStreamingSession.java @@ -82,6 +82,7 @@ public class MbmsStreamingSession implements AutoCloseable { }; private InternalStreamingSessionCallback mInternalCallback; + private ServiceConnection mServiceConnection; private Set<StreamingService> mKnownActiveStreamingServices = new ArraySet<>(); private final Context mContext; @@ -168,7 +169,7 @@ public class MbmsStreamingSession implements AutoCloseable { 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 @@ public class MbmsStreamingSession implements AutoCloseable { 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 @@ public class MbmsStreamingSession implements AutoCloseable { } 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) { |