OSDN Git Service

Use a better way to bind slice for slice precheck.
authorYi-Ling Chuang <emilychuang@google.com>
Thu, 14 Feb 2019 12:36:20 +0000 (20:36 +0800)
committerYi-Ling Chuang <emilychuang@google.com>
Thu, 21 Feb 2019 06:18:01 +0000 (14:18 +0800)
Calling Slice.bindSlice() directly will cause the exception stating that
slices are not pinned, which sometimes leads to crash. Hence, change the
way we bind slices which handles pinSlice() for us before onBindSlice().

Bug: 120552892
Test: robotests, unit tests
Change-Id: I3e65c6b79876dbee5db6f19387bc6b675f734161

src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
tests/unit/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java

index 7a0eb0f..1acddab 100644 (file)
@@ -18,13 +18,10 @@ package com.android.settings.homepage.contextualcards;
 
 import static android.app.slice.Slice.HINT_ERROR;
 
-import static androidx.slice.widget.SliceLiveData.SUPPORTED_SPECS;
-
 import static com.android.settings.slices.CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI;
 import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI;
 import static com.android.settings.slices.CustomSliceRegistry.NOTIFICATION_CHANNEL_SLICE_URI;
 
-import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
@@ -38,6 +35,7 @@ import android.util.Log;
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 import androidx.slice.Slice;
+import androidx.slice.SliceViewManager;
 
 import com.android.settings.R;
 import com.android.settings.overlay.FeatureFactory;
@@ -45,6 +43,8 @@ import com.android.settingslib.utils.AsyncLoaderCompat;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>> {
@@ -55,6 +55,7 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
     static final long CARD_CONTENT_LOADER_TIMEOUT_MS = DateUtils.SECOND_IN_MILLIS * 3;
 
     private static final String TAG = "ContextualCardLoader";
+    private static final long LATCH_TIMEOUT_MS = 200;
 
     private final ContentObserver mObserver = new ContentObserver(
             new Handler(Looper.getMainLooper())) {
@@ -186,16 +187,7 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
             return false;
         }
 
-        //check if the uri has a provider associated with.
-        final ContentProviderClient provider =
-                mContext.getContentResolver().acquireContentProviderClient(uri);
-        if (provider == null) {
-            return false;
-        }
-        //release contentProviderClient to prevent from memory leak.
-        provider.release();
-
-        final Slice slice = Slice.bindSlice(mContext, uri, SUPPORTED_SPECS);
+        final Slice slice = bindSlice(uri);
         //TODO(b/123668403): remove the log here once we do the change with FutureTask
         final long bindTime = System.currentTimeMillis() - startTime;
         Log.d(TAG, "Binding time for " + uri + " = " + bindTime);
@@ -208,6 +200,40 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
         return true;
     }
 
+    @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];
+    }
+
     private int getNumberOfLargeCard(List<ContextualCard> cards) {
         return (int) cards.stream()
                 .filter(card -> isLargeCard(card))
index f04008b..beb2d40 100644 (file)
@@ -74,15 +74,6 @@ public class ContextualCardLoaderTest {
     }
 
     @Test
-    public void isCardEligibleToDisplay_noProvider_returnFalse() {
-        final String sliceUri = "content://com.android.settings.test.slices/action/flashlight";
-
-        assertThat(
-                mContextualCardLoader.isCardEligibleToDisplay(
-                        getContextualCard(sliceUri))).isFalse();
-    }
-
-    @Test
     public void getDisplayableCards_twoEligibleCards_shouldShowAll() {
         final List<ContextualCard> cards = getContextualCardList().stream().limit(2)
                 .collect(Collectors.toList());
index 82fcd7d..55ad1ff 100644 (file)
@@ -21,9 +21,12 @@ import static com.google.common.truth.Truth.assertThat;
 import android.content.Context;
 import android.net.Uri;
 
+import androidx.slice.Slice;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.settings.slices.CustomSliceRegistry;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -44,7 +47,7 @@ public class ContextualCardLoaderTest {
     }
 
     @Test
-    public void filter_twoInvalidCards_shouldReturnOneCard() {
+    public void filterEligibleCards_twoInvalidCards_shouldReturnOneCard() {
         final String sliceUri1 = "content://com.android.settings.slices/action/flashlight"; //valid
         final String sliceUri2 = "content://com.android.settings.test.slices/action/flashlight";
         final String sliceUri3 = "cotent://com.android.settings.slices/action/flashlight";
@@ -59,6 +62,23 @@ public class ContextualCardLoaderTest {
         assertThat(result).hasSize(1);
     }
 
+    @Test
+    public void bindSlice_flashlightUri_shouldReturnFlashlightSlice() {
+        final Slice loadedSlice =
+                mContextualCardLoader.bindSlice(CustomSliceRegistry.FLASHLIGHT_SLICE_URI);
+
+        assertThat(loadedSlice.getUri()).isEqualTo(CustomSliceRegistry.FLASHLIGHT_SLICE_URI);
+    }
+
+    @Test
+    public void bindSlice_noProvider_shouldReturnNull() {
+        final String sliceUri = "content://com.android.settings.test.slices/action/flashlight";
+
+        final Slice loadedSlice = mContextualCardLoader.bindSlice(Uri.parse(sliceUri));
+
+        assertThat(loadedSlice).isNull();
+    }
+
     private ContextualCard getContextualCard(String sliceUri) {
         return new ContextualCard.Builder()
                 .setName("test_card")