Skip to content

Commit fccb9ad

Browse files
committed
Make frequently called controller callbacks local binder aware
Especially MediaBrowser will profit from this, as List<MediaItem> no longer has to be bundled if the caller is in the same process.
1 parent 2fc5e58 commit fccb9ad

File tree

8 files changed

+121
-6
lines changed

8 files changed

+121
-6
lines changed

libraries/session/src/main/java/androidx/media3/session/LibraryResult.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static java.lang.annotation.ElementType.TYPE_USE;
2121

2222
import android.annotation.SuppressLint;
23+
import android.os.Binder;
2324
import android.os.Bundle;
2425
import android.os.IBinder;
2526
import android.os.SystemClock;
@@ -337,6 +338,7 @@ private static void verifyMediaItem(MediaItem item) {
337338
private static final String FIELD_VALUE = Util.intToStringMaxRadix(3);
338339
private static final String FIELD_VALUE_TYPE = Util.intToStringMaxRadix(4);
339340
private static final String FIELD_SESSION_ERROR = Util.intToStringMaxRadix(5);
341+
private static final String FIELD_IN_PROCESS_BINDER = Util.intToStringMaxRadix(6);
340342

341343
/**
342344
* @deprecated Use {@link #toBundle(int)} instead.
@@ -385,6 +387,17 @@ public Bundle toBundle(int interfaceVersion) {
385387
return bundle;
386388
}
387389

390+
/**
391+
* Returns a {@link Bundle} containing the entirety of this {@link #LibraryResult} object without
392+
* bundling it, for use in local process communication only.
393+
*/
394+
@UnstableApi
395+
public Bundle toBundleForLocalProcess() {
396+
Bundle bundle = new Bundle();
397+
bundle.putBinder(FIELD_IN_PROCESS_BINDER, new InProcessBinder());
398+
return bundle;
399+
}
400+
388401
/**
389402
* @deprecated USe {@link #fromVoidBundle(Bundle, int)} instead.
390403
*/
@@ -484,6 +497,13 @@ public static LibraryResult<?> fromUnknownBundle(Bundle bundle, int interfaceVer
484497
*/
485498
private static LibraryResult<?> fromBundle(
486499
Bundle bundle, @Nullable @ValueType Integer expectedType, int interfaceVersion) {
500+
IBinder inProcessBinder = bundle.getBinder(FIELD_IN_PROCESS_BINDER);
501+
if (inProcessBinder instanceof LibraryResult<?>.InProcessBinder) {
502+
LibraryResult<?> result =
503+
((LibraryResult<?>.InProcessBinder) inProcessBinder).getLibraryResult();
504+
checkState(expectedType == null || expectedType == result.valueType);
505+
return result;
506+
}
487507
int resultCode = bundle.getInt(FIELD_RESULT_CODE, /* defaultValue= */ RESULT_SUCCESS);
488508
long completionTimeMs =
489509
bundle.getLong(FIELD_COMPLETION_TIME_MS, /* defaultValue= */ SystemClock.elapsedRealtime());
@@ -541,4 +561,10 @@ private static LibraryResult<?> fromBundle(
541561

542562
/** The value type isn't known because the result is carrying an error. */
543563
private static final int VALUE_TYPE_ERROR = 4;
564+
565+
private final class InProcessBinder extends Binder {
566+
public LibraryResult<V> getLibraryResult() {
567+
return LibraryResult.this;
568+
}
569+
}
544570
}

libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2875,7 +2875,12 @@ private void sendControllerResult(int seq, SessionResult result) {
28752875
return;
28762876
}
28772877
try {
2878-
iSession.onControllerResult(controllerStub, seq, result.toBundle());
2878+
iSession.onControllerResult(
2879+
controllerStub,
2880+
seq,
2881+
iSession instanceof MediaSessionStub
2882+
? result.toBundleForLocalProcess()
2883+
: result.toBundle());
28792884
} catch (RemoteException e) {
28802885
Log.w(TAG, "Error in sending");
28812886
}

libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2297,13 +2297,21 @@ public IBinder getCallbackBinder() {
22972297

22982298
@Override
22992299
public void onSessionResult(int sequenceNumber, SessionResult result) throws RemoteException {
2300-
iController.onSessionResult(sequenceNumber, result.toBundle());
2300+
iController.onSessionResult(
2301+
sequenceNumber,
2302+
iController instanceof MediaControllerStub
2303+
? result.toBundleForLocalProcess()
2304+
: result.toBundle());
23012305
}
23022306

23032307
@Override
23042308
public void onLibraryResult(int sequenceNumber, LibraryResult<?> result)
23052309
throws RemoteException {
2306-
iController.onLibraryResult(sequenceNumber, result.toBundle(controllerInterfaceVersion));
2310+
iController.onLibraryResult(
2311+
sequenceNumber,
2312+
iController instanceof MediaControllerStub
2313+
? result.toBundleForLocalProcess()
2314+
: result.toBundle(controllerInterfaceVersion));
23072315
}
23082316

23092317
@Override
@@ -2443,11 +2451,14 @@ public void onPeriodicSessionPositionInfoChanged(
24432451
boolean canAccessTimeline,
24442452
int controllerInterfaceVersion)
24452453
throws RemoteException {
2454+
SessionPositionInfo filteredPositionInfo =
2455+
sessionPositionInfo.filterByAvailableCommands(
2456+
canAccessCurrentMediaItem, canAccessTimeline);
24462457
iController.onPeriodicSessionPositionInfoChanged(
24472458
sequenceNumber,
2448-
sessionPositionInfo
2449-
.filterByAvailableCommands(canAccessCurrentMediaItem, canAccessTimeline)
2450-
.toBundle(controllerInterfaceVersion));
2459+
iController instanceof MediaControllerStub
2460+
? filteredPositionInfo.toBundleForLocalProcess()
2461+
: filteredPositionInfo.toBundle(controllerInterfaceVersion));
24512462
}
24522463

24532464
@Override

libraries/session/src/main/java/androidx/media3/session/SessionPositionInfo.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
import static com.google.common.base.Preconditions.checkArgument;
1919

20+
import android.os.Binder;
2021
import android.os.Bundle;
22+
import android.os.IBinder;
2123
import androidx.annotation.Nullable;
2224
import androidx.annotation.VisibleForTesting;
2325
import androidx.media3.common.C;
@@ -170,6 +172,8 @@ public String toString() {
170172
@VisibleForTesting
171173
static final String FIELD_CONTENT_BUFFERED_POSITION_MS = Util.intToStringMaxRadix(9);
172174

175+
private static final String FIELD_IN_PROCESS_BINDER = Util.intToStringMaxRadix(10);
176+
173177
/**
174178
* Returns a copy of this session position info, filtered by the specified available commands.
175179
*
@@ -243,13 +247,23 @@ public static SessionPositionInfo fromBundle(Bundle bundle) {
243247
return fromBundle(bundle, MediaLibraryInfo.INTERFACE_VERSION);
244248
}
245249

250+
public Bundle toBundleForLocalProcess() {
251+
Bundle bundle = new Bundle();
252+
bundle.putBinder(FIELD_IN_PROCESS_BINDER, new InProcessBinder());
253+
return bundle;
254+
}
255+
246256
/**
247257
* Restores a {@code SessionPositionInfo} from a {@link Bundle}.
248258
*
249259
* @param bundle The {@link Bundle}.
250260
* @param interfaceVersion The {@link MediaLibraryInfo#INTERFACE_VERSION} of the sending process.
251261
*/
252262
public static SessionPositionInfo fromBundle(Bundle bundle, int interfaceVersion) {
263+
IBinder inProcessBinder = bundle.getBinder(FIELD_IN_PROCESS_BINDER);
264+
if (inProcessBinder instanceof InProcessBinder) {
265+
return ((InProcessBinder) inProcessBinder).getSessionPositionInfo();
266+
}
253267
@Nullable Bundle positionInfoBundle = bundle.getBundle(FIELD_POSITION_INFO);
254268
PositionInfo positionInfo =
255269
positionInfoBundle == null
@@ -281,4 +295,10 @@ public static SessionPositionInfo fromBundle(Bundle bundle, int interfaceVersion
281295
contentDurationMs,
282296
contentBufferedPositionMs);
283297
}
298+
299+
private final class InProcessBinder extends Binder {
300+
public SessionPositionInfo getSessionPositionInfo() {
301+
return SessionPositionInfo.this;
302+
}
303+
}
284304
}

libraries/session/src/main/java/androidx/media3/session/SessionResult.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import static java.lang.annotation.ElementType.TYPE_USE;
2020

2121
import android.annotation.SuppressLint;
22+
import android.os.Binder;
2223
import android.os.Bundle;
24+
import android.os.IBinder;
2325
import android.os.SystemClock;
2426
import androidx.annotation.IntDef;
2527
import androidx.annotation.Nullable;
@@ -241,6 +243,7 @@ private SessionResult(
241243
private static final String FIELD_EXTRAS = Util.intToStringMaxRadix(1);
242244
private static final String FIELD_COMPLETION_TIME_MS = Util.intToStringMaxRadix(2);
243245
private static final String FIELD_SESSION_ERROR = Util.intToStringMaxRadix(3);
246+
private static final String FIELD_IN_PROCESS_BINDER = Util.intToStringMaxRadix(4);
244247

245248
@UnstableApi
246249
public Bundle toBundle() {
@@ -254,9 +257,24 @@ public Bundle toBundle() {
254257
return bundle;
255258
}
256259

260+
/**
261+
* Returns a {@link Bundle} containing the entirety of this {@link #SessionResult} object without
262+
* bundling it, for use in local process communication only.
263+
*/
264+
@UnstableApi
265+
public Bundle toBundleForLocalProcess() {
266+
Bundle bundle = new Bundle();
267+
bundle.putBinder(FIELD_IN_PROCESS_BINDER, new InProcessBinder());
268+
return bundle;
269+
}
270+
257271
/** Restores a {@code SessionResult} from a {@link Bundle}. */
258272
@UnstableApi
259273
public static SessionResult fromBundle(Bundle bundle) {
274+
IBinder inProcessBinder = bundle.getBinder(FIELD_IN_PROCESS_BINDER);
275+
if (inProcessBinder instanceof InProcessBinder) {
276+
return ((InProcessBinder) inProcessBinder).getSessionResult();
277+
}
260278
int resultCode =
261279
bundle.getInt(FIELD_RESULT_CODE, /* defaultValue= */ SessionError.ERROR_UNKNOWN);
262280
@Nullable Bundle extras = bundle.getBundle(FIELD_EXTRAS);
@@ -274,4 +292,10 @@ public static SessionResult fromBundle(Bundle bundle) {
274292
return new SessionResult(
275293
resultCode, extras == null ? Bundle.EMPTY : extras, completionTimeMs, sessionError);
276294
}
295+
296+
private final class InProcessBinder extends Binder {
297+
public SessionResult getSessionResult() {
298+
return SessionResult.this;
299+
}
300+
}
277301
}

libraries/session/src/test/java/androidx/media3/session/LibraryResultTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,14 @@ public void toBundle_roundTrip_equalsWithOriginal() {
166166
assertThat(errorLibraryResultFromBundle.completionTimeMs)
167167
.isEqualTo(errorLibraryResult.completionTimeMs);
168168
}
169+
170+
@Test
171+
public void roundTripViaBundleForLocalProcess_yieldsSameInstance() {
172+
LibraryResult<SessionError> errorLibraryResult =
173+
LibraryResult.ofError(new SessionError(ERROR_NOT_SUPPORTED, "error message", new Bundle()));
174+
LibraryResult<?> unbundledLibraryResult =
175+
LibraryResult.fromUnknownBundle(errorLibraryResult.toBundleForLocalProcess());
176+
177+
assertThat(errorLibraryResult == unbundledLibraryResult).isTrue();
178+
}
169179
}

libraries/session/src/test/java/androidx/media3/session/SessionPositionInfoTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ public void roundTripViaBundle_yieldsEqualInstance() {
6262
assertThat(sessionPositionInfo).isEqualTo(testSessionPositionInfo);
6363
}
6464

65+
@Test
66+
public void roundTripViaBundleForLocalProcess_yieldsSameInstance() {
67+
SessionPositionInfo roundTripValue =
68+
SessionPositionInfo.fromBundle(SessionPositionInfo.DEFAULT.toBundleForLocalProcess());
69+
70+
assertThat(SessionPositionInfo.DEFAULT == roundTripValue).isTrue();
71+
}
72+
6573
@Test
6674
public void constructor_invalidIsPlayingAd_throwsIllegalArgumentException() {
6775
Assert.assertThrows(

libraries/session/src/test/java/androidx/media3/session/SessionResultTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package androidx.media3.session;
1717

18+
import static androidx.media3.session.SessionError.ERROR_NOT_SUPPORTED;
1819
import static androidx.media3.session.SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED;
1920
import static androidx.media3.session.SessionError.ERROR_SESSION_CONCURRENT_STREAM_LIMIT;
2021
import static com.google.common.truth.Truth.assertThat;
@@ -72,4 +73,14 @@ public void toBundle_roundTrip_resultsInEqualObjectWithSameBundle() {
7273
assertThat(resultFromBundle.sessionError.extras.getString("errorKey")).isEqualTo("errorValue");
7374
assertThat(resultFromBundle.extras.size()).isEqualTo(0);
7475
}
76+
77+
@Test
78+
public void roundTripViaBundleForLocalProcess_yieldsSameInstance() {
79+
SessionResult errorSessionResult =
80+
new SessionResult(new SessionError(ERROR_NOT_SUPPORTED, "error message", new Bundle()));
81+
SessionResult unbundledSessionResult =
82+
SessionResult.fromBundle(errorSessionResult.toBundleForLocalProcess());
83+
84+
assertThat(errorSessionResult == unbundledSessionResult).isTrue();
85+
}
7586
}

0 commit comments

Comments
 (0)