Merge "Fix the overlapping problem of the burst of slice updates" into rvc-dev
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index 6c245ce..75061a5 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -243,7 +243,7 @@
.createWifiCallingPreferenceSlice(sliceUri);
}
- SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri);
+ final SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri);
if (cachedSliceData == null) {
loadSliceInBackground(sliceUri);
return getSliceStub(sliceUri);
@@ -466,14 +466,14 @@
final SliceBackgroundWorker worker = SliceBackgroundWorker.getInstance(
getContext(), sliceable, uri);
mPinnedWorkers.put(uri, worker);
- worker.onSlicePinned();
+ worker.pin();
}
private void stopBackgroundWorker(Uri uri) {
final SliceBackgroundWorker worker = mPinnedWorkers.get(uri);
if (worker != null) {
Log.d(TAG, "Stopping background worker for: " + uri);
- worker.onSliceUnpinned();
+ worker.unpin();
mPinnedWorkers.remove(uri);
}
}
diff --git a/src/com/android/settings/slices/SliceBackgroundWorker.java b/src/com/android/settings/slices/SliceBackgroundWorker.java
index 6bafc00..6eb154e 100644
--- a/src/com/android/settings/slices/SliceBackgroundWorker.java
+++ b/src/com/android/settings/slices/SliceBackgroundWorker.java
@@ -20,6 +20,12 @@
import android.annotation.Nullable;
import android.content.Context;
import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.Log;
@@ -47,6 +53,8 @@
private static final String TAG = "SliceBackgroundWorker";
+ private static final long SLICE_UPDATE_THROTTLE_INTERVAL = 300L;
+
private static final Map<Uri, SliceBackgroundWorker> LIVE_WORKERS = new ArrayMap<>();
private final Context mContext;
@@ -164,6 +172,75 @@
* Notify that data was updated and attempt to sync changes to the Slice.
*/
protected final void notifySliceChange() {
- mContext.getContentResolver().notifyChange(mUri, null);
+ NotifySliceChangeHandler.getInstance().updateSlice(this);
}
+
+ void pin() {
+ onSlicePinned();
+ }
+
+ void unpin() {
+ onSliceUnpinned();
+ NotifySliceChangeHandler.getInstance().cancelSliceUpdate(this);
+ }
+
+ private static class NotifySliceChangeHandler extends Handler {
+
+ private static final int MSG_UPDATE_SLICE = 1000;
+
+ private static NotifySliceChangeHandler sHandler;
+
+ private final Map<Uri, Long> mLastUpdateTimeLookup = new ArrayMap<>();
+
+ private static NotifySliceChangeHandler getInstance() {
+ if (sHandler == null) {
+ final HandlerThread workerThread = new HandlerThread("NotifySliceChangeHandler",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ workerThread.start();
+ sHandler = new NotifySliceChangeHandler(workerThread.getLooper());
+ }
+ return sHandler;
+ }
+
+ private NotifySliceChangeHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what != MSG_UPDATE_SLICE) {
+ return;
+ }
+
+ final SliceBackgroundWorker worker = (SliceBackgroundWorker) msg.obj;
+ final Uri uri = worker.getUri();
+ final Context context = worker.getContext();
+ mLastUpdateTimeLookup.put(uri, SystemClock.uptimeMillis());
+ context.getContentResolver().notifyChange(uri, null);
+ }
+
+ private void updateSlice(SliceBackgroundWorker worker) {
+ if (hasMessages(MSG_UPDATE_SLICE, worker)) {
+ return;
+ }
+
+ final Message message = obtainMessage(MSG_UPDATE_SLICE, worker);
+ final long lastUpdateTime = mLastUpdateTimeLookup.getOrDefault(worker.getUri(), 0L);
+ if (lastUpdateTime == 0L) {
+ // Postpone the first update triggering by onSlicePinned() to avoid being too close
+ // to the first Slice bind.
+ sendMessageDelayed(message, SLICE_UPDATE_THROTTLE_INTERVAL);
+ } else if (SystemClock.uptimeMillis() - lastUpdateTime
+ > SLICE_UPDATE_THROTTLE_INTERVAL) {
+ sendMessage(message);
+ } else {
+ sendMessageAtTime(message, lastUpdateTime + SLICE_UPDATE_THROTTLE_INTERVAL);
+ }
+ }
+
+ private void cancelSliceUpdate(SliceBackgroundWorker worker) {
+ removeMessages(MSG_UPDATE_SLICE, worker);
+ mLastUpdateTimeLookup.remove(worker.getUri());
+ }
+ };
}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
index 4da5c09..e34737b 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.net.Uri;
+import com.android.settings.slices.ShadowSliceBackgroundWorker;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -35,7 +36,7 @@
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowBluetoothAdapter.class})
+@Config(shadows = {ShadowBluetoothAdapter.class, ShadowSliceBackgroundWorker.class})
public class BluetoothUpdateWorkerTest {
private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
diff --git a/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java b/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java
index 624bbd8..be86b6e 100644
--- a/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java
@@ -34,6 +34,7 @@
import android.media.RoutingSessionInfo;
import android.net.Uri;
+import com.android.settings.slices.ShadowSliceBackgroundWorker;
import com.android.settings.testutils.shadow.ShadowAudioManager;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
@@ -57,7 +58,7 @@
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowAudioManager.class, ShadowBluetoothAdapter.class,
- ShadowBluetoothUtils.class})
+ ShadowBluetoothUtils.class, ShadowSliceBackgroundWorker.class})
public class MediaDeviceUpdateWorkerTest {
private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorWorkerTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorWorkerTest.java
index 0aec952..423c7ac 100644
--- a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorWorkerTest.java
@@ -39,6 +39,7 @@
import android.media.session.PlaybackState;
import android.net.Uri;
+import com.android.settings.slices.ShadowSliceBackgroundWorker;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothEventManager;
@@ -59,7 +60,8 @@
import java.util.List;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class})
+@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class,
+ ShadowSliceBackgroundWorker.class})
public class MediaOutputIndicatorWorkerTest {
private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
private static final String TEST_PACKAGE_NAME = "com.android.test";
diff --git a/tests/robotests/src/com/android/settings/slices/ShadowSliceBackgroundWorker.java b/tests/robotests/src/com/android/settings/slices/ShadowSliceBackgroundWorker.java
new file mode 100644
index 0000000..7bf8358
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/slices/ShadowSliceBackgroundWorker.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.slices;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+
+@Implements(SliceBackgroundWorker.class)
+public class ShadowSliceBackgroundWorker {
+
+ @RealObject
+ private SliceBackgroundWorker mRealWorker;
+
+ @Implementation
+ protected final void notifySliceChange() {
+ mRealWorker.getContext().getContentResolver().notifyChange(mRealWorker.getUri(), null);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
index d40331c..3319543 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
@@ -43,6 +43,7 @@
import android.os.Bundle;
import android.os.UserHandle;
+import com.android.settings.slices.ShadowSliceBackgroundWorker;
import com.android.settings.testutils.shadow.ShadowWifiManager;
import com.android.settingslib.wifi.AccessPoint;
import com.android.settingslib.wifi.WifiTracker;
@@ -63,10 +64,8 @@
import java.util.List;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {
- ShadowWifiManager.class,
- WifiScanWorkerTest.ShadowWifiTracker.class,
-})
+@Config(shadows = {ShadowSliceBackgroundWorker.class, ShadowWifiManager.class,
+ WifiScanWorkerTest.ShadowWifiTracker.class})
public class WifiScanWorkerTest {
private Context mContext;