Fix Slice not pinned error

Slices should be pinned before being bound. The original design calls
registerSliceCallback() to pin a slice, and then calls bindSlice() and
passes the result to the callback directly. When the callback is
called, it executes unregisterSliceCallback() and unpins the slice.

However, registerSliceCallback() starts to observe the slice change and
then rebind it in an AsyncTask. If the slice is updating via its
background worker and the timing of the binding overlaps the callback
execution, it's possible to bind the slice right after unpinning it and
causes the error.

The solution is to remove the callback mechanism, and just to pin, bind
and unpin the slice directly.

Fixes: 157387583
Test: robotest
Change-Id: I8748dd3038a3662599935f07420d07cf254a4073
This commit is contained in:
Jason Chiu
2020-06-11 16:28:58 +08:00
parent 03b20f864a
commit f7afded1d1
3 changed files with 9 additions and 37 deletions

View File

@@ -35,13 +35,10 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class EligibleCardChecker implements Callable<ContextualCard> {
private static final String TAG = "EligibleCardChecker";
private static final long LATCH_TIMEOUT_MS = 300;
private final Context mContext;
@@ -54,7 +51,7 @@ public class EligibleCardChecker implements Callable<ContextualCard> {
}
@Override
public ContextualCard call() throws Exception {
public ContextualCard call() {
final long startTime = System.currentTimeMillis();
final MetricsFeatureProvider metricsFeatureProvider =
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
@@ -113,35 +110,10 @@ public class EligibleCardChecker implements Callable<ContextualCard> {
@VisibleForTesting
Slice bindSlice(Uri uri) {
final SliceViewManager manager = SliceViewManager.getInstance(mContext);
final Slice[] returnSlice = new Slice[1];
final CountDownLatch latch = new CountDownLatch(1);
final SliceViewManager.SliceCallback callback =
new SliceViewManager.SliceCallback() {
@Override
public void onSliceUpdated(Slice slice) {
try {
// We are just making sure the existence of the slice, so ignore
// slice loading state here.
returnSlice[0] = slice;
latch.countDown();
} catch (Exception e) {
Log.w(TAG, uri + " cannot be indexed", e);
} finally {
manager.unregisterSliceCallback(uri, this);
}
}
};
// Register a callback until we get a loaded slice.
manager.registerSliceCallback(uri, callback);
// Trigger the binding.
callback.onSliceUpdated(manager.bindSlice(uri));
try {
latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Log.w(TAG, "Error waiting for slice binding for uri" + uri, e);
manager.unregisterSliceCallback(uri, callback);
}
return returnSlice[0];
manager.pinSlice(uri);
final Slice slice = manager.bindSlice(uri);
manager.unpinSlice(uri);
return slice;
}
@VisibleForTesting

View File

@@ -77,9 +77,9 @@ public class EligibleCardCheckerTest {
@Test
public void isCardEligibleToDisplay_invalidScheme_returnFalse() {
final Uri sliceUri = Uri.parse("contet://com.android.settings.slices/action/flashlight");
final Uri invalidUri = Uri.parse("contet://com.android.settings.slices/action/flashlight");
assertThat(mEligibleCardChecker.isCardEligibleToDisplay(getContextualCard(sliceUri)))
assertThat(mEligibleCardChecker.isCardEligibleToDisplay(getContextualCard(invalidUri)))
.isFalse();
}

View File

@@ -86,10 +86,10 @@ public class SliceContextualCardRendererTest {
@Test
public void bindView_invalidScheme_sliceShouldBeNull() {
final Uri sliceUri = Uri.parse("contet://com.android.settings.slices/action/flashlight");
final Uri invalidUri = Uri.parse("contet://com.android.settings.slices/action/flashlight");
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
mRenderer.bindView(viewHolder, buildContextualCard(sliceUri));
mRenderer.bindView(viewHolder, buildContextualCard(invalidUri));
assertThat(
((SliceFullCardRendererHelper.SliceViewHolder) viewHolder).sliceView.getSlice())