ConditionalCoreStartable Introduction.

This changelist introduces ConditionalCoreStartable, an abstract
implementation of CoreStartable that allows conditions to gate
actions from lifecycle events (start/boot completed).

Bug: 261420431
Test: atest ConditionalCoreStartableTest
Change-Id: Ib4121d375f509ec16e8d2e34cdbee4f92f2ed7d6
This commit is contained in:
Bryce Lee
2023-02-06 09:16:48 -08:00
parent e026a6ff3f
commit 099a3b8d20
2 changed files with 239 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2023 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.condition;
import com.android.systemui.CoreStartable;
import com.android.systemui.shared.condition.Condition;
import com.android.systemui.shared.condition.Monitor;
import java.util.Set;
/**
* {@link ConditionalCoreStartable} is a {@link com.android.systemui.CoreStartable} abstract
* implementation where conditions must be met before routines are executed.
*/
public abstract class ConditionalCoreStartable implements CoreStartable {
private final Monitor mMonitor;
private final Set<Condition> mConditionSet;
private Monitor.Subscription.Token mStartToken;
private Monitor.Subscription.Token mBootCompletedToken;
public ConditionalCoreStartable(Monitor monitor) {
this(monitor, null);
}
public ConditionalCoreStartable(Monitor monitor, Set<Condition> conditionSet) {
mMonitor = monitor;
mConditionSet = conditionSet;
}
@Override
public final void start() {
if (mConditionSet == null || mConditionSet.isEmpty()) {
onStart();
return;
}
mStartToken = mMonitor.addSubscription(
new Monitor.Subscription.Builder(allConditionsMet -> {
if (allConditionsMet) {
mMonitor.removeSubscription(mStartToken);
mStartToken = null;
onStart();
}
}).addConditions(mConditionSet)
.build());
}
protected abstract void onStart();
@Override
public final void onBootCompleted() {
if (mConditionSet == null || mConditionSet.isEmpty()) {
bootCompleted();
return;
}
mBootCompletedToken = mMonitor.addSubscription(
new Monitor.Subscription.Builder(allConditionsMet -> {
if (allConditionsMet) {
mMonitor.removeSubscription(mBootCompletedToken);
mBootCompletedToken = null;
bootCompleted();
}
}).addConditions(mConditionSet)
.build());
}
protected void bootCompleted() {
}
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright (C) 2023 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.condition;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.CoreStartable;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.condition.Condition;
import com.android.systemui.shared.condition.Monitor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class ConditionalCoreStartableTest extends SysuiTestCase {
public static class FakeConditionalCoreStartable extends ConditionalCoreStartable {
interface Callback {
void onStart();
void bootCompleted();
}
private final Callback mCallback;
public FakeConditionalCoreStartable(Monitor monitor, Set<Condition> conditions,
Callback callback) {
super(monitor, conditions);
mCallback = callback;
}
@Override
protected void onStart() {
mCallback.onStart();
}
@Override
protected void bootCompleted() {
mCallback.bootCompleted();
}
}
final Set<Condition> mConditions = new HashSet<>();
@Mock
Condition mCondition;
@Mock
Monitor mMonitor;
@Mock
FakeConditionalCoreStartable.Callback mCallback;
@Mock
Monitor.Subscription.Token mSubscriptionToken;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mConditions.clear();
}
/**
* Verifies that {@link ConditionalCoreStartable#onStart()} is predicated on conditions being
* met.
*/
@Test
public void testOnStartCallback() {
final CoreStartable coreStartable =
new FakeConditionalCoreStartable(mMonitor,
new HashSet<>(Arrays.asList(mCondition)),
mCallback);
when(mMonitor.addSubscription(any())).thenReturn(mSubscriptionToken);
coreStartable.start();
final ArgumentCaptor<Monitor.Subscription> subscriptionCaptor = ArgumentCaptor.forClass(
Monitor.Subscription.class);
verify(mMonitor).addSubscription(subscriptionCaptor.capture());
final Monitor.Subscription subscription = subscriptionCaptor.getValue();
assertThat(subscription.getConditions()).containsExactly(mCondition);
verify(mCallback, never()).onStart();
subscription.getCallback().onConditionsChanged(true);
verify(mCallback).onStart();
verify(mMonitor).removeSubscription(mSubscriptionToken);
}
/**
* Verifies that {@link ConditionalCoreStartable#bootCompleted()} ()} is predicated on
* conditions being met.
*/
@Test
public void testBootCompleted() {
final CoreStartable coreStartable =
new FakeConditionalCoreStartable(mMonitor,
new HashSet<>(Arrays.asList(mCondition)),
mCallback);
when(mMonitor.addSubscription(any())).thenReturn(mSubscriptionToken);
coreStartable.onBootCompleted();
final ArgumentCaptor<Monitor.Subscription> subscriptionCaptor = ArgumentCaptor.forClass(
Monitor.Subscription.class);
verify(mMonitor).addSubscription(subscriptionCaptor.capture());
final Monitor.Subscription subscription = subscriptionCaptor.getValue();
assertThat(subscription.getConditions()).containsExactly(mCondition);
verify(mCallback, never()).bootCompleted();
subscription.getCallback().onConditionsChanged(true);
verify(mCallback).bootCompleted();
verify(mMonitor).removeSubscription(mSubscriptionToken);
}
}