Merge "Clean up Session2Record in MCS" into sc-dev

This commit is contained in:
Kyunglyul Hyun
2021-04-29 05:37:00 +00:00
committed by Android (Google) Code Review
2 changed files with 148 additions and 80 deletions

View File

@@ -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);
} }

View File

@@ -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,15 +111,15 @@ 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
CallbackRecord findCallbackRecordLocked(@Nullable IMediaCommunicationServiceCallback callback) { CallbackRecord findCallbackRecordLocked(@Nullable IMediaCommunicationServiceCallback callback) {
@@ -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> getSession2Tokens(int userId) { public List<Session2Token> getAllSession2Tokens() {
synchronized (mUserLock) {
return mSessionRecords.stream() return mSessionRecords.stream()
.filter(record -> record.isActive()
&& (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 List<Session2Token> getSession2Tokens(int userId) {
synchronized (mLock) { synchronized (mUserLock) {
for (Session2Record record : mSessionRecords) { return mSessionRecords.stream()
if (userId == UserHandle.ALL.getIdentifier() .filter(record -> record.getUserId() == userId)
|| record.getUserId() == userId) { .map(Session2Record::getSessionToken)
destroySessionLocked(record); .collect(Collectors.toList());
} }
} }
public void destroyAllSessions() {
synchronized (mUserLock) {
for (Session2Record session : mSessionRecords) {
session.close();
}
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();