From 926ebb860a8b5cbdec17f07404272be7c484fd79 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 11 Mar 2015 09:59:49 -0700 Subject: [PATCH] Fix context leak Using an activity context with AudioManager could cause that context to be held on to longer than desired, for example if the caller acquired audio focus but never abandoned it. Fix acquire/abandon in VideoView, and use the application context in AudioManager to mitigate the issue for other misbehaving code. Bug: https://code.google.com/p/android/issues/detail?id=152173 Change-Id: I0fb8390207422c784800dda25b1f2c03d4574bcd --- core/java/android/widget/VideoView.java | 11 ++- media/java/android/media/AudioManager.java | 79 ++++++++++++---------- 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index 572cca2ff2298..47644f9d15713 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -311,6 +311,8 @@ public class VideoView extends SurfaceView mMediaPlayer = null; mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; + AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + am.abandonAudioFocus(null); } } @@ -319,12 +321,13 @@ public class VideoView extends SurfaceView // not ready for playback just yet, will try again later return; } - AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); - // we shouldn't clear the target state, because somebody might have // called start() previously release(false); + + AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); + try { mMediaPlayer = new MediaPlayer(); // TODO: create SubtitleController in MediaPlayer, but we need @@ -650,6 +653,8 @@ public class VideoView extends SurfaceView if (cleartargetstate) { mTargetState = STATE_IDLE; } + AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + am.abandonAudioFocus(null); } } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 620f0ace28277..ed6ce875f1d33 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -58,7 +58,7 @@ import java.util.Iterator; */ public class AudioManager { - private final Context mContext; + private final Context mApplicationContext; private long mVolumeKeyUpTime; private final boolean mUseMasterVolume; private final boolean mUseVolumeKeySounds; @@ -641,12 +641,12 @@ public class AudioManager { * @hide */ public AudioManager(Context context) { - mContext = context; - mUseMasterVolume = mContext.getResources().getBoolean( + mApplicationContext = context.getApplicationContext(); + mUseMasterVolume = mApplicationContext.getResources().getBoolean( com.android.internal.R.bool.config_useMasterVolume); - mUseVolumeKeySounds = mContext.getResources().getBoolean( + mUseVolumeKeySounds = mApplicationContext.getResources().getBoolean( com.android.internal.R.bool.config_useVolumeKeySounds); - mUseFixedVolume = mContext.getResources().getBoolean( + mUseFixedVolume = mApplicationContext.getResources().getBoolean( com.android.internal.R.bool.config_useFixedVolume); sAudioPortEventHandler.init(); } @@ -685,7 +685,7 @@ public class AudioManager { * or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}. */ public void dispatchMediaKeyEvent(KeyEvent keyEvent) { - MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); + MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext); helper.sendMediaButtonEvent(keyEvent, false); } @@ -746,7 +746,8 @@ public class AudioManager { break; case KeyEvent.KEYCODE_VOLUME_MUTE: if (event.getRepeatCount() == 0) { - MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false); + MediaSessionLegacyHelper.getHelper(mApplicationContext) + .sendVolumeKeyEvent(event, false); } break; } @@ -778,7 +779,8 @@ public class AudioManager { mVolumeKeyUpTime = SystemClock.uptimeMillis(); break; case KeyEvent.KEYCODE_VOLUME_MUTE: - MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false); + MediaSessionLegacyHelper.getHelper(mApplicationContext) + .sendVolumeKeyEvent(event, false); break; } } @@ -823,10 +825,11 @@ public class AudioManager { IAudioService service = getService(); try { if (mUseMasterVolume) { - service.adjustMasterVolume(direction, flags, mContext.getOpPackageName()); + service.adjustMasterVolume(direction, flags, + mApplicationContext.getOpPackageName()); } else { service.adjustStreamVolume(streamType, direction, flags, - mContext.getOpPackageName()); + mApplicationContext.getOpPackageName()); } } catch (RemoteException e) { Log.e(TAG, "Dead object in adjustStreamVolume", e); @@ -856,9 +859,11 @@ public class AudioManager { IAudioService service = getService(); try { if (mUseMasterVolume) { - service.adjustMasterVolume(direction, flags, mContext.getOpPackageName()); + service.adjustMasterVolume(direction, flags, + mApplicationContext.getOpPackageName()); } else { - MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); + MediaSessionLegacyHelper helper = + MediaSessionLegacyHelper.getHelper(mApplicationContext); helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags); } } catch (RemoteException e) { @@ -890,9 +895,11 @@ public class AudioManager { IAudioService service = getService(); try { if (mUseMasterVolume) { - service.adjustMasterVolume(direction, flags, mContext.getOpPackageName()); + service.adjustMasterVolume(direction, flags, + mApplicationContext.getOpPackageName()); } else { - MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); + MediaSessionLegacyHelper helper = + MediaSessionLegacyHelper.getHelper(mApplicationContext); helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags); } } catch (RemoteException e) { @@ -912,7 +919,7 @@ public class AudioManager { public void adjustMasterVolume(int steps, int flags) { IAudioService service = getService(); try { - service.adjustMasterVolume(steps, flags, mContext.getOpPackageName()); + service.adjustMasterVolume(steps, flags, mApplicationContext.getOpPackageName()); } catch (RemoteException e) { Log.e(TAG, "Dead object in adjustMasterVolume", e); } @@ -1053,7 +1060,7 @@ public class AudioManager { } IAudioService service = getService(); try { - service.setRingerModeExternal(ringerMode, mContext.getOpPackageName()); + service.setRingerModeExternal(ringerMode, mApplicationContext.getOpPackageName()); } catch (RemoteException e) { Log.e(TAG, "Dead object in setRingerMode", e); } @@ -1075,9 +1082,10 @@ public class AudioManager { IAudioService service = getService(); try { if (mUseMasterVolume) { - service.setMasterVolume(index, flags, mContext.getOpPackageName()); + service.setMasterVolume(index, flags, mApplicationContext.getOpPackageName()); } else { - service.setStreamVolume(streamType, index, flags, mContext.getOpPackageName()); + service.setStreamVolume(streamType, index, flags, + mApplicationContext.getOpPackageName()); } } catch (RemoteException e) { Log.e(TAG, "Dead object in setStreamVolume", e); @@ -1143,7 +1151,7 @@ public class AudioManager { public void setMasterVolume(int index, int flags) { IAudioService service = getService(); try { - service.setMasterVolume(index, flags, mContext.getOpPackageName()); + service.setMasterVolume(index, flags, mApplicationContext.getOpPackageName()); } catch (RemoteException e) { Log.e(TAG, "Dead object in setMasterVolume", e); } @@ -1244,7 +1252,7 @@ public class AudioManager { public void setMasterMute(boolean state, int flags) { IAudioService service = getService(); try { - service.setMasterMute(state, flags, mContext.getOpPackageName(), mICallBack); + service.setMasterMute(state, flags, mApplicationContext.getOpPackageName(), mICallBack); } catch (RemoteException e) { Log.e(TAG, "Dead object in setMasterMute", e); } @@ -1482,7 +1490,7 @@ public class AudioManager { * @see #startBluetoothSco() */ public boolean isBluetoothScoAvailableOffCall() { - return mContext.getResources().getBoolean( + return mApplicationContext.getResources().getBoolean( com.android.internal.R.bool.config_bluetooth_sco_off_call); } @@ -1534,7 +1542,8 @@ public class AudioManager { public void startBluetoothSco(){ IAudioService service = getService(); try { - service.startBluetoothSco(mICallBack, mContext.getApplicationInfo().targetSdkVersion); + service.startBluetoothSco(mICallBack, + mApplicationContext.getApplicationInfo().targetSdkVersion); } catch (RemoteException e) { Log.e(TAG, "Dead object in startBluetoothSco", e); } @@ -1682,7 +1691,7 @@ public class AudioManager { public void setMicrophoneMute(boolean on){ IAudioService service = getService(); try { - service.setMicrophoneMute(on, mContext.getOpPackageName()); + service.setMicrophoneMute(on, mApplicationContext.getOpPackageName()); } catch (RemoteException e) { Log.e(TAG, "Dead object in setMicrophoneMute", e); } @@ -2113,7 +2122,7 @@ public class AudioManager { * Settings has an in memory cache, so this is fast. */ private boolean querySoundEffectsEnabled(int user) { - return Settings.System.getIntForUser(mContext.getContentResolver(), + return Settings.System.getIntForUser(mApplicationContext.getContentResolver(), Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0; } @@ -2525,7 +2534,7 @@ public class AudioManager { try { status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack, mAudioFocusDispatcher, getIdForAudioFocusListener(l), - mContext.getOpPackageName() /* package name */, flags, + mApplicationContext.getOpPackageName() /* package name */, flags, ap != null ? ap.cb() : null); } catch (RemoteException e) { Log.e(TAG, "Can't call requestAudioFocus() on AudioService:", e); @@ -2550,7 +2559,7 @@ public class AudioManager { .setInternalLegacyStreamType(streamType).build(), durationHint, mICallBack, null, MediaFocusControl.IN_VOICE_COMM_FOCUS_ID, - mContext.getOpPackageName(), + mApplicationContext.getOpPackageName(), AUDIOFOCUS_FLAG_LOCK, null /* policy token */); } catch (RemoteException e) { @@ -2619,7 +2628,7 @@ public class AudioManager { if (eventReceiver == null) { return; } - if (!eventReceiver.getPackageName().equals(mContext.getPackageName())) { + if (!eventReceiver.getPackageName().equals(mApplicationContext.getPackageName())) { Log.e(TAG, "registerMediaButtonEventReceiver() error: " + "receiver and context package names don't match"); return; @@ -2628,7 +2637,7 @@ public class AudioManager { Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); // the associated intent will be handled by the component being registered mediaButtonIntent.setComponent(eventReceiver); - PendingIntent pi = PendingIntent.getBroadcast(mContext, + PendingIntent pi = PendingIntent.getBroadcast(mApplicationContext, 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/); registerMediaButtonIntent(pi, eventReceiver); } @@ -2662,8 +2671,8 @@ public class AudioManager { Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter"); return; } - MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); - helper.addMediaButtonListener(pi, eventReceiver, mContext); + MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext); + helper.addMediaButtonListener(pi, eventReceiver, mApplicationContext); } /** @@ -2681,7 +2690,7 @@ public class AudioManager { Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); // the associated intent will be handled by the component being registered mediaButtonIntent.setComponent(eventReceiver); - PendingIntent pi = PendingIntent.getBroadcast(mContext, + PendingIntent pi = PendingIntent.getBroadcast(mApplicationContext, 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/); unregisterMediaButtonIntent(pi); } @@ -2704,7 +2713,7 @@ public class AudioManager { * @hide */ public void unregisterMediaButtonIntent(PendingIntent pi) { - MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); + MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mApplicationContext); helper.removeMediaButtonListener(pi); } @@ -2721,7 +2730,7 @@ public class AudioManager { if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) { return; } - rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mContext)); + rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mApplicationContext)); } /** @@ -2736,7 +2745,7 @@ public class AudioManager { if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) { return; } - rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mContext)); + rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mApplicationContext)); } /** @@ -3397,7 +3406,7 @@ public class AudioManager { */ public void setRingerModeInternal(int ringerMode) { try { - getService().setRingerModeInternal(ringerMode, mContext.getOpPackageName()); + getService().setRingerModeInternal(ringerMode, mApplicationContext.getOpPackageName()); } catch (RemoteException e) { Log.w(TAG, "Error calling setRingerModeInternal", e); }