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

Commit 4f5e8059 authored by Jaewan Kim's avatar Jaewan Kim
Browse files

MediaSessionManager: Prevent volume keys from being ignored

When the volume key is pressed and the foreground activity doesn't
handle the event, the foreground activity tells the MediaSessionService
for adjusting system volume with its package name returned by
Context#getPackageName().

However, if the foreground process runs on another application's process
by specifying android:process in the AndroidManifest.xml. (e.g.
ApnSettingsActivity in the Settings), its package name wouldn't match
with the UID returned by the Binder.getCallingUid(). Because of the
mismatch, the app ops check in the AudioService will fail and
SecurityException will be thrown.

This fixes the issue by two ways
  1. Use Context#getOpPackageName() for sending package name.
  2. Use system service's package name and UID when adjusting volume
     with the hardware volume key presses.

Bug: 110525559
Test: Verified manually that the issue doesn't happen.
      And run all media CTS
Change-Id: I8bba691a36cbe442212aab3f0b23c97533009052
parent bd2a8811
Loading
Loading
Loading
Loading
+31 −32
Original line number Diff line number Diff line
@@ -130,9 +130,7 @@ public final class MediaController {
    }

    /**
     * Dispatches the media button event as system service to the session. This only effects the
     * {@link MediaSession.Callback#getCurrentControllerInfo()} and doesn't bypass any permission
     * check done by the system service.
     * Dispatches the media button event as system service to the session.
     * <p>
     * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the
     * foreground activity didn't consume the key from the hardware devices.
@@ -154,7 +152,7 @@ public final class MediaController {
            return false;
        }
        try {
            return mSessionBinder.sendMediaButton(mContext.getPackageName(), mCbStub,
            return mSessionBinder.sendMediaButton(mContext.getOpPackageName(), mCbStub,
                    asSystemService, keyEvent);
        } catch (RemoteException e) {
            // System is dead. =(
@@ -163,9 +161,7 @@ public final class MediaController {
    }

    /**
     * Dispatches the volume button event as system service to the session. This only effects the
     * {@link MediaSession.Callback#getCurrentControllerInfo()} and doesn't bypass any permission
     * check done by the system service.
     * Dispatches the volume button event as system service to the session.
     * <p>
     * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the
     * foreground activity didn't consume the key from the hardware devices.
@@ -189,8 +185,8 @@ public final class MediaController {
                        break;
                }
                try {
                    mSessionBinder.adjustVolume(mContext.getPackageName(), mCbStub, true, direction,
                            AudioManager.FLAG_SHOW_UI);
                    mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true,
                            direction, AudioManager.FLAG_SHOW_UI);
                } catch (RemoteException e) {
                    Log.wtf(TAG, "Error calling adjustVolumeBy", e);
                }
@@ -200,7 +196,8 @@ public final class MediaController {
                final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE
                        | AudioManager.FLAG_FROM_KEY;
                try {
                    mSessionBinder.adjustVolume(mContext.getPackageName(), mCbStub, true, 0, flags);
                    mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true, 0,
                            flags);
                } catch (RemoteException e) {
                    Log.wtf(TAG, "Error calling adjustVolumeBy", e);
                }
@@ -369,7 +366,7 @@ public final class MediaController {
     */
    public void setVolumeTo(int value, int flags) {
        try {
            mSessionBinder.setVolumeTo(mContext.getPackageName(), mCbStub, value, flags);
            mSessionBinder.setVolumeTo(mContext.getOpPackageName(), mCbStub, value, flags);
        } catch (RemoteException e) {
            Log.wtf(TAG, "Error calling setVolumeTo.", e);
        }
@@ -390,7 +387,7 @@ public final class MediaController {
     */
    public void adjustVolume(int direction, int flags) {
        try {
            mSessionBinder.adjustVolume(mContext.getPackageName(), mCbStub, false, direction,
            mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, false, direction,
                    flags);
        } catch (RemoteException e) {
            Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
@@ -457,7 +454,7 @@ public final class MediaController {
            throw new IllegalArgumentException("command cannot be null or empty");
        }
        try {
            mSessionBinder.sendCommand(mContext.getPackageName(), mCbStub, command, args, cb);
            mSessionBinder.sendCommand(mContext.getOpPackageName(), mCbStub, command, args, cb);
        } catch (RemoteException e) {
            Log.d(TAG, "Dead object in sendCommand.", e);
        }
@@ -522,7 +519,7 @@ public final class MediaController {

        if (!mCbRegistered) {
            try {
                mSessionBinder.registerCallbackListener(mContext.getPackageName(), mCbStub);
                mSessionBinder.registerCallbackListener(mContext.getOpPackageName(), mCbStub);
                mCbRegistered = true;
            } catch (RemoteException e) {
                Log.e(TAG, "Dead object in registerCallback", e);
@@ -669,7 +666,7 @@ public final class MediaController {
         */
        public void prepare() {
            try {
                mSessionBinder.prepare(mContext.getPackageName(), mCbStub);
                mSessionBinder.prepare(mContext.getOpPackageName(), mCbStub);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling prepare.", e);
            }
@@ -693,7 +690,7 @@ public final class MediaController {
                        "You must specify a non-empty String for prepareFromMediaId.");
            }
            try {
                mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mCbStub, mediaId,
                mSessionBinder.prepareFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId,
                        extras);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
@@ -720,7 +717,8 @@ public final class MediaController {
                query = "";
            }
            try {
                mSessionBinder.prepareFromSearch(mContext.getPackageName(), mCbStub, query, extras);
                mSessionBinder.prepareFromSearch(mContext.getOpPackageName(), mCbStub, query,
                        extras);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
            }
@@ -744,7 +742,7 @@ public final class MediaController {
                        "You must specify a non-empty Uri for prepareFromUri.");
            }
            try {
                mSessionBinder.prepareFromUri(mContext.getPackageName(), mCbStub, uri, extras);
                mSessionBinder.prepareFromUri(mContext.getOpPackageName(), mCbStub, uri, extras);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
            }
@@ -755,7 +753,7 @@ public final class MediaController {
         */
        public void play() {
            try {
                mSessionBinder.play(mContext.getPackageName(), mCbStub);
                mSessionBinder.play(mContext.getOpPackageName(), mCbStub);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling play.", e);
            }
@@ -774,7 +772,8 @@ public final class MediaController {
                        "You must specify a non-empty String for playFromMediaId.");
            }
            try {
                mSessionBinder.playFromMediaId(mContext.getPackageName(), mCbStub, mediaId, extras);
                mSessionBinder.playFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId,
                        extras);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
            }
@@ -796,7 +795,7 @@ public final class MediaController {
                query = "";
            }
            try {
                mSessionBinder.playFromSearch(mContext.getPackageName(), mCbStub, query, extras);
                mSessionBinder.playFromSearch(mContext.getOpPackageName(), mCbStub, query, extras);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling play(" + query + ").", e);
            }
@@ -815,7 +814,7 @@ public final class MediaController {
                        "You must specify a non-empty Uri for playFromUri.");
            }
            try {
                mSessionBinder.playFromUri(mContext.getPackageName(), mCbStub, uri, extras);
                mSessionBinder.playFromUri(mContext.getOpPackageName(), mCbStub, uri, extras);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling play(" + uri + ").", e);
            }
@@ -827,7 +826,7 @@ public final class MediaController {
         */
        public void skipToQueueItem(long id) {
            try {
                mSessionBinder.skipToQueueItem(mContext.getPackageName(), mCbStub, id);
                mSessionBinder.skipToQueueItem(mContext.getOpPackageName(), mCbStub, id);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
            }
@@ -839,7 +838,7 @@ public final class MediaController {
         */
        public void pause() {
            try {
                mSessionBinder.pause(mContext.getPackageName(), mCbStub);
                mSessionBinder.pause(mContext.getOpPackageName(), mCbStub);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling pause.", e);
            }
@@ -851,7 +850,7 @@ public final class MediaController {
         */
        public void stop() {
            try {
                mSessionBinder.stop(mContext.getPackageName(), mCbStub);
                mSessionBinder.stop(mContext.getOpPackageName(), mCbStub);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling stop.", e);
            }
@@ -864,7 +863,7 @@ public final class MediaController {
         */
        public void seekTo(long pos) {
            try {
                mSessionBinder.seekTo(mContext.getPackageName(), mCbStub, pos);
                mSessionBinder.seekTo(mContext.getOpPackageName(), mCbStub, pos);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling seekTo.", e);
            }
@@ -876,7 +875,7 @@ public final class MediaController {
         */
        public void fastForward() {
            try {
                mSessionBinder.fastForward(mContext.getPackageName(), mCbStub);
                mSessionBinder.fastForward(mContext.getOpPackageName(), mCbStub);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling fastForward.", e);
            }
@@ -887,7 +886,7 @@ public final class MediaController {
         */
        public void skipToNext() {
            try {
                mSessionBinder.next(mContext.getPackageName(), mCbStub);
                mSessionBinder.next(mContext.getOpPackageName(), mCbStub);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling next.", e);
            }
@@ -899,7 +898,7 @@ public final class MediaController {
         */
        public void rewind() {
            try {
                mSessionBinder.rewind(mContext.getPackageName(), mCbStub);
                mSessionBinder.rewind(mContext.getOpPackageName(), mCbStub);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling rewind.", e);
            }
@@ -910,7 +909,7 @@ public final class MediaController {
         */
        public void skipToPrevious() {
            try {
                mSessionBinder.previous(mContext.getPackageName(), mCbStub);
                mSessionBinder.previous(mContext.getOpPackageName(), mCbStub);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling previous.", e);
            }
@@ -925,7 +924,7 @@ public final class MediaController {
         */
        public void setRating(Rating rating) {
            try {
                mSessionBinder.rate(mContext.getPackageName(), mCbStub, rating);
                mSessionBinder.rate(mContext.getOpPackageName(), mCbStub, rating);
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error calling rate.", e);
            }
@@ -960,7 +959,7 @@ public final class MediaController {
                throw new IllegalArgumentException("CustomAction cannot be null.");
            }
            try {
                mSessionBinder.sendCustomAction(mContext.getPackageName(), mCbStub, action, args);
                mSessionBinder.sendCustomAction(mContext.getOpPackageName(), mCbStub, action, args);
            } catch (RemoteException e) {
                Log.d(TAG, "Dead object in sendCustomAction.", e);
            }
+10 −8
Original line number Diff line number Diff line
@@ -321,7 +321,7 @@ public final class MediaSessionManager {
    private void dispatchMediaKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
            boolean needWakeLock) {
        try {
            mService.dispatchMediaKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,
            mService.dispatchMediaKeyEvent(mContext.getOpPackageName(), asSystemService, keyEvent,
                    needWakeLock);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to send key event.", e);
@@ -357,7 +357,7 @@ public final class MediaSessionManager {
    private void dispatchVolumeKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
            int stream, boolean musicOnly) {
        try {
            mService.dispatchVolumeKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,
            mService.dispatchVolumeKeyEvent(mContext.getOpPackageName(), asSystemService, keyEvent,
                    stream, musicOnly);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to send volume key event.", e);
@@ -378,7 +378,7 @@ public final class MediaSessionManager {
     */
    public void dispatchAdjustVolume(int suggestedStream, int direction, int flags) {
        try {
            mService.dispatchAdjustVolume(mContext.getPackageName(), suggestedStream, direction,
            mService.dispatchAdjustVolume(mContext.getOpPackageName(), suggestedStream, direction,
                    flags);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to send adjust volume.", e);
@@ -460,7 +460,7 @@ public final class MediaSessionManager {
        try {
            List<Bundle> bundles = mService.getSessionTokens(
                    /* activeSessionOnly */ true, /* sessionServiceOnly */ false,
                    mContext.getPackageName());
                    mContext.getOpPackageName());
            return toTokenList(bundles);
        } catch (RemoteException e) {
            Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -483,7 +483,7 @@ public final class MediaSessionManager {
        try {
            List<Bundle> bundles = mService.getSessionTokens(
                    /* activeSessionOnly */ false, /* sessionServiceOnly */ true,
                    mContext.getPackageName());
                    mContext.getOpPackageName());
            return toTokenList(bundles);
        } catch (RemoteException e) {
            Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -508,7 +508,7 @@ public final class MediaSessionManager {
        try {
            List<Bundle> bundles = mService.getSessionTokens(
                    /* activeSessionOnly */ false, /* sessionServiceOnly */ false,
                    mContext.getPackageName());
                    mContext.getOpPackageName());
            return toTokenList(bundles);
        } catch (RemoteException e) {
            Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -561,7 +561,8 @@ public final class MediaSessionManager {
            SessionTokensChangedWrapper wrapper = new SessionTokensChangedWrapper(
                    mContext, executor, listener);
            try {
                mService.addSessionTokensListener(wrapper.mStub, userId, mContext.getPackageName());
                mService.addSessionTokensListener(wrapper.mStub, userId,
                        mContext.getOpPackageName());
                mSessionTokensListener.put(listener, wrapper);
            } catch (RemoteException e) {
                Log.e(TAG, "Error in addSessionTokensListener.", e);
@@ -584,7 +585,8 @@ public final class MediaSessionManager {
            SessionTokensChangedWrapper wrapper = mSessionTokensListener.remove(listener);
            if (wrapper != null) {
                try {
                    mService.removeSessionTokensListener(wrapper.mStub, mContext.getPackageName());
                    mService.removeSessionTokensListener(wrapper.mStub,
                            mContext.getOpPackageName());
                } catch (RemoteException e) {
                    Log.e(TAG, "Error in removeSessionTokensListener.", e);
                } finally {
+7 −4
Original line number Diff line number Diff line
@@ -255,8 +255,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
        if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
            // Adjust the volume with a handler not to be blocked by other system service.
            int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
            postAdjustLocalVolume(stream, direction, flags, packageName, uid, useSuggested,
                    previousFlagPlaySound);
            postAdjustLocalVolume(stream, direction, flags, packageName, uid, asSystemService,
                    useSuggested, previousFlagPlaySound);
        } else {
            if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
                // Nothing to do, the volume cannot be changed
@@ -462,8 +462,11 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
    }

    private void postAdjustLocalVolume(final int stream, final int direction, final int flags,
            final String packageName, final int uid, final boolean useSuggested,
            final int previousFlagPlaySound) {
            final String callingPackageName, final int callingUid, final boolean asSystemService,
            final boolean useSuggested, final int previousFlagPlaySound) {
        final String packageName = asSystemService
                ? mContext.getOpPackageName() : callingPackageName;
        final int uid = asSystemService ? Process.SYSTEM_UID : callingUid;
        mHandler.post(new Runnable() {
            @Override
            public void run() {
+1 −1
Original line number Diff line number Diff line
@@ -1844,7 +1844,7 @@ public class MediaSessionService extends SystemService implements Monitor {
                            String packageName = getContext().getOpPackageName();
                            mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
                                    flags, packageName, TAG);
                        } catch (RemoteException e) {
                        } catch (RemoteException|SecurityException e) {
                            Log.e(TAG, "Error adjusting default volume.", e);
                        } catch (IllegalArgumentException e) {
                            Log.e(TAG, "Cannot adjust volume: direction=" + direction