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

Commit c3a5109a authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "MediaSessionManager: Add listener for Session2Token changes"

parents 413ba846 ea911860
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -502,6 +502,7 @@ java_defaults {
        "media/java/android/media/session/IOnMediaKeyListener.aidl",
        "media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl",
        "media/java/android/media/session/ISession.aidl",
        "media/java/android/media/session/ISession2TokensListener.aidl",
        "media/java/android/media/session/ISessionCallback.aidl",
        "media/java/android/media/session/ISessionController.aidl",
        "media/java/android/media/session/ISessionControllerCallback.aidl",
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media.session;

import android.media.Session2Token;

/**
 * Listens for changes to the list of session2 tokens.
 * @hide
 */
oneway interface ISession2TokensListener {
    void onSession2TokensChanged(in List<Session2Token> tokens);
}
+3 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.media.session.ICallback;
import android.media.session.IOnMediaKeyListener;
import android.media.session.IOnVolumeKeyLongPressListener;
import android.media.session.ISession;
import android.media.session.ISession2TokensListener;
import android.media.session.SessionCallbackLink;
import android.os.Bundle;
import android.view.KeyEvent;
@@ -45,6 +46,8 @@ interface ISessionManager {
    void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
            int userId);
    void removeSessionsListener(in IActiveSessionsListener listener);
    void addSession2TokensListener(in ISession2TokensListener listener, int userId);
    void removeSession2TokensListener(in ISession2TokensListener listener);

    // This is for the system volume UI only
    void setRemoteVolumeController(in IRemoteVolumeController rvc);
@@ -56,6 +59,5 @@ interface ISessionManager {
    void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener);
    void setOnMediaKeyListener(in IOnMediaKeyListener listener);

    // MediaSession2
    boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
}
+129 −1
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@ import android.util.ArrayMap;
import android.util.Log;
import android.view.KeyEvent;

import com.android.internal.annotations.GuardedBy;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -69,9 +71,13 @@ public final class MediaSessionManager {
     */
    public static final int RESULT_MEDIA_KEY_HANDLED = 1;

    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners
            = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>();
    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private final ArrayMap<OnSession2TokensChangedListener, Session2TokensChangedWrapper>
            mSession2TokensListeners = new ArrayMap<>();
    private final ISessionManager mService;

    private Context mContext;
@@ -323,6 +329,87 @@ public final class MediaSessionManager {
        }
    }

    /**
     * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
     * <p>
     * This API is not generally intended for third party application developers.
     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
     * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
     * for consistent behavior across all devices.
     *
     * @param listener The listener to add
     * @param handler The handler to call listener on. If {@code null}, calling thread's looper will
     *                be used.
     * @hide
     */
    // TODO(jaewan): Unhide
    public void addOnSession2TokensChangedListener(
            @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) {
        addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, handler);
    }

    /**
     * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
     * <p>
     * This API is not generally intended for third party application developers.
     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
     * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
     * for consistent behavior across all devices.
     *
     * @param userId The userId to listen for changes on
     * @param listener The listener to add
     * @param handler The handler to call listener on. If {@code null}, calling thread's looper will
     *                be used.
     * @hide
     */
    public void addOnSession2TokensChangedListener(int userId,
            @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) {
        if (listener == null) {
            throw new IllegalArgumentException("listener shouldn't be null");
        }
        synchronized (mLock) {
            if (mSession2TokensListeners.get(listener) != null) {
                Log.w(TAG, "Attempted to add session listener twice, ignoring.");
                return;
            }
            Session2TokensChangedWrapper wrapper =
                    new Session2TokensChangedWrapper(listener, handler);
            try {
                mService.addSession2TokensListener(wrapper.getStub(), userId);
                mSession2TokensListeners.put(listener, wrapper);
            } catch (RemoteException e) {
                Log.e(TAG, "Error in addSessionTokensListener.", e);
                e.rethrowFromSystemServer();
            }
        }
    }

    /**
     * Removes the {@link OnSession2TokensChangedListener} to stop receiving session token updates.
     *
     * @param listener The listener to remove.
     * @hide
     */
    // TODO(jaewan): Unhide
    public void removeOnSession2TokensChangedListener(
            @NonNull OnSession2TokensChangedListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("listener may not be null");
        }
        final Session2TokensChangedWrapper wrapper;
        synchronized (mLock) {
            wrapper = mSession2TokensListeners.remove(listener);
        }
        if (wrapper != null) {
            try {
                mService.removeSession2TokensListener(wrapper.getStub());
            } catch (RemoteException e) {
                Log.e(TAG, "Error in removeSessionTokensListener.", e);
                e.rethrowFromSystemServer();
            }
        }
    }

    /**
     * Set the remote volume controller to receive volume updates on. Only for
     * use by system UI.
@@ -589,6 +676,26 @@ public final class MediaSessionManager {
        public void onActiveSessionsChanged(@Nullable List<MediaController> controllers);
    }

    /**
     * Listens for changes to the {@link #getSession2Tokens()}. This can be added
     * using {@link #addOnSession2TokensChangedListener(OnSession2TokensChangedListener, Handler)}.
     * <p>
     * This API is not generally intended for third party application developers.
     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
     * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
     * for consistent behavior across all devices.
     *
     * @hide
     */
    public interface OnSession2TokensChangedListener {
        /**
         * Called when the {@link #getSession2Tokens()} is changed.
         *
         * @param tokens list of {@link Session2Token}
         */
        void onSession2TokensChanged(@NonNull List<Session2Token> tokens);
    }

    /**
     * Listens the volume key long-presses.
     * @hide
@@ -807,6 +914,27 @@ public final class MediaSessionManager {
        }
    }

    private static final class Session2TokensChangedWrapper {
        private final OnSession2TokensChangedListener mListener;
        private final Handler mHandler;
        private final ISession2TokensListener.Stub mStub =
                new ISession2TokensListener.Stub() {
                    @Override
                    public void onSession2TokensChanged(final List<Session2Token> tokens) {
                        mHandler.post(() -> mListener.onSession2TokensChanged(tokens));
                    }
                };

        Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Handler handler) {
            mListener = listener;
            mHandler = (handler == null) ? new Handler() : new Handler(handler.getLooper());
        }

        public ISession2TokensListener.Stub getStub() {
            return mStub;
        }
    }

    private static final class OnVolumeKeyLongPressListenerImpl
            extends IOnVolumeKeyLongPressListener.Stub {
        private OnVolumeKeyLongPressListener mListener;
+141 −31
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.media;

import static android.os.UserHandle.USER_ALL;

import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.INotificationManager;
@@ -48,6 +50,7 @@ import android.media.session.ICallback;
import android.media.session.IOnMediaKeyListener;
import android.media.session.IOnVolumeKeyLongPressListener;
import android.media.session.ISession;
import android.media.session.ISession2TokensListener;
import android.media.session.ISessionManager;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
@@ -119,6 +122,9 @@ public class MediaSessionService extends SystemService implements Monitor {
    //       one place.
    @GuardedBy("mLock")
    private final SparseArray<List<Session2Token>> mSession2TokensPerUser = new SparseArray<>();
    @GuardedBy("mLock")
    private final List<Session2TokensListenerRecord> mSession2TokensListenerRecords =
            new ArrayList<>();

    private KeyguardManager mKeyguardManager;
    private IAudioService mAudioService;
@@ -235,7 +241,7 @@ public class MediaSessionService extends SystemService implements Monitor {

    private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
        List<MediaSessionRecord> records = new ArrayList<>();
        if (userId == UserHandle.USER_ALL) {
        if (userId == USER_ALL) {
            int size = mUserRecords.size();
            for (int i = 0; i < size; i++) {
                records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
@@ -251,13 +257,24 @@ public class MediaSessionService extends SystemService implements Monitor {

        // Return global priority session at the first whenever it's asked.
        if (isGlobalPriorityActiveLocked()
                && (userId == UserHandle.USER_ALL
                    || userId == mGlobalPrioritySession.getUserId())) {
                && (userId == USER_ALL || userId == mGlobalPrioritySession.getUserId())) {
            records.add(0, mGlobalPrioritySession);
        }
        return records;
    }

    List<Session2Token> getSession2TokensLocked(int userId) {
        List<Session2Token> list = new ArrayList<>();
        if (userId == USER_ALL) {
            for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
                list.addAll(mSession2TokensPerUser.valueAt(i));
            }
        } else {
            list.addAll(mSession2TokensPerUser.get(userId));
        }
        return list;
    }

    /**
     * Tells the system UI that volume has changed on an active remote session.
     */
@@ -316,7 +333,7 @@ public class MediaSessionService extends SystemService implements Monitor {
            FullUserRecord user = getFullUserRecordLocked(userId);
            if (user != null) {
                if (user.mFullUserId == userId) {
                    user.destroySessionsForUserLocked(UserHandle.USER_ALL);
                    user.destroySessionsForUserLocked(USER_ALL);
                    mUserRecords.remove(userId);
                } else {
                    user.destroySessionsForUserLocked(userId);
@@ -393,14 +410,14 @@ public class MediaSessionService extends SystemService implements Monitor {
            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
                SessionsListenerRecord listener = mSessionsListeners.get(i);
                try {
                    enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
                            listener.mUserId);
                    enforceMediaPermissions(listener.componentName, listener.pid, listener.uid,
                            listener.userId);
                } catch (SecurityException e) {
                    Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
                    Log.i(TAG, "ActiveSessionsListener " + listener.componentName
                            + " is no longer authorized. Disconnecting.");
                    mSessionsListeners.remove(i);
                    try {
                        listener.mListener
                        listener.listener
                                .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
                    } catch (Exception e1) {
                        // ignore
@@ -562,13 +579,23 @@ public class MediaSessionService extends SystemService implements Monitor {

    private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
        for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
            if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
            if (mSessionsListeners.get(i).listener.asBinder() == listener.asBinder()) {
                return i;
            }
        }
        return -1;
    }

    private int findIndexOfSession2TokensListenerLocked(ISession2TokensListener listener) {
        for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) {
            if (mSession2TokensListenerRecords.get(i).listener.asBinder() == listener.asBinder()) {
                return i;
            }
        }
        return -1;
    }


    private void pushSessionsChanged(int userId) {
        synchronized (mLock) {
            FullUserRecord user = getFullUserRecordLocked(userId);
@@ -585,9 +612,9 @@ public class MediaSessionService extends SystemService implements Monitor {
            pushRemoteVolumeUpdateLocked(userId);
            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
                SessionsListenerRecord record = mSessionsListeners.get(i);
                if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
                if (record.userId == USER_ALL || record.userId == userId) {
                    try {
                        record.mListener.onActiveSessionsChanged(tokens);
                        record.listener.onActiveSessionsChanged(tokens);
                    } catch (RemoteException e) {
                        Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
                                e);
@@ -614,6 +641,25 @@ public class MediaSessionService extends SystemService implements Monitor {
        }
    }

    void pushSession2TokensChangedLocked(int userId) {
        List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL);
        List<Session2Token> session2Tokens = getSession2TokensLocked(userId);

        for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) {
            Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i);
            try {
                if (listenerRecord.userId == USER_ALL) {
                    listenerRecord.listener.onSession2TokensChanged(allSession2Tokens);
                } else if (listenerRecord.userId == userId) {
                    listenerRecord.listener.onSession2TokensChanged(session2Tokens);
                }
            } catch (RemoteException e) {
                Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e);
                mSession2TokensListenerRecords.remove(i);
            }
        }
    }

    /**
     * Called when the media button receiver for the {@param record} is changed.
     *
@@ -855,20 +901,20 @@ public class MediaSessionService extends SystemService implements Monitor {
    }

    final class SessionsListenerRecord implements IBinder.DeathRecipient {
        private final IActiveSessionsListener mListener;
        private final ComponentName mComponentName;
        private final int mUserId;
        private final int mPid;
        private final int mUid;
        public final IActiveSessionsListener listener;
        public final ComponentName componentName;
        public final int userId;
        public final int pid;
        public final int uid;

        public SessionsListenerRecord(IActiveSessionsListener listener,
                ComponentName componentName,
                int userId, int pid, int uid) {
            mListener = listener;
            mComponentName = componentName;
            mUserId = userId;
            mPid = pid;
            mUid = uid;
            this.listener = listener;
            this.componentName = componentName;
            this.userId = userId;
            this.pid = pid;
            this.uid = uid;
        }

        @Override
@@ -879,6 +925,24 @@ public class MediaSessionService extends SystemService implements Monitor {
        }
    }

    final class Session2TokensListenerRecord implements IBinder.DeathRecipient {
        public final ISession2TokensListener listener;
        public final int userId;

        Session2TokensListenerRecord(ISession2TokensListener listener,
                int userId) {
            this.listener = listener;
            this.userId = userId;
        }

        @Override
        public void binderDied() {
            synchronized (mLock) {
                mSession2TokensListenerRecords.remove(this);
            }
        }
    }

    final class SettingsObserver extends ContentObserver {
        private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
@@ -889,7 +953,7 @@ public class MediaSessionService extends SystemService implements Monitor {

        private void observe() {
            mContentResolver.registerContentObserver(mSecureSettingsUri,
                    false, this, UserHandle.USER_ALL);
                    false, this, USER_ALL);
        }

        @Override
@@ -984,15 +1048,9 @@ public class MediaSessionService extends SystemService implements Monitor {
                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
                        true /* allowAll */, true /* requireFull */, "getSession2Tokens",
                        null /* optional packageName */);
                List<Session2Token> result = new ArrayList<>();
                List<Session2Token> result;
                synchronized (mLock) {
                    if (resolvedUserId == UserHandle.USER_ALL) {
                        for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
                            result.addAll(mSession2TokensPerUser.valueAt(i));
                        }
                    } else {
                        result.addAll(mSession2TokensPerUser.get(userId));
                    }
                    result = getSession2TokensLocked(resolvedUserId);
                }
                return result;
            } finally {
@@ -1038,7 +1096,7 @@ public class MediaSessionService extends SystemService implements Monitor {
                if (index != -1) {
                    SessionsListenerRecord record = mSessionsListeners.remove(index);
                    try {
                        record.mListener.asBinder().unlinkToDeath(record, 0);
                        record.listener.asBinder().unlinkToDeath(record, 0);
                    } catch (Exception e) {
                        // ignore exceptions, the record is being removed
                    }
@@ -1046,6 +1104,56 @@ public class MediaSessionService extends SystemService implements Monitor {
            }
        }

        @Override
        public void addSession2TokensListener(ISession2TokensListener listener,
                int userId) {
            final int pid = Binder.getCallingPid();
            final int uid = Binder.getCallingUid();
            final long token = Binder.clearCallingIdentity();

            try {
                // Check that they can make calls on behalf of the user and get the final user id.
                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
                        true /* allowAll */, true /* requireFull */, "addSession2TokensListener",
                        null /* optional packageName */);
                synchronized (mLock) {
                    int index = findIndexOfSession2TokensListenerLocked(listener);
                    if (index >= 0) {
                        Log.w(TAG, "addSession2TokensListener is already added, ignoring");
                        return;
                    }
                    mSession2TokensListenerRecords.add(
                            new Session2TokensListenerRecord(listener, resolvedUserId));
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void removeSession2TokensListener(ISession2TokensListener listener) {
            final int pid = Binder.getCallingPid();
            final int uid = Binder.getCallingUid();
            final long token = Binder.clearCallingIdentity();

            try {
                synchronized (mLock) {
                    int index = findIndexOfSession2TokensListenerLocked(listener);
                    if (index >= 0) {
                        Session2TokensListenerRecord listenerRecord =
                                mSession2TokensListenerRecords.remove(index);
                        try {
                            listenerRecord.listener.asBinder().unlinkToDeath(listenerRecord, 0);
                        } catch (Exception e) {
                            // Ignore exception.
                        }
                    }
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        /**
         * Handles the dispatching of the media button events to one of the
         * registered listeners, or if there was none, broadcast an
@@ -2012,6 +2120,7 @@ public class MediaSessionService extends SystemService implements Monitor {
            synchronized (mLock) {
                int userId = UserHandle.getUserId(mToken.getUid());
                mSession2TokensPerUser.get(userId).add(mToken);
                pushSession2TokensChangedLocked(userId);
            }
        }

@@ -2020,6 +2129,7 @@ public class MediaSessionService extends SystemService implements Monitor {
            synchronized (mLock) {
                int userId = UserHandle.getUserId(mToken.getUid());
                mSession2TokensPerUser.get(userId).remove(mToken);
                pushSession2TokensChangedLocked(userId);
            }
        }
    }