Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0625bb48 authored by Jason Chiu's avatar Jason Chiu
Browse files

Fix the overlapping problem of the burst of slice updates

Implement a throttle in SliceBackgroundWorker to control slice updates.

Test: robotest
Fixes: 152366832
Change-Id: I8b65d1b57973e036b932172627aca506f4fae3a4
parent 5bd41e57
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -243,7 +243,7 @@ public class SettingsSliceProvider extends SliceProvider {
                        .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 @@ public class SettingsSliceProvider extends SliceProvider {
        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);
        }
    }
+78 −1
Original line number Diff line number Diff line
@@ -20,6 +20,12 @@ import android.annotation.MainThread;
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 @@ public abstract class SliceBackgroundWorker<E> implements Closeable {

    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 @@ public abstract class SliceBackgroundWorker<E> implements Closeable {
     * 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());
        }
    };
}
+2 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.content.ContentResolver;
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.RuntimeEnvironment;
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");
+2 −1
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.media.MediaRoute2ProviderService;
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 @@ import java.util.List;

@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");
+3 −1
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.media.session.MediaSessionManager;
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.ArrayList;
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";
Loading