Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 926ebb86 authored by Marco Nelissen's avatar Marco Nelissen
Browse files

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
parent 68694780
Loading
Loading
Loading
Loading
+8 −3
Original line number Diff line number Diff line
@@ -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);
        }
    }

+44 −35
Original line number Diff line number Diff line
@@ -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);
        }