Error handling when looper is different for the getChoreographer

than originally sourced.

getChoreographer without the looper will return the cached
Choreographer instance.

Test: atest ChoreographerTests
BUG: 258235754
Change-Id: I02ca535730627d7ca932ebeedb211f102c6aa0e0
This commit is contained in:
ramindani
2023-02-01 14:54:19 -08:00
parent 1585f19982
commit 2cf250c087
3 changed files with 65 additions and 21 deletions

View File

@@ -366,6 +366,14 @@ public final class Choreographer {
dispose();
}
/**
* Check if the sourced looper and the current looper are same.
* @hide
*/
boolean isTheLooperSame(Looper looper) {
return mLooper == looper;
}
/**
* The amount of time, in milliseconds, between each frame of the animation.
* <p>

View File

@@ -1274,27 +1274,36 @@ public final class SurfaceControl implements Parcelable {
}
/**
* Returns the associated {@link Choreographer} instance with the
* current instance of the SurfaceControl.
* Must be called from a thread that already has a {@link android.os.Looper}
* associated with it.
* If there is no {@link Choreographer} associated with the SurfaceControl then a new instance
* of the {@link Choreographer} is created.
* When called for the first time a new instance of the {@link Choreographer} is created
* with a {@link android.os.Looper} of the current thread. Every subsequent call will return
* the same instance of the Choreographer.
*
* @see #getChoreographer(Looper) to create Choreographer with a different
* looper than current thread looper.
*
* @hide
*/
@TestApi
public @NonNull Choreographer getChoreographer() {
return getChoreographer(Looper.myLooper());
checkNotReleased();
synchronized (mChoreographerLock) {
if (mChoreographer == null) {
return getChoreographer(Looper.myLooper());
}
return mChoreographer;
}
}
/**
* Returns the associated {@link Choreographer} instance with the
* current instance of the SurfaceControl.
* If there is no {@link Choreographer} associated with the SurfaceControl then a new instance
* of the {@link Choreographer} is created.
* When called for the first time a new instance of the {@link Choreographer} is created with
* the sourced {@link android.os.Looper}. Every subsequent call will return the same
* instance of the Choreographer.
*
* @param looper the choreographer is attached on this looper
* @see #getChoreographer()
*
* @throws IllegalStateException when a {@link Choreographer} instance exists with a different
* looper than sourced.
* @param looper the choreographer is attached on this looper.
*
* @hide
*/
@@ -1302,11 +1311,12 @@ public final class SurfaceControl implements Parcelable {
public @NonNull Choreographer getChoreographer(@NonNull Looper looper) {
checkNotReleased();
synchronized (mChoreographerLock) {
if (mChoreographer != null) {
return mChoreographer;
if (mChoreographer == null) {
mChoreographer = Choreographer.getInstanceForSurfaceControl(mNativeHandle, looper);
} else if (!mChoreographer.isTheLooperSame(looper)) {
throw new IllegalStateException(
"Choreographer already exists with a different looper");
}
mChoreographer = Choreographer.getInstanceForSurfaceControl(mNativeHandle, looper);
return mChoreographer;
}
}

View File

@@ -16,7 +16,9 @@
package android.view.choreographertests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -63,6 +65,7 @@ public class AttachedChoreographerTest {
private DisplayManager mDisplayManager;
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private Choreographer mChoreographer;
private boolean mIsFirstCallback = true;
private int mCallbackMissedCounter = 0;
@@ -127,37 +130,60 @@ public class AttachedChoreographerTest {
@Test
public void testCreateChoreographer() {
Looper testLooper = Looper.myLooper();
mScenario.onActivity(activity -> {
if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
fail("Unable to create surface within 1 Second");
}
SurfaceControl sc = mSurfaceView.getSurfaceControl();
mChoreographer = sc.getChoreographer();
mTestCompleteSignal.countDown();
SurfaceControl sc1 = new SurfaceControl(sc, "AttachedChoreographerTests");
// Create attached choreographer with getChoreographer
sc1.getChoreographer();
Choreographer choreographer1 = sc1.getChoreographer();
assertTrue(sc1.hasChoreographer());
assertTrue(sc1.isValid());
sc1.release();
assertEquals(choreographer1, sc1.getChoreographer());
assertEquals(choreographer1, sc1.getChoreographer(Looper.myLooper()));
assertEquals(choreographer1, sc1.getChoreographer(Looper.getMainLooper()));
assertThrows(IllegalStateException.class, () -> sc1.getChoreographer(testLooper));
SurfaceControl sc2 = new SurfaceControl(sc, "AttachedChoreographerTests");
// Create attached choreographer with Looper.myLooper
sc2.getChoreographer(Looper.myLooper());
Choreographer choreographer2 = sc2.getChoreographer(Looper.myLooper());
assertTrue(sc2.hasChoreographer());
assertTrue(sc2.isValid());
sc2.release();
assertEquals(choreographer2, sc2.getChoreographer(Looper.myLooper()));
assertEquals(choreographer2, sc2.getChoreographer(Looper.getMainLooper()));
assertEquals(choreographer2, sc2.getChoreographer());
assertThrows(IllegalStateException.class, () -> sc2.getChoreographer(testLooper));
SurfaceControl sc3 = new SurfaceControl(sc, "AttachedChoreographerTests");
// Create attached choreographer with Looper.myLooper
sc3.getChoreographer(Looper.getMainLooper());
Choreographer choreographer3 = sc3.getChoreographer(Looper.getMainLooper());
assertTrue(sc3.hasChoreographer());
assertTrue(sc3.isValid());
assertEquals(choreographer3, sc3.getChoreographer(Looper.getMainLooper()));
assertEquals(choreographer3, sc3.getChoreographer(Looper.myLooper()));
assertEquals(choreographer3, sc3.getChoreographer());
assertThrows(IllegalStateException.class, () -> sc3.getChoreographer(testLooper));
assertNotEquals(choreographer1, choreographer2);
assertNotEquals(choreographer1, choreographer3);
assertNotEquals(choreographer2, choreographer3);
sc1.release();
sc2.release();
sc3.release();
mTestCompleteSignal.countDown();
});
if (waitForCountDown(mTestCompleteSignal, /* timeoutInSeconds */ 2L)) {
fail("Test not finished in 2 Seconds");
}
SurfaceControl surfaceControl = mSurfaceView.getSurfaceControl();
assertTrue(surfaceControl.hasChoreographer());
assertEquals(mChoreographer, surfaceControl.getChoreographer());
assertThrows(IllegalStateException.class,
() -> surfaceControl.getChoreographer(testLooper));
}
@Test