Merge "New Executor implementaiton for SystemUI."
This commit is contained in:
committed by
Android (Google) Code Review
commit
6a06d9f864
@@ -37,6 +37,7 @@ import com.android.systemui.statusbar.phone.KeyguardLiftController;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
import com.android.systemui.statusbar.phone.StatusBarComponent;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
import com.android.systemui.util.concurrency.ConcurrencyModule;
|
||||
import com.android.systemui.util.sensors.AsyncSensorManager;
|
||||
import com.android.systemui.util.time.SystemClock;
|
||||
import com.android.systemui.util.time.SystemClockImpl;
|
||||
@@ -53,6 +54,7 @@ import dagger.Provides;
|
||||
* implementation.
|
||||
*/
|
||||
@Module(includes = {AssistModule.class,
|
||||
ConcurrencyModule.class,
|
||||
PeopleHubModule.class},
|
||||
subcomponents = {StatusBarComponent.class})
|
||||
public abstract class SystemUIModule {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.dagger.qualifiers;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
public @interface Background {
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.dagger.qualifiers;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
public @interface Main {
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
@@ -34,8 +33,8 @@ import android.util.ArraySet;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dagger.qualifiers.BgHandler;
|
||||
import com.android.systemui.dagger.qualifiers.MainHandler;
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.plugins.qs.QSTile;
|
||||
import com.android.systemui.plugins.qs.QSTile.State;
|
||||
import com.android.systemui.qs.QSTileHost;
|
||||
@@ -47,6 +46,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -55,8 +55,8 @@ public class TileQueryHelper {
|
||||
|
||||
private final ArrayList<TileInfo> mTiles = new ArrayList<>();
|
||||
private final ArraySet<String> mSpecs = new ArraySet<>();
|
||||
private final Handler mBgHandler;
|
||||
private final Handler mMainHandler;
|
||||
private final Executor mMainExecutor;
|
||||
private final Executor mBgExecutor;
|
||||
private final Context mContext;
|
||||
private TileStateListener mListener;
|
||||
|
||||
@@ -64,10 +64,10 @@ public class TileQueryHelper {
|
||||
|
||||
@Inject
|
||||
public TileQueryHelper(Context context,
|
||||
@MainHandler Handler mainHandler, @BgHandler Handler bgHandler) {
|
||||
@Main Executor mainExecutor, @Background Executor bgExecutor) {
|
||||
mContext = context;
|
||||
mMainHandler = mainHandler;
|
||||
mBgHandler = bgHandler;
|
||||
mMainExecutor = mainExecutor;
|
||||
mBgExecutor = bgExecutor;
|
||||
}
|
||||
|
||||
public void setListener(TileStateListener listener) {
|
||||
@@ -126,7 +126,7 @@ public class TileQueryHelper {
|
||||
tilesToAdd.add(tile);
|
||||
}
|
||||
|
||||
mBgHandler.post(() -> {
|
||||
mBgExecutor.execute(() -> {
|
||||
for (QSTile tile : tilesToAdd) {
|
||||
final QSTile.State state = tile.getState().copy();
|
||||
// Ignore the current state and get the generic label instead.
|
||||
@@ -139,7 +139,7 @@ public class TileQueryHelper {
|
||||
}
|
||||
|
||||
private void addPackageTiles(final QSTileHost host) {
|
||||
mBgHandler.post(() -> {
|
||||
mBgExecutor.execute(() -> {
|
||||
Collection<QSTile> params = host.getTiles();
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
List<ResolveInfo> services = pm.queryIntentServicesAsUser(
|
||||
@@ -185,7 +185,7 @@ public class TileQueryHelper {
|
||||
|
||||
private void notifyTilesChanged(final boolean finished) {
|
||||
final ArrayList<TileInfo> tilesToReturn = new ArrayList<>(mTiles);
|
||||
mMainHandler.post(() -> {
|
||||
mMainExecutor.execute(() -> {
|
||||
if (mListener != null) {
|
||||
mListener.onTilesChanged(tilesToReturn);
|
||||
}
|
||||
|
||||
@@ -17,14 +17,13 @@
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import com.android.systemui.dagger.qualifiers.BgHandler;
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -49,10 +48,10 @@ public class FeatureFlags {
|
||||
private final Map<String, Boolean> mCachedDeviceConfigFlags = new ArrayMap<>();
|
||||
|
||||
@Inject
|
||||
public FeatureFlags(@BgHandler Handler bgHandler) {
|
||||
public FeatureFlags(@Background Executor executor) {
|
||||
DeviceConfig.addOnPropertiesChangedListener(
|
||||
"systemui",
|
||||
new HandlerExecutor(bgHandler),
|
||||
executor,
|
||||
this::onPropertiesChanged);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.util.concurrency;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
import com.android.systemui.dagger.qualifiers.BgHandler;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.dagger.qualifiers.MainHandler;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
/**
|
||||
* Dagger Module for classes found within the concurrent package.
|
||||
*/
|
||||
@Module
|
||||
public abstract class ConcurrencyModule {
|
||||
/**
|
||||
* Provide a Background-Thread Executor by default.
|
||||
*/
|
||||
@Provides
|
||||
public static Executor provideExecutor(@BgHandler Handler handler) {
|
||||
return new ExecutorImpl(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a Background-Thread Executor.
|
||||
*/
|
||||
@Provides
|
||||
@Background
|
||||
public static Executor provideBackgroundExecutor(@BgHandler Handler handler) {
|
||||
return new ExecutorImpl(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a Main-Thread Executor.
|
||||
*/
|
||||
@Provides
|
||||
@Main
|
||||
public static Executor provideMainExecutor(Context context) {
|
||||
return context.getMainExecutor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a Background-Thread Executor by default.
|
||||
*/
|
||||
@Provides
|
||||
public static DelayableExecutor provideDelayableExecutor(@BgHandler Handler handler) {
|
||||
return new ExecutorImpl(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a Background-Thread Executor.
|
||||
*/
|
||||
@Provides
|
||||
@Background
|
||||
public static DelayableExecutor provideBackgroundDelayableExecutor(@BgHandler Handler handler) {
|
||||
return new ExecutorImpl(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a Main-Thread Executor.
|
||||
*/
|
||||
@Provides
|
||||
@Main
|
||||
public static DelayableExecutor provideMainDelayableExecutor(@MainHandler Handler handler) {
|
||||
return new ExecutorImpl(handler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.util.concurrency;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A sub-class of {@link Executor} that allows Runnables to be delayed and/or cancelled.
|
||||
*/
|
||||
public interface DelayableExecutor extends Executor {
|
||||
/**
|
||||
* Execute supplied Runnable on the Executors thread after a specified delay.
|
||||
*
|
||||
* See {@link android.os.Handler#postDelayed(Runnable, long)}.
|
||||
*
|
||||
* @return A Runnable that, when run, removes the supplied argument from the Executor queue.
|
||||
*/
|
||||
default Runnable executeDelayed(Runnable r, long delayMillis) {
|
||||
return executeDelayed(r, delayMillis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute supplied Runnable on the Executors thread after a specified delay.
|
||||
*
|
||||
* See {@link android.os.Handler#postDelayed(Runnable, long)}.
|
||||
*
|
||||
* @return A Runnable that, when run, removes the supplied argument from the Executor queue..
|
||||
*/
|
||||
Runnable executeDelayed(Runnable r, long delay, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Execute supplied Runnable on the Executors thread at a specified uptime.
|
||||
*
|
||||
* See {@link android.os.Handler#postAtTime(Runnable, long)}.
|
||||
*
|
||||
* @return A Runnable that, when run, removes the supplied argument from the Executor queue.
|
||||
*/
|
||||
default Runnable executeAtTime(Runnable r, long uptime) {
|
||||
return executeAtTime(r, uptime, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute supplied Runnable on the Executors thread at a specified uptime.
|
||||
*
|
||||
* See {@link android.os.Handler#postAtTime(Runnable, long)}.
|
||||
*
|
||||
* @return A Runnable that, when run, removes the supplied argument from the Executor queue.
|
||||
*/
|
||||
Runnable executeAtTime(Runnable r, long uptimeMillis, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Remove all pending Runnables.
|
||||
*
|
||||
*/
|
||||
void removeAll();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.util.concurrency;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.Message;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Implementations of {@link DelayableExecutor} for SystemUI.
|
||||
*/
|
||||
public class ExecutorImpl extends HandlerExecutor implements DelayableExecutor {
|
||||
private final Handler mHandler;
|
||||
|
||||
public ExecutorImpl(Handler handler) {
|
||||
super(handler);
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable executeDelayed(Runnable r, long delay, TimeUnit unit) {
|
||||
Object token = new Object();
|
||||
Message m = mHandler.obtainMessage(0, token);
|
||||
mHandler.sendMessageDelayed(m, unit.toMillis(delay));
|
||||
|
||||
return () -> mHandler.removeCallbacksAndMessages(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable executeAtTime(Runnable r, long uptimeMillis, TimeUnit unit) {
|
||||
Object token = new Object();
|
||||
Message m = mHandler.obtainMessage(0, token);
|
||||
mHandler.sendMessageAtTime(m, unit.toMillis(uptimeMillis));
|
||||
|
||||
return () -> mHandler.removeCallbacksAndMessages(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAll() {
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
}
|
||||
@@ -36,13 +36,9 @@ import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
|
||||
@@ -52,6 +48,8 @@ import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.plugins.qs.QSTile;
|
||||
import com.android.systemui.qs.QSTileHost;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -69,7 +67,6 @@ import java.util.Set;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper
|
||||
public class TileQueryHelperTest extends SysuiTestCase {
|
||||
private static final String CURRENT_TILES = "wifi,dnd,nfc";
|
||||
private static final String ONLY_STOCK_TILES = "wifi,dnd";
|
||||
@@ -98,14 +95,13 @@ public class TileQueryHelperTest extends SysuiTestCase {
|
||||
private ArgumentCaptor<List<TileQueryHelper.TileInfo>> mCaptor;
|
||||
|
||||
private QSTile.State mState;
|
||||
private TestableLooper mBGLooper;
|
||||
private TileQueryHelper mTileQueryHelper;
|
||||
private Handler mMainHandler;
|
||||
private FakeExecutor mMainExecutor;
|
||||
private FakeExecutor mBgExecutor;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mBGLooper = TestableLooper.get(this);
|
||||
mContext.setMockPackageManager(mPackageManager);
|
||||
|
||||
mState = new QSTile.State();
|
||||
@@ -123,9 +119,11 @@ public class TileQueryHelperTest extends SysuiTestCase {
|
||||
}
|
||||
).when(mQSTileHost).createTile(anyString());
|
||||
|
||||
mMainHandler = new Handler(Looper.getMainLooper());
|
||||
mTileQueryHelper = new TileQueryHelper(mContext, mMainHandler,
|
||||
new Handler(mBGLooper.getLooper()));
|
||||
FakeSystemClock clock = new FakeSystemClock();
|
||||
clock.setAutoIncrement(false);
|
||||
mMainExecutor = new FakeExecutor(clock);
|
||||
mBgExecutor = new FakeExecutor(clock);
|
||||
mTileQueryHelper = new TileQueryHelper(mContext, mMainExecutor, mBgExecutor);
|
||||
mTileQueryHelper.setListener(mListener);
|
||||
}
|
||||
|
||||
@@ -138,8 +136,7 @@ public class TileQueryHelperTest extends SysuiTestCase {
|
||||
public void testIsFinished_trueAfterQuerying() {
|
||||
mTileQueryHelper.queryTiles(mQSTileHost);
|
||||
|
||||
mBGLooper.processAllMessages();
|
||||
waitForIdleSync(mMainHandler);
|
||||
FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
|
||||
|
||||
assertTrue(mTileQueryHelper.isFinished());
|
||||
}
|
||||
@@ -148,8 +145,7 @@ public class TileQueryHelperTest extends SysuiTestCase {
|
||||
public void testQueryTiles_callsListenerTwice() {
|
||||
mTileQueryHelper.queryTiles(mQSTileHost);
|
||||
|
||||
mBGLooper.processAllMessages();
|
||||
waitForIdleSync(mMainHandler);
|
||||
FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
|
||||
|
||||
verify(mListener, times(2)).onTilesChanged(any());
|
||||
}
|
||||
@@ -163,8 +159,7 @@ public class TileQueryHelperTest extends SysuiTestCase {
|
||||
|
||||
mTileQueryHelper.queryTiles(mQSTileHost);
|
||||
|
||||
mBGLooper.processAllMessages();
|
||||
waitForIdleSync(mMainHandler);
|
||||
FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
|
||||
|
||||
assertTrue(mTileQueryHelper.isFinished());
|
||||
}
|
||||
@@ -178,8 +173,7 @@ public class TileQueryHelperTest extends SysuiTestCase {
|
||||
|
||||
mTileQueryHelper.queryTiles(mQSTileHost);
|
||||
|
||||
mBGLooper.processAllMessages();
|
||||
waitForIdleSync(mMainHandler);
|
||||
FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
|
||||
|
||||
verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
|
||||
List<String> specs = new ArrayList<>();
|
||||
@@ -199,8 +193,7 @@ public class TileQueryHelperTest extends SysuiTestCase {
|
||||
|
||||
mTileQueryHelper.queryTiles(mQSTileHost);
|
||||
|
||||
mBGLooper.processAllMessages();
|
||||
waitForIdleSync(mMainHandler);
|
||||
FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
|
||||
|
||||
verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
|
||||
List<String> specs = new ArrayList<>();
|
||||
@@ -220,8 +213,7 @@ public class TileQueryHelperTest extends SysuiTestCase {
|
||||
|
||||
mTileQueryHelper.queryTiles(mQSTileHost);
|
||||
|
||||
mBGLooper.processAllMessages();
|
||||
waitForIdleSync(mMainHandler);
|
||||
FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
|
||||
|
||||
verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
|
||||
List<String> specs = new ArrayList<>();
|
||||
@@ -251,8 +243,7 @@ public class TileQueryHelperTest extends SysuiTestCase {
|
||||
"");
|
||||
|
||||
mTileQueryHelper.queryTiles(mQSTileHost);
|
||||
mBGLooper.processAllMessages();
|
||||
waitForIdleSync(mMainHandler);
|
||||
FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
|
||||
|
||||
verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
|
||||
List<TileQueryHelper.TileInfo> tileInfos = mCaptor.getValue();
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.util.concurrency;
|
||||
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
import com.android.systemui.util.time.FakeSystemClock.ClockTickListener;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class FakeExecutor implements DelayableExecutor {
|
||||
private final FakeSystemClock mClock;
|
||||
private PriorityQueue<QueuedRunnable> mQueuedRunnables = new PriorityQueue<>();
|
||||
private boolean mIgnoreClockUpdates;
|
||||
|
||||
private ClockTickListener mClockTickListener = new ClockTickListener() {
|
||||
@Override
|
||||
public void onUptimeMillis(long uptimeMillis) {
|
||||
if (!mIgnoreClockUpdates) {
|
||||
runAllReady();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a fake executor.
|
||||
*
|
||||
* @param clock FakeSystemClock allowing control over delayed runnables. It is strongly
|
||||
* recommended that this clock have its auto-increment setting set to false to
|
||||
* prevent unexpected advancement of the time.
|
||||
*/
|
||||
public FakeExecutor(FakeSystemClock clock) {
|
||||
mClock = clock;
|
||||
mClock.addListener(mClockTickListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a single runnable if it's scheduled to run according to the internal clock.
|
||||
*
|
||||
* If constructed to advance the clock automatically, this will advance the clock enough to
|
||||
* run the next pending item.
|
||||
*
|
||||
* This method does not advance the clock past the item that was run.
|
||||
*
|
||||
* @return Returns true if an item was run.
|
||||
*/
|
||||
public boolean runNextReady() {
|
||||
if (!mQueuedRunnables.isEmpty() && mQueuedRunnables.peek().mWhen <= mClock.uptimeMillis()) {
|
||||
mQueuedRunnables.poll().mRunnable.run();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all Runnables that are scheduled to run according to the internal clock.
|
||||
*
|
||||
* If constructed to advance the clock automatically, this will advance the clock enough to
|
||||
* run all the pending items. This method does not advance the clock past items that were
|
||||
* run. It is equivalent to calling {@link #runNextReady()} in a loop.
|
||||
*
|
||||
* @return Returns the number of items that ran.
|
||||
*/
|
||||
public int runAllReady() {
|
||||
int num = 0;
|
||||
while (runNextReady()) {
|
||||
num++;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the internal clock to the next item to run.
|
||||
*
|
||||
* The clock will only move forward. If the next item is set to run in the past or there is no
|
||||
* next item, the clock does not change.
|
||||
*
|
||||
* Note that this will cause one or more items to actually run.
|
||||
*
|
||||
* @return The delta in uptimeMillis that the clock advanced, or 0 if the clock did not advance.
|
||||
*/
|
||||
public long advanceClockToNext() {
|
||||
if (mQueuedRunnables.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long startTime = mClock.uptimeMillis();
|
||||
long nextTime = mQueuedRunnables.peek().mWhen;
|
||||
if (nextTime <= startTime) {
|
||||
return 0;
|
||||
}
|
||||
updateClock(nextTime);
|
||||
|
||||
return nextTime - startTime;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Advances the internal clock to the last item to run.
|
||||
*
|
||||
* The clock will only move forward. If the last item is set to run in the past or there is no
|
||||
* next item, the clock does not change.
|
||||
*
|
||||
* @return The delta in uptimeMillis that the clock advanced, or 0 if the clock did not advance.
|
||||
*/
|
||||
public long advanceClockToLast() {
|
||||
if (mQueuedRunnables.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long startTime = mClock.uptimeMillis();
|
||||
long nextTime = Collections.max(mQueuedRunnables).mWhen;
|
||||
if (nextTime <= startTime) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
updateClock(nextTime);
|
||||
|
||||
return nextTime - startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of un-executed runnables waiting to run.
|
||||
*/
|
||||
public int numPending() {
|
||||
return mQueuedRunnables.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable executeDelayed(Runnable r, long delay, TimeUnit unit) {
|
||||
if (delay < 0) {
|
||||
delay = 0;
|
||||
}
|
||||
return executeAtTime(r, mClock.uptimeMillis() + unit.toMillis(delay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable executeAtTime(Runnable r, long uptime, TimeUnit unit) {
|
||||
long uptimeMillis = unit.toMillis(uptime);
|
||||
|
||||
QueuedRunnable container = new QueuedRunnable(r, uptimeMillis);
|
||||
|
||||
mQueuedRunnables.offer(container);
|
||||
|
||||
return () -> mQueuedRunnables.remove(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
executeDelayed(command, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAll() {
|
||||
mQueuedRunnables.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all Executors in a loop until they all report they have no ready work to do.
|
||||
*
|
||||
* Useful if you have Executors the post work to other Executors, and you simply want to
|
||||
* run them all until they stop posting work.
|
||||
*/
|
||||
public static void exhaustExecutors(FakeExecutor ...executors) {
|
||||
boolean didAnything;
|
||||
do {
|
||||
didAnything = false;
|
||||
for (FakeExecutor executor : executors) {
|
||||
didAnything = didAnything || executor.runAllReady() != 0;
|
||||
}
|
||||
} while (didAnything);
|
||||
}
|
||||
|
||||
private void updateClock(long nextTime) {
|
||||
mIgnoreClockUpdates = true;
|
||||
mClock.setUptimeMillis(nextTime);
|
||||
mIgnoreClockUpdates = false;
|
||||
}
|
||||
|
||||
private static class QueuedRunnable implements Comparable<QueuedRunnable> {
|
||||
private static AtomicInteger sCounter = new AtomicInteger();
|
||||
|
||||
Runnable mRunnable;
|
||||
long mWhen;
|
||||
private int mCounter;
|
||||
|
||||
private QueuedRunnable(Runnable r, long when) {
|
||||
mRunnable = r;
|
||||
mWhen = when;
|
||||
|
||||
// PrioirityQueue orders items arbitrarily when equal. We want to ensure that
|
||||
// otherwise-equal elements are ordered according to their insertion order. Because this
|
||||
// class only is constructed right before insertion, we use a static counter to track
|
||||
// insertion order of otherwise equal elements.
|
||||
mCounter = sCounter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(QueuedRunnable other) {
|
||||
long diff = mWhen - other.mWhen;
|
||||
|
||||
if (diff == 0) {
|
||||
return mCounter - other.mCounter;
|
||||
}
|
||||
|
||||
return diff > 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.util.concurrency;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import kotlin.jvm.functions.Function4;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class FakeExecutorTest extends SysuiTestCase {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test FakeExecutor that receives non-delayed items to execute.
|
||||
*/
|
||||
@Test
|
||||
public void testNoDelay() {
|
||||
FakeSystemClock clock = new FakeSystemClock();
|
||||
clock.setAutoIncrement(false);
|
||||
FakeExecutor fakeExecutor = new FakeExecutor(clock);
|
||||
RunnableImpl runnable = new RunnableImpl();
|
||||
|
||||
assertEquals(0, clock.uptimeMillis());
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
|
||||
// Execute two runnables. They should not run and should be left pending.
|
||||
fakeExecutor.execute(runnable);
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
assertEquals(0, clock.uptimeMillis());
|
||||
assertEquals(1, fakeExecutor.numPending());
|
||||
fakeExecutor.execute(runnable);
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
assertEquals(0, clock.uptimeMillis());
|
||||
assertEquals(2, fakeExecutor.numPending());
|
||||
|
||||
// Run one pending runnable.
|
||||
assertTrue(fakeExecutor.runNextReady());
|
||||
assertEquals(1, runnable.mRunCount);
|
||||
assertEquals(0, clock.uptimeMillis());
|
||||
assertEquals(1, fakeExecutor.numPending());
|
||||
// Run a second pending runnable.
|
||||
assertTrue(fakeExecutor.runNextReady());
|
||||
assertEquals(2, runnable.mRunCount);
|
||||
assertEquals(0, clock.uptimeMillis());
|
||||
assertEquals(0, fakeExecutor.numPending());
|
||||
|
||||
// No more runnables to run.
|
||||
assertFalse(fakeExecutor.runNextReady());
|
||||
|
||||
// Add two more runnables.
|
||||
fakeExecutor.execute(runnable);
|
||||
fakeExecutor.execute(runnable);
|
||||
assertEquals(2, runnable.mRunCount);
|
||||
assertEquals(0, clock.uptimeMillis());
|
||||
assertEquals(2, fakeExecutor.numPending());
|
||||
// Execute all pending runnables in batch.
|
||||
assertEquals(2, fakeExecutor.runAllReady());
|
||||
assertEquals(4, runnable.mRunCount);
|
||||
assertEquals(0, clock.uptimeMillis());
|
||||
assertEquals(0, fakeExecutor.runAllReady());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test FakeExecutor that is told to delay execution on items.
|
||||
*/
|
||||
@Test
|
||||
public void testDelayed() {
|
||||
FakeSystemClock clock = new FakeSystemClock();
|
||||
clock.setAutoIncrement(false);
|
||||
FakeExecutor fakeExecutor = new FakeExecutor(clock);
|
||||
RunnableImpl runnable = new RunnableImpl();
|
||||
|
||||
// Add three delayed runnables.
|
||||
fakeExecutor.executeDelayed(runnable, 1);
|
||||
fakeExecutor.executeDelayed(runnable, 50);
|
||||
fakeExecutor.executeDelayed(runnable, 100);
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
assertEquals(0, clock.uptimeMillis());
|
||||
assertEquals(3, fakeExecutor.numPending());
|
||||
// Delayed runnables should not advance the clock and therefore should not run.
|
||||
assertFalse(fakeExecutor.runNextReady());
|
||||
assertEquals(0, fakeExecutor.runAllReady());
|
||||
assertEquals(3, fakeExecutor.numPending());
|
||||
|
||||
// Advance the clock to the next runnable. One runnable should execute.
|
||||
assertEquals(1, fakeExecutor.advanceClockToNext());
|
||||
assertEquals(1, fakeExecutor.runAllReady());
|
||||
assertEquals(2, fakeExecutor.numPending());
|
||||
assertEquals(1, runnable.mRunCount);
|
||||
// Advance the clock to the last runnable.
|
||||
assertEquals(99, fakeExecutor.advanceClockToLast());
|
||||
assertEquals(2, fakeExecutor.runAllReady());
|
||||
// Now all remaining runnables should execute.
|
||||
assertEquals(0, fakeExecutor.numPending());
|
||||
assertEquals(3, runnable.mRunCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test FakeExecutor that is told to delay execution on items.
|
||||
*/
|
||||
@Test
|
||||
public void testDelayed_AdvanceAndRun() {
|
||||
FakeSystemClock clock = new FakeSystemClock();
|
||||
clock.setAutoIncrement(false);
|
||||
FakeExecutor fakeExecutor = new FakeExecutor(clock);
|
||||
RunnableImpl runnable = new RunnableImpl();
|
||||
|
||||
// Add three delayed runnables.
|
||||
fakeExecutor.executeDelayed(runnable, 1);
|
||||
fakeExecutor.executeDelayed(runnable, 50);
|
||||
fakeExecutor.executeDelayed(runnable, 100);
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
assertEquals(0, clock.uptimeMillis());
|
||||
assertEquals(3, fakeExecutor.numPending());
|
||||
// Delayed runnables should not advance the clock and therefore should not run.
|
||||
assertFalse(fakeExecutor.runNextReady());
|
||||
assertEquals(0, fakeExecutor.runAllReady());
|
||||
assertEquals(3, fakeExecutor.numPending());
|
||||
|
||||
// Advance the clock to the next runnable. Check that it is run.
|
||||
assertEquals(1, fakeExecutor.advanceClockToNext());
|
||||
assertEquals(1, fakeExecutor.runAllReady());
|
||||
assertEquals(1, clock.uptimeMillis());
|
||||
assertEquals(2, fakeExecutor.numPending());
|
||||
assertEquals(1, runnable.mRunCount);
|
||||
assertEquals(49, fakeExecutor.advanceClockToNext());
|
||||
assertEquals(1, fakeExecutor.runAllReady());
|
||||
assertEquals(50, clock.uptimeMillis());
|
||||
assertEquals(1, fakeExecutor.numPending());
|
||||
assertEquals(2, runnable.mRunCount);
|
||||
assertEquals(50, fakeExecutor.advanceClockToNext());
|
||||
assertEquals(1, fakeExecutor.runAllReady());
|
||||
assertEquals(100, clock.uptimeMillis());
|
||||
assertEquals(0, fakeExecutor.numPending());
|
||||
assertEquals(3, runnable.mRunCount);
|
||||
|
||||
// Nothing left to do
|
||||
assertEquals(0, fakeExecutor.advanceClockToNext());
|
||||
assertEquals(0, fakeExecutor.runAllReady());
|
||||
assertEquals(100, clock.uptimeMillis());
|
||||
assertEquals(0, fakeExecutor.numPending());
|
||||
assertEquals(3, runnable.mRunCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test execution order.
|
||||
*/
|
||||
@Test
|
||||
public void testExecutionOrder() {
|
||||
FakeSystemClock clock = new FakeSystemClock();
|
||||
clock.setAutoIncrement(false);
|
||||
FakeExecutor fakeExecutor = new FakeExecutor(clock);
|
||||
RunnableImpl runnableA = new RunnableImpl();
|
||||
RunnableImpl runnableB = new RunnableImpl();
|
||||
RunnableImpl runnableC = new RunnableImpl();
|
||||
RunnableImpl runnableD = new RunnableImpl();
|
||||
|
||||
Function4<Integer, Integer, Integer, Integer, Void> checkRunCounts =
|
||||
(Integer argA, Integer argB, Integer argC, Integer argD) -> {
|
||||
assertEquals("RunnableA run count wrong", argA.intValue(), runnableA.mRunCount);
|
||||
assertEquals("RunnableB run count wrong", argB.intValue(), runnableB.mRunCount);
|
||||
assertEquals("RunnableC run count wrong", argC.intValue(), runnableC.mRunCount);
|
||||
assertEquals("RunnableD run count wrong", argD.intValue(), runnableD.mRunCount);
|
||||
return null;
|
||||
};
|
||||
|
||||
assertEquals(0, clock.uptimeMillis());
|
||||
checkRunCounts.invoke(0, 0, 0, 0);
|
||||
|
||||
fakeExecutor.execute(runnableA);
|
||||
fakeExecutor.execute(runnableB);
|
||||
fakeExecutor.execute(runnableC);
|
||||
fakeExecutor.execute(runnableD);
|
||||
|
||||
fakeExecutor.runNextReady();
|
||||
checkRunCounts.invoke(1, 0, 0, 0);
|
||||
fakeExecutor.runNextReady();
|
||||
checkRunCounts.invoke(1, 1, 0, 0);
|
||||
fakeExecutor.runNextReady();
|
||||
checkRunCounts.invoke(1, 1, 1, 0);
|
||||
fakeExecutor.runNextReady();
|
||||
checkRunCounts.invoke(1, 1, 1, 1);
|
||||
|
||||
fakeExecutor.executeDelayed(runnableA, 100);
|
||||
fakeExecutor.execute(runnableB);
|
||||
fakeExecutor.executeDelayed(runnableC, 50);
|
||||
fakeExecutor.execute(runnableD);
|
||||
|
||||
fakeExecutor.advanceClockToNext();
|
||||
fakeExecutor.runAllReady();
|
||||
checkRunCounts.invoke(1, 2, 1, 2);
|
||||
fakeExecutor.advanceClockToNext();
|
||||
fakeExecutor.runAllReady();
|
||||
checkRunCounts.invoke(1, 2, 2, 2);
|
||||
fakeExecutor.advanceClockToNext();
|
||||
fakeExecutor.runAllReady();
|
||||
checkRunCounts.invoke(2, 2, 2, 2);
|
||||
|
||||
fakeExecutor.execute(runnableA);
|
||||
fakeExecutor.executeAtTime(runnableB, 0); // this is in the past!
|
||||
fakeExecutor.executeAtTime(runnableC, 1000);
|
||||
fakeExecutor.executeAtTime(runnableD, 500);
|
||||
|
||||
fakeExecutor.advanceClockToNext();
|
||||
fakeExecutor.runAllReady();
|
||||
checkRunCounts.invoke(3, 3, 2, 2);
|
||||
fakeExecutor.advanceClockToNext();
|
||||
fakeExecutor.runAllReady();
|
||||
checkRunCounts.invoke(3, 3, 2, 3);
|
||||
fakeExecutor.advanceClockToNext();
|
||||
fakeExecutor.runAllReady();
|
||||
checkRunCounts.invoke(3, 3, 3, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test removing a single item.
|
||||
*/
|
||||
@Test
|
||||
public void testRemoval_single() {
|
||||
FakeSystemClock clock = new FakeSystemClock();
|
||||
clock.setAutoIncrement(false);
|
||||
FakeExecutor fakeExecutor = new FakeExecutor(clock);
|
||||
RunnableImpl runnable = new RunnableImpl();
|
||||
Runnable removeFunction;
|
||||
|
||||
// Nothing to remove.
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
assertEquals(0, fakeExecutor.numPending());
|
||||
|
||||
// Two pending items that have not yet run.
|
||||
// We will try to remove the second item.
|
||||
fakeExecutor.executeDelayed(runnable, 100);
|
||||
removeFunction = fakeExecutor.executeDelayed(runnable, 200);
|
||||
assertEquals(2, fakeExecutor.numPending());
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
|
||||
// Remove the item.
|
||||
removeFunction.run();
|
||||
assertEquals(1, fakeExecutor.numPending());
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
|
||||
// One item to run.
|
||||
fakeExecutor.advanceClockToLast();
|
||||
fakeExecutor.runAllReady();
|
||||
assertEquals(0, fakeExecutor.numPending());
|
||||
assertEquals(1, runnable.mRunCount);
|
||||
|
||||
// Nothing to remove.
|
||||
removeFunction.run();
|
||||
fakeExecutor.runAllReady();
|
||||
assertEquals(0, fakeExecutor.numPending());
|
||||
assertEquals(1, runnable.mRunCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test removing multiple items.
|
||||
*/
|
||||
@Test
|
||||
public void testRemoval_multi() {
|
||||
FakeSystemClock clock = new FakeSystemClock();
|
||||
clock.setAutoIncrement(false);
|
||||
FakeExecutor fakeExecutor = new FakeExecutor(clock);
|
||||
List<Runnable> removeFunctions = new ArrayList<>();
|
||||
RunnableImpl runnable = new RunnableImpl();
|
||||
|
||||
// Nothing to remove.
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
assertEquals(0, fakeExecutor.numPending());
|
||||
|
||||
// Three pending items that have not yet run.
|
||||
// We will try to remove the first and third items.
|
||||
removeFunctions.add(fakeExecutor.executeDelayed(runnable, 100));
|
||||
fakeExecutor.executeDelayed(runnable, 200);
|
||||
removeFunctions.add(fakeExecutor.executeDelayed(runnable, 300));
|
||||
assertEquals(3, fakeExecutor.numPending());
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
|
||||
// Remove the items.
|
||||
removeFunctions.forEach(Runnable::run);
|
||||
assertEquals(1, fakeExecutor.numPending());
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
|
||||
// One item to run.
|
||||
fakeExecutor.advanceClockToLast();
|
||||
fakeExecutor.runAllReady();
|
||||
assertEquals(0, fakeExecutor.numPending());
|
||||
assertEquals(1, runnable.mRunCount);
|
||||
|
||||
// Nothing to remove.
|
||||
removeFunctions.forEach(Runnable::run);
|
||||
assertEquals(0, fakeExecutor.numPending());
|
||||
assertEquals(1, runnable.mRunCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test removing everything
|
||||
*/
|
||||
@Test
|
||||
public void testRemoval_all() {
|
||||
FakeSystemClock clock = new FakeSystemClock();
|
||||
clock.setAutoIncrement(false);
|
||||
FakeExecutor fakeExecutor = new FakeExecutor(clock);
|
||||
RunnableImpl runnable = new RunnableImpl();
|
||||
|
||||
// Nothing to remove.
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
assertEquals(0, fakeExecutor.numPending());
|
||||
|
||||
// Two pending items that have not yet run.
|
||||
fakeExecutor.executeDelayed(runnable, 100);
|
||||
fakeExecutor.executeDelayed(runnable, 200);
|
||||
assertEquals(2, fakeExecutor.numPending());
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
|
||||
// Remove the items.
|
||||
fakeExecutor.removeAll();
|
||||
|
||||
// Nothing to run
|
||||
fakeExecutor.advanceClockToLast();
|
||||
assertEquals(0, fakeExecutor.runAllReady());
|
||||
assertEquals(0, fakeExecutor.numPending());
|
||||
assertEquals(0, runnable.mRunCount);
|
||||
}
|
||||
|
||||
private static class RunnableImpl implements Runnable {
|
||||
int mRunCount;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mRunCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.systemui.util.time;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FakeSystemClock implements SystemClock {
|
||||
private boolean mAutoIncrement = true;
|
||||
|
||||
@@ -26,11 +29,13 @@ public class FakeSystemClock implements SystemClock {
|
||||
private long mCurrentThreadTimeMicro;
|
||||
private long mCurrentTimeMicro;
|
||||
|
||||
List<ClockTickListener> mListeners = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public long uptimeMillis() {
|
||||
long value = mUptimeMillis;
|
||||
if (mAutoIncrement) {
|
||||
mUptimeMillis++;
|
||||
setUptimeMillis(mUptimeMillis + 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -39,7 +44,7 @@ public class FakeSystemClock implements SystemClock {
|
||||
public long elapsedRealtime() {
|
||||
long value = mElapsedRealtime;
|
||||
if (mAutoIncrement) {
|
||||
mElapsedRealtime++;
|
||||
setElapsedRealtime(mElapsedRealtime + 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -48,7 +53,7 @@ public class FakeSystemClock implements SystemClock {
|
||||
public long elapsedRealtimeNanos() {
|
||||
long value = mElapsedRealtimeNanos;
|
||||
if (mAutoIncrement) {
|
||||
mElapsedRealtimeNanos++;
|
||||
setElapsedRealtimeNanos(mElapsedRealtimeNanos + 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -57,7 +62,7 @@ public class FakeSystemClock implements SystemClock {
|
||||
public long currentThreadTimeMillis() {
|
||||
long value = mCurrentThreadTimeMillis;
|
||||
if (mAutoIncrement) {
|
||||
mCurrentThreadTimeMillis++;
|
||||
setCurrentThreadTimeMillis(mCurrentThreadTimeMillis + 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -66,7 +71,7 @@ public class FakeSystemClock implements SystemClock {
|
||||
public long currentThreadTimeMicro() {
|
||||
long value = mCurrentThreadTimeMicro;
|
||||
if (mAutoIncrement) {
|
||||
mCurrentThreadTimeMicro++;
|
||||
setCurrentThreadTimeMicro(mCurrentThreadTimeMicro + 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -75,37 +80,90 @@ public class FakeSystemClock implements SystemClock {
|
||||
public long currentTimeMicro() {
|
||||
long value = mCurrentTimeMicro;
|
||||
if (mAutoIncrement) {
|
||||
mCurrentTimeMicro++;
|
||||
setCurrentTimeMicro(mCurrentTimeMicro + 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setUptimeMillis(long uptimeMillis) {
|
||||
mUptimeMillis = uptimeMillis;
|
||||
for (ClockTickListener listener : mListeners) {
|
||||
listener.onUptimeMillis(mUptimeMillis);
|
||||
}
|
||||
}
|
||||
|
||||
public void setElapsedRealtime(long elapsedRealtime) {
|
||||
mElapsedRealtime = elapsedRealtime;
|
||||
for (ClockTickListener listener : mListeners) {
|
||||
listener.onElapsedRealtime(mElapsedRealtime);
|
||||
}
|
||||
}
|
||||
|
||||
public void setElapsedRealtimeNanos(long elapsedRealtimeNanos) {
|
||||
mElapsedRealtimeNanos = elapsedRealtimeNanos;
|
||||
for (ClockTickListener listener : mListeners) {
|
||||
listener.onElapsedRealtimeNanos(mElapsedRealtimeNanos);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrentThreadTimeMillis(long currentThreadTimeMillis) {
|
||||
mCurrentThreadTimeMillis = currentThreadTimeMillis;
|
||||
for (ClockTickListener listener : mListeners) {
|
||||
listener.onCurrentThreadTimeMillis(mCurrentThreadTimeMillis);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrentThreadTimeMicro(long currentThreadTimeMicro) {
|
||||
mCurrentThreadTimeMicro = currentThreadTimeMicro;
|
||||
for (ClockTickListener listener : mListeners) {
|
||||
listener.onCurrentThreadTimeMicro(mCurrentThreadTimeMicro);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrentTimeMicro(long currentTimeMicro) {
|
||||
mCurrentTimeMicro = currentTimeMicro;
|
||||
for (ClockTickListener listener : mListeners) {
|
||||
listener.onCurrentTimeMicro(mCurrentTimeMicro);
|
||||
}
|
||||
}
|
||||
|
||||
/** If true, each call to get____ will be one higher than the previous call to that method. */
|
||||
public void setAutoIncrement(boolean autoIncrement) {
|
||||
mAutoIncrement = autoIncrement;
|
||||
}
|
||||
|
||||
public void addListener(ClockTickListener listener) {
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(ClockTickListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
/** Alert all the listeners about the current time. */
|
||||
public void synchronizeListeners() {
|
||||
for (ClockTickListener listener : mListeners) {
|
||||
listener.onUptimeMillis(mUptimeMillis);
|
||||
listener.onElapsedRealtime(mElapsedRealtime);
|
||||
listener.onElapsedRealtimeNanos(mElapsedRealtimeNanos);
|
||||
listener.onCurrentThreadTimeMillis(mCurrentThreadTimeMillis);
|
||||
listener.onCurrentThreadTimeMicro(mCurrentThreadTimeMicro);
|
||||
listener.onCurrentTimeMicro(mCurrentTimeMicro);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface ClockTickListener {
|
||||
default void onUptimeMillis(long uptimeMillis) {}
|
||||
|
||||
default void onElapsedRealtime(long elapsedRealtime) {}
|
||||
|
||||
default void onElapsedRealtimeNanos(long elapsedRealtimeNanos) {}
|
||||
|
||||
default void onCurrentThreadTimeMillis(long currentThreadTimeMillis) {}
|
||||
|
||||
default void onCurrentThreadTimeMicro(long currentThreadTimeMicro) {}
|
||||
|
||||
default void onCurrentTimeMicro(long currentTimeMicro) {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user