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

Commit d1b0047d authored by Kyunglyul Hyun's avatar Kyunglyul Hyun Committed by Automerger Merge Worker
Browse files

Merge "Clean up Session2Record in MCS" into sc-dev am: b82f38cf

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14233537

Change-Id: Id9efcd0bec45dfae32bdba70595a88c1af45e3c8
parents 28138e54 b82f38cf
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -19,7 +19,7 @@ import android.media.Session2Token;
import android.media.MediaParceledListSlice;
import android.media.MediaParceledListSlice;


/** {@hide} */
/** {@hide} */
interface IMediaCommunicationServiceCallback {
oneway interface IMediaCommunicationServiceCallback {
    void onSession2Created(in Session2Token token);
    void onSession2Created(in Session2Token token);
    void onSession2Changed(in MediaParceledListSlice tokens);
    void onSession2Changed(in MediaParceledListSlice tokens);
}
}
+142 −74
Original line number Original line Diff line number Diff line
@@ -65,17 +65,17 @@ public class MediaCommunicationService extends SystemService {


    final Context mContext;
    final Context mContext;


    private final Object mLock = new Object();
    final Object mLock = new Object();
    private final Handler mHandler = new Handler(Looper.getMainLooper());
    final Handler mHandler = new Handler(Looper.getMainLooper());


    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private final SparseIntArray mFullUserIds = new SparseIntArray();
    private final SparseIntArray mFullUserIds = new SparseIntArray();
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<>();
    private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<>();


    private final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
    final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
    final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
    final NotificationManager mNotificationManager;
    final NotificationManager mNotificationManager;


    public MediaCommunicationService(Context context) {
    public MediaCommunicationService(Context context) {
@@ -111,14 +111,14 @@ public class MediaCommunicationService extends SystemService {
            FullUserRecord user = getFullUserRecordLocked(userId);
            FullUserRecord user = getFullUserRecordLocked(userId);
            if (user != null) {
            if (user != null) {
                if (user.getFullUserId() == userId) {
                if (user.getFullUserId() == userId) {
                    user.destroySessionsForUserLocked(UserHandle.ALL.getIdentifier());
                    user.destroyAllSessions();
                    mUserRecords.remove(userId);
                    mUserRecords.remove(userId);
                } else {
                } else {
                    user.destroySessionsForUserLocked(userId);
                    user.destroySessionsForUser(userId);
                }
                }
            }
            }
            updateUser();
        }
        }
        updateUser();
    }
    }


    @Nullable
    @Nullable
@@ -134,6 +134,22 @@ public class MediaCommunicationService extends SystemService {
        return null;
        return null;
    }
    }


    List<Session2Token> getSession2TokensLocked(int userId) {
        List<Session2Token> list = new ArrayList<>();
        if (userId == ALL.getIdentifier()) {
            int size = mUserRecords.size();
            for (int i = 0; i < size; i++) {
                list.addAll(mUserRecords.valueAt(i).getAllSession2Tokens());
            }
        } else {
            FullUserRecord user = getFullUserRecordLocked(userId);
            if (user != null) {
                list.addAll(user.getSession2Tokens(userId));
            }
        }
        return list;
    }

    private FullUserRecord getFullUserRecordLocked(int userId) {
    private FullUserRecord getFullUserRecordLocked(int userId) {
        int fullUserId = mFullUserIds.get(userId, -1);
        int fullUserId = mFullUserIds.get(userId, -1);
        if (fullUserId < 0) {
        if (fullUserId < 0) {
@@ -188,7 +204,8 @@ public class MediaCommunicationService extends SystemService {
        }
        }
    }
    }


    void dispatchSessionCreated(Session2Token token) {
    void dispatchSession2Created(Session2Token token) {
        synchronized (mLock) {
            for (CallbackRecord record : mCallbackRecords) {
            for (CallbackRecord record : mCallbackRecords) {
                if (record.mUserId != ALL.getIdentifier()
                if (record.mUserId != ALL.getIdentifier()
                        && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) {
                        && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) {
@@ -197,18 +214,44 @@ public class MediaCommunicationService extends SystemService {
                try {
                try {
                    record.mCallback.onSession2Created(token);
                    record.mCallback.onSession2Created(token);
                } catch (RemoteException e) {
                } catch (RemoteException e) {
                e.printStackTrace();
                    Log.w(TAG, "Failed to notify session2 token created " + record);
                }
            }
            }
        }
        }
    }
    }


    void onSessionDied(Session2Record record) {
    void dispatchSession2Changed(int userId) {
        MediaParceledListSlice<Session2Token> allSession2Tokens;
        MediaParceledListSlice<Session2Token> userSession2Tokens;

        synchronized (mLock) {
        synchronized (mLock) {
            destroySessionLocked(record);
            allSession2Tokens =
                    new MediaParceledListSlice<>(getSession2TokensLocked(ALL.getIdentifier()));
            userSession2Tokens = new MediaParceledListSlice<>(getSession2TokensLocked(userId));
        }
        allSession2Tokens.setInlineCountLimit(1);
        userSession2Tokens.setInlineCountLimit(1);

        synchronized (mLock) {
            for (CallbackRecord record : mCallbackRecords) {
                if (record.mUserId == ALL.getIdentifier()) {
                    try {
                        record.mCallback.onSession2Changed(allSession2Tokens);
                    } catch (RemoteException e) {
                        Log.w(TAG, "Failed to notify session2 tokens changed " + record);
                    }
                } else if (record.mUserId == userId) {
                    try {
                        record.mCallback.onSession2Changed(userSession2Tokens);
                    } catch (RemoteException e) {
                        Log.w(TAG, "Failed to notify session2 tokens changed " + record);
                    }
                }
            }
        }
        }
    }
    }


    private void destroySessionLocked(Session2Record session) {
    void onSessionDied(Session2Record session) {
        if (DEBUG) {
        if (DEBUG) {
            Log.d(TAG, "Destroying " + session);
            Log.d(TAG, "Destroying " + session);
        }
        }
@@ -217,12 +260,10 @@ public class MediaCommunicationService extends SystemService {
            return;
            return;
        }
        }


        FullUserRecord user = getFullUserRecordLocked(session.getUserId());
        FullUserRecord user = session.getFullUser();

        if (user != null) {
        if (user != null) {
            user.removeSession(session);
            user.removeSession(session);
        }
        }

        session.close();
        session.close();
    }
    }


@@ -241,17 +282,17 @@ public class MediaCommunicationService extends SystemService {
                    throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
                    throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
                            + " but actually=" + sessionToken.getUid());
                            + " but actually=" + sessionToken.getUid());
                }
                }
                synchronized (mLock) {
                FullUserRecord user;
                int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier();
                int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier();
                    FullUserRecord user = getFullUserRecordLocked(userId);
                synchronized (mLock) {
                    user = getFullUserRecordLocked(userId);
                }
                if (user == null) {
                if (user == null) {
                    Log.w(TAG, "notifySession2Created: Ignore session of an unknown user");
                    Log.w(TAG, "notifySession2Created: Ignore session of an unknown user");
                    return;
                    return;
                }
                }
                user.addSession(new Session2Record(MediaCommunicationService.this,
                user.addSession(new Session2Record(MediaCommunicationService.this,
                            sessionToken, mRecordExecutor));
                        user, sessionToken, mRecordExecutor));
                    mHandler.post(() -> dispatchSessionCreated(sessionToken));
                }
            } finally {
            } finally {
                Binder.restoreCallingIdentity(token);
                Binder.restoreCallingIdentity(token);
            }
            }
@@ -299,10 +340,11 @@ public class MediaCommunicationService extends SystemService {
                int resolvedUserId = handleIncomingUser(pid, uid, userId, null);
                int resolvedUserId = handleIncomingUser(pid, uid, userId, null);
                List<Session2Token> result;
                List<Session2Token> result;
                synchronized (mLock) {
                synchronized (mLock) {
                    FullUserRecord user = getFullUserRecordLocked(userId);
                    result = getSession2TokensLocked(resolvedUserId);
                    result = user.getSession2Tokens(resolvedUserId);
                }
                }
                return new MediaParceledListSlice(result);
                MediaParceledListSlice parceledListSlice = new MediaParceledListSlice<>(result);
                parceledListSlice.setInlineCountLimit(1);
                return parceledListSlice;
            } finally {
            } finally {
                Binder.restoreCallingIdentity(token);
                Binder.restoreCallingIdentity(token);
            }
            }
@@ -427,7 +469,8 @@ public class MediaCommunicationService extends SystemService {


    final class FullUserRecord {
    final class FullUserRecord {
        private final int mFullUserId;
        private final int mFullUserId;
        /** Sorted list of media sessions */
        private final Object mUserLock = new Object();
        @GuardedBy("mUserLock")
        private final List<Session2Record> mSessionRecords = new ArrayList<>();
        private final List<Session2Record> mSessionRecords = new ArrayList<>();


        FullUserRecord(int fullUserId) {
        FullUserRecord(int fullUserId) {
@@ -435,11 +478,18 @@ public class MediaCommunicationService extends SystemService {
        }
        }


        public void addSession(Session2Record record) {
        public void addSession(Session2Record record) {
            synchronized (mUserLock) {
                mSessionRecords.add(record);
                mSessionRecords.add(record);
            }
            }
            mHandler.post(() -> dispatchSession2Created(record.mSessionToken));
            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
        }


        public void removeSession(Session2Record record) {
        private void removeSession(Session2Record record) {
            synchronized (mUserLock) {
                mSessionRecords.remove(record);
                mSessionRecords.remove(record);
            }
            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
            //TODO: Handle if the removed session was the media button session.
            //TODO: Handle if the removed session was the media button session.
        }
        }


@@ -447,42 +497,68 @@ public class MediaCommunicationService extends SystemService {
            return mFullUserId;
            return mFullUserId;
        }
        }


        public List<Session2Token> getAllSession2Tokens() {
            synchronized (mUserLock) {
                return mSessionRecords.stream()
                        .map(Session2Record::getSessionToken)
                        .collect(Collectors.toList());
            }
        }

        public List<Session2Token> getSession2Tokens(int userId) {
        public List<Session2Token> getSession2Tokens(int userId) {
            synchronized (mUserLock) {
                return mSessionRecords.stream()
                return mSessionRecords.stream()
                    .filter(record -> record.isActive()
                        .filter(record -> record.getUserId() == userId)
                            && (userId == UserHandle.ALL.getIdentifier()
                                    || record.getUserId() == userId))
                        .map(Session2Record::getSessionToken)
                        .map(Session2Record::getSessionToken)
                        .collect(Collectors.toList());
                        .collect(Collectors.toList());
            }
            }
        }


        public void destroySessionsForUserLocked(int userId) {
        public void destroyAllSessions() {
            synchronized (mLock) {
            synchronized (mUserLock) {
                for (Session2Record record : mSessionRecords) {
                for (Session2Record session : mSessionRecords) {
                    if (userId == UserHandle.ALL.getIdentifier()
                    session.close();
                            || record.getUserId() == userId) {
                }
                        destroySessionLocked(record);
                mSessionRecords.clear();
            }
            }
            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
        }
        }

        public void destroySessionsForUser(int userId) {
            boolean changed = false;
            synchronized (mUserLock) {
                for (int i = mSessionRecords.size() - 1; i >= 0; i--) {
                    Session2Record session = mSessionRecords.get(i);
                    if (session.getUserId() == userId) {
                        mSessionRecords.remove(i);
                        session.close();
                        changed = true;
                    }
                }
            }
            if (changed) {
                mHandler.post(() -> dispatchSession2Changed(mFullUserId));
            }
            }
        }
        }
    }
    }


    static final class Session2Record {
    static final class Session2Record {
        private final Session2Token mSessionToken;
        final Session2Token mSessionToken;
        private final Object mLock = new Object();
        final Object mSession2RecordLock = new Object();
        private final WeakReference<MediaCommunicationService> mServiceRef;
        final WeakReference<MediaCommunicationService> mServiceRef;
        @GuardedBy("mLock")
        final WeakReference<FullUserRecord> mFullUserRef;
        @GuardedBy("mSession2RecordLock")
        private final MediaController2 mController;
        private final MediaController2 mController;


        @GuardedBy("mLock")
        @GuardedBy("mSession2RecordLock")
        private boolean mIsConnected;
        boolean mIsConnected;
        @GuardedBy("mLock")
        @GuardedBy("mSession2RecordLock")
        private boolean mIsClosed;
        private boolean mIsClosed;


        Session2Record(MediaCommunicationService service, Session2Token token,
        Session2Record(MediaCommunicationService service, FullUserRecord fullUser,
                Executor controllerExecutor) {
                Session2Token token, Executor controllerExecutor) {
            mServiceRef = new WeakReference<>(service);
            mServiceRef = new WeakReference<>(service);
            mFullUserRef = new WeakReference<>(fullUser);
            mSessionToken = token;
            mSessionToken = token;
            mController = new MediaController2.Builder(service.getContext(), token)
            mController = new MediaController2.Builder(service.getContext(), token)
                    .setControllerCallback(controllerExecutor, new Controller2Callback())
                    .setControllerCallback(controllerExecutor, new Controller2Callback())
@@ -493,23 +569,19 @@ public class MediaCommunicationService extends SystemService {
            return UserHandle.getUserHandleForUid(mSessionToken.getUid()).getIdentifier();
            return UserHandle.getUserHandleForUid(mSessionToken.getUid()).getIdentifier();
        }
        }


        public boolean isActive() {
        public FullUserRecord getFullUser() {
            synchronized (mLock) {
            return mFullUserRef.get();
                return mIsConnected;
            }
        }
        }


        public boolean isClosed() {
        public boolean isClosed() {
            synchronized (mLock) {
            synchronized (mSession2RecordLock) {
                return mIsClosed;
                return mIsClosed;
            }
            }
        }
        }


        public void close() {
        public void close() {
            synchronized (mLock) {
            synchronized (mSession2RecordLock) {
                mIsClosed = true;
                mIsClosed = true;
                // Call close regardless of the mIsConnected. This may be called when it's not yet
                // connected.
                mController.close();
                mController.close();
            }
            }
        }
        }
@@ -525,13 +597,9 @@ public class MediaCommunicationService extends SystemService {
                if (DEBUG) {
                if (DEBUG) {
                    Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands);
                    Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands);
                }
                }
                synchronized (mLock) {
                synchronized (mSession2RecordLock) {
                    mIsConnected = true;
                    mIsConnected = true;
                }
                }
                MediaCommunicationService service = mServiceRef.get();
                if (service != null) {
                    //TODO: notify session state changed
                }
            }
            }


            @Override
            @Override
@@ -539,7 +607,7 @@ public class MediaCommunicationService extends SystemService {
                if (DEBUG) {
                if (DEBUG) {
                    Log.d(TAG, "disconnected from " + mSessionToken);
                    Log.d(TAG, "disconnected from " + mSessionToken);
                }
                }
                synchronized (mLock) {
                synchronized (mSession2RecordLock) {
                    mIsConnected = false;
                    mIsConnected = false;
                }
                }
                MediaCommunicationService service = mServiceRef.get();
                MediaCommunicationService service = mServiceRef.get();