Merge "CommunalSource.Connection Introduction"
This commit is contained in:
@@ -35,7 +35,23 @@ public interface CommunalSource {
|
||||
* {@link Connector} defines an interface for {@link CommunalSource} instances to be generated.
|
||||
*/
|
||||
interface Connector {
|
||||
ListenableFuture<Optional<CommunalSource>> connect();
|
||||
Connection connect(Connection.Callback callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Connection} defines an interface for an entity which holds the necessary components
|
||||
* for establishing and maintaining a connection to the communal source.
|
||||
*/
|
||||
interface Connection {
|
||||
/**
|
||||
* {@link Callback} defines an interface for clients to be notified when a source is ready
|
||||
*/
|
||||
interface Callback {
|
||||
void onSourceEstablished(Optional<CommunalSource> source);
|
||||
void onDisconnected();
|
||||
}
|
||||
|
||||
void disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,29 +102,4 @@ public interface CommunalSource {
|
||||
* value will be {@code null} in case of a failure.
|
||||
*/
|
||||
ListenableFuture<CommunalViewResult> requestCommunalView(Context context);
|
||||
|
||||
/**
|
||||
* Adds a {@link Callback} to receive future status updates regarding this
|
||||
* {@link CommunalSource}.
|
||||
*
|
||||
* @param callback The {@link Callback} to be added.
|
||||
*/
|
||||
void addCallback(Callback callback);
|
||||
|
||||
/**
|
||||
* Removes a {@link Callback} from receiving future updates.
|
||||
*
|
||||
* @param callback The {@link Callback} to be removed.
|
||||
*/
|
||||
void removeCallback(Callback callback);
|
||||
|
||||
/**
|
||||
* An interface for receiving updates on the state of the {@link CommunalSource}.
|
||||
*/
|
||||
interface Callback {
|
||||
/**
|
||||
* Invoked when the {@link CommunalSource} is no longer available for use.
|
||||
*/
|
||||
void onDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,14 @@ import android.util.Log;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -41,6 +43,7 @@ public class CommunalSourceMonitor {
|
||||
// A list of {@link Callback} that have registered to receive updates.
|
||||
private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
|
||||
private final CommunalConditionsMonitor mConditionsMonitor;
|
||||
private final Executor mExecutor;
|
||||
|
||||
private CommunalSource mCurrentSource;
|
||||
|
||||
@@ -50,14 +53,6 @@ public class CommunalSourceMonitor {
|
||||
// Whether the class is currently listening for condition changes.
|
||||
private boolean mListeningForConditions = false;
|
||||
|
||||
private CommunalSource.Callback mSourceCallback = new CommunalSource.Callback() {
|
||||
@Override
|
||||
public void onDisconnected() {
|
||||
// Clear source reference.
|
||||
setSource(null /* source */);
|
||||
}
|
||||
};
|
||||
|
||||
private final CommunalConditionsMonitor.Callback mConditionsCallback =
|
||||
allConditionsMet -> {
|
||||
if (mAllCommunalConditionsMet != allConditionsMet) {
|
||||
@@ -70,7 +65,9 @@ public class CommunalSourceMonitor {
|
||||
|
||||
@VisibleForTesting
|
||||
@Inject
|
||||
public CommunalSourceMonitor(CommunalConditionsMonitor communalConditionsMonitor) {
|
||||
public CommunalSourceMonitor(@Main Executor executor,
|
||||
CommunalConditionsMonitor communalConditionsMonitor) {
|
||||
mExecutor = executor;
|
||||
mConditionsMonitor = communalConditionsMonitor;
|
||||
}
|
||||
|
||||
@@ -81,35 +78,28 @@ public class CommunalSourceMonitor {
|
||||
* @param source The new {@link CommunalSource}.
|
||||
*/
|
||||
public void setSource(CommunalSource source) {
|
||||
if (mCurrentSource != null) {
|
||||
mCurrentSource.removeCallback(mSourceCallback);
|
||||
}
|
||||
|
||||
mCurrentSource = source;
|
||||
|
||||
if (mAllCommunalConditionsMet) {
|
||||
executeOnSourceAvailableCallbacks();
|
||||
}
|
||||
|
||||
// Add callback to be informed when the source disconnects.
|
||||
if (mCurrentSource != null) {
|
||||
mCurrentSource.addCallback(mSourceCallback);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeOnSourceAvailableCallbacks() {
|
||||
// If the new source is valid, inform registered Callbacks of its presence.
|
||||
Iterator<WeakReference<Callback>> itr = mCallbacks.iterator();
|
||||
while (itr.hasNext()) {
|
||||
Callback cb = itr.next().get();
|
||||
if (cb == null) {
|
||||
itr.remove();
|
||||
} else {
|
||||
cb.onSourceAvailable(
|
||||
(mAllCommunalConditionsMet && mCurrentSource != null) ? new WeakReference<>(
|
||||
mCurrentSource) : null);
|
||||
mExecutor.execute(() -> {
|
||||
// If the new source is valid, inform registered Callbacks of its presence.
|
||||
Iterator<WeakReference<Callback>> itr = mCallbacks.iterator();
|
||||
while (itr.hasNext()) {
|
||||
Callback cb = itr.next().get();
|
||||
if (cb == null) {
|
||||
itr.remove();
|
||||
} else {
|
||||
cb.onSourceAvailable(
|
||||
(mAllCommunalConditionsMet && mCurrentSource != null)
|
||||
? new WeakReference<>(mCurrentSource) : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,17 +108,19 @@ public class CommunalSourceMonitor {
|
||||
* @param callback The {@link Callback} to add.
|
||||
*/
|
||||
public void addCallback(Callback callback) {
|
||||
mCallbacks.add(new WeakReference<>(callback));
|
||||
mExecutor.execute(() -> {
|
||||
mCallbacks.add(new WeakReference<>(callback));
|
||||
|
||||
// Inform the callback of any already present CommunalSource.
|
||||
if (mAllCommunalConditionsMet && mCurrentSource != null) {
|
||||
callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
|
||||
}
|
||||
// Inform the callback of any already present CommunalSource.
|
||||
if (mAllCommunalConditionsMet && mCurrentSource != null) {
|
||||
callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
|
||||
}
|
||||
|
||||
if (!mListeningForConditions) {
|
||||
mConditionsMonitor.addCallback(mConditionsCallback);
|
||||
mListeningForConditions = true;
|
||||
}
|
||||
if (!mListeningForConditions) {
|
||||
mConditionsMonitor.addCallback(mConditionsCallback);
|
||||
mListeningForConditions = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,12 +129,14 @@ public class CommunalSourceMonitor {
|
||||
* @param callback The {@link Callback} to add.
|
||||
*/
|
||||
public void removeCallback(Callback callback) {
|
||||
mCallbacks.removeIf(el -> el.get() == callback);
|
||||
mExecutor.execute(() -> {
|
||||
mCallbacks.removeIf(el -> el.get() == callback);
|
||||
|
||||
if (mCallbacks.isEmpty() && mListeningForConditions) {
|
||||
mConditionsMonitor.removeCallback(mConditionsCallback);
|
||||
mListeningForConditions = false;
|
||||
}
|
||||
if (mCallbacks.isEmpty() && mListeningForConditions) {
|
||||
mConditionsMonitor.removeCallback(mConditionsCallback);
|
||||
mListeningForConditions = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,8 +27,6 @@ import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||
import com.android.systemui.util.time.SystemClock;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -53,10 +51,11 @@ public class CommunalSourcePrimer extends CoreStartable {
|
||||
|
||||
private int mReconnectAttempts = 0;
|
||||
private Runnable mCurrentReconnectCancelable;
|
||||
private ListenableFuture<Optional<CommunalSource>> mGetSourceFuture;
|
||||
|
||||
private final Optional<CommunalSource.Connector> mConnector;
|
||||
private final Optional<CommunalSource.Observer> mObserver;
|
||||
private final Optional<CommunalSource.Connector> mConnector;
|
||||
|
||||
private CommunalSource.Connection mCurrentConnection;
|
||||
|
||||
private final Runnable mConnectRunnable = new Runnable() {
|
||||
@Override
|
||||
@@ -66,6 +65,10 @@ public class CommunalSourcePrimer extends CoreStartable {
|
||||
}
|
||||
};
|
||||
|
||||
private final CommunalSource.Observer.Callback mObserverCallback = () -> {
|
||||
initiateConnectionAttempt();
|
||||
};
|
||||
|
||||
@Inject
|
||||
public CommunalSourcePrimer(Context context, @Main Resources resources,
|
||||
SystemClock clock,
|
||||
@@ -132,7 +135,7 @@ public class CommunalSourcePrimer extends CoreStartable {
|
||||
@Override
|
||||
protected void onBootCompleted() {
|
||||
if (mObserver.isPresent()) {
|
||||
mObserver.get().addCallback(() -> initiateConnectionAttempt());
|
||||
mObserver.get().addCallback(mObserverCallback);
|
||||
}
|
||||
initiateConnectionAttempt();
|
||||
}
|
||||
@@ -142,34 +145,36 @@ public class CommunalSourcePrimer extends CoreStartable {
|
||||
Log.d(TAG, "attempting to communal to communal source");
|
||||
}
|
||||
|
||||
if (mGetSourceFuture != null) {
|
||||
if (mCurrentConnection != null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "canceling in-flight connection");
|
||||
}
|
||||
mGetSourceFuture.cancel(true);
|
||||
mCurrentConnection.disconnect();
|
||||
}
|
||||
|
||||
mGetSourceFuture = mConnector.get().connect();
|
||||
mGetSourceFuture.addListener(() -> {
|
||||
try {
|
||||
final long startTime = mSystemClock.currentTimeMillis();
|
||||
Optional<CommunalSource> result = mGetSourceFuture.get();
|
||||
if (result.isPresent()) {
|
||||
final CommunalSource source = result.get();
|
||||
source.addCallback(() -> {
|
||||
if (mSystemClock.currentTimeMillis() - startTime > mMinConnectionDuration) {
|
||||
initiateConnectionAttempt();
|
||||
} else {
|
||||
scheduleConnectionAttempt();
|
||||
}
|
||||
});
|
||||
mCurrentConnection = mConnector.get().connect(new CommunalSource.Connection.Callback() {
|
||||
private long mStartTime;
|
||||
|
||||
@Override
|
||||
public void onSourceEstablished(Optional<CommunalSource> optionalSource) {
|
||||
mStartTime = mSystemClock.currentTimeMillis();
|
||||
|
||||
if (optionalSource.isPresent()) {
|
||||
final CommunalSource source = optionalSource.get();
|
||||
mMonitor.setSource(source);
|
||||
} else {
|
||||
scheduleConnectionAttempt();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, mMainExecutor);
|
||||
|
||||
@Override
|
||||
public void onDisconnected() {
|
||||
if (mSystemClock.currentTimeMillis() - mStartTime > mMinConnectionDuration) {
|
||||
initiateConnectionAttempt();
|
||||
} else {
|
||||
scheduleConnectionAttempt();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,8 +519,6 @@ public class NotificationPanelViewController extends PanelViewController {
|
||||
|
||||
private WeakReference<CommunalSource> mCommunalSource;
|
||||
|
||||
private final CommunalSource.Callback mCommunalSourceCallback;
|
||||
|
||||
private final CommandQueue mCommandQueue;
|
||||
private final NotificationLockscreenUserManager mLockscreenUserManager;
|
||||
private final UserManager mUserManager;
|
||||
@@ -906,9 +904,6 @@ public class NotificationPanelViewController extends PanelViewController {
|
||||
|
||||
mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
|
||||
mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition());
|
||||
mCommunalSourceCallback = () -> {
|
||||
mUiExecutor.execute(() -> setCommunalSource(null /*source*/));
|
||||
};
|
||||
|
||||
mCommunalSourceMonitorCallback = (source) -> {
|
||||
mUiExecutor.execute(() -> setCommunalSource(source));
|
||||
@@ -4713,7 +4708,6 @@ public class NotificationPanelViewController extends PanelViewController {
|
||||
CommunalSource existingSource = mCommunalSource != null ? mCommunalSource.get() : null;
|
||||
|
||||
if (existingSource != null) {
|
||||
existingSource.removeCallback(mCommunalSourceCallback);
|
||||
mCommunalViewController.show(null /*source*/);
|
||||
}
|
||||
|
||||
@@ -4722,7 +4716,6 @@ public class NotificationPanelViewController extends PanelViewController {
|
||||
CommunalSource currentSource = mCommunalSource != null ? mCommunalSource.get() : null;
|
||||
// Set source and register callback
|
||||
if (currentSource != null && mCommunalViewController != null) {
|
||||
currentSource.addCallback(mCommunalSourceCallback);
|
||||
mCommunalViewController.show(source);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
package com.android.systemui.util.service;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -26,6 +24,8 @@ import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.ref.WeakReference;
|
||||
@@ -35,6 +35,8 @@ import java.util.Optional;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* {@link ObservableServiceConnection} is a concrete implementation of {@link ServiceConnection}
|
||||
* that enables monitoring the status of a binder connection. It also aides in automatically
|
||||
@@ -119,17 +121,17 @@ public class ObservableServiceConnection<T> implements ServiceConnection {
|
||||
* Default constructor for {@link ObservableServiceConnection}.
|
||||
* @param context The context from which the service will be bound with.
|
||||
* @param serviceIntent The intent to bind service with.
|
||||
* @param flags The flags to use during the binding
|
||||
* @param executor The executor for connection callbacks to be delivered on
|
||||
* @param transformer A {@link ServiceTransformer} for transforming the resulting service
|
||||
* into a desired type.
|
||||
*/
|
||||
@Inject
|
||||
public ObservableServiceConnection(Context context, Intent serviceIntent,
|
||||
@Context.BindServiceFlags int flags, @NonNull @CallbackExecutor Executor executor,
|
||||
@Main Executor executor,
|
||||
ServiceTransformer<T> transformer) {
|
||||
mContext = context;
|
||||
mServiceIntent = serviceIntent;
|
||||
mFlags = flags;
|
||||
mFlags = Context.BIND_AUTO_CREATE;
|
||||
mExecutor = executor;
|
||||
mTransformer = transformer;
|
||||
mCallbacks = new ArrayList<>();
|
||||
|
||||
@@ -29,6 +29,8 @@ import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -45,6 +47,8 @@ public class CommunalManagerUpdaterTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private CommunalConditionsMonitor mCommunalConditionsMonitor;
|
||||
|
||||
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
@@ -56,7 +60,7 @@ public class CommunalManagerUpdaterTest extends SysuiTestCase {
|
||||
return null;
|
||||
}).when(mCommunalConditionsMonitor).addCallback(any());
|
||||
|
||||
mMonitor = new CommunalSourceMonitor(mCommunalConditionsMonitor);
|
||||
mMonitor = new CommunalSourceMonitor(mExecutor, mCommunalConditionsMonitor);
|
||||
final CommunalManagerUpdater updater = new CommunalManagerUpdater(mContext, mMonitor);
|
||||
updater.start();
|
||||
clearInvocations(mCommunalManager);
|
||||
@@ -65,6 +69,7 @@ public class CommunalManagerUpdaterTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void testUpdateSystemService_false() {
|
||||
mMonitor.setSource(null);
|
||||
mExecutor.runAllReady();
|
||||
verify(mCommunalManager).setCommunalViewShowing(false);
|
||||
}
|
||||
|
||||
@@ -72,6 +77,7 @@ public class CommunalManagerUpdaterTest extends SysuiTestCase {
|
||||
public void testUpdateSystemService_true() {
|
||||
final CommunalSource source = mock(CommunalSource.class);
|
||||
mMonitor.setSource(source);
|
||||
mExecutor.runAllReady();
|
||||
verify(mCommunalManager).setCommunalViewShowing(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -52,11 +54,12 @@ public class CommunalSourceMonitorTest extends SysuiTestCase {
|
||||
@Captor private ArgumentCaptor<CommunalConditionsMonitor.Callback> mConditionsCallbackCaptor;
|
||||
|
||||
private CommunalSourceMonitor mCommunalSourceMonitor;
|
||||
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mCommunalSourceMonitor = new CommunalSourceMonitor(mCommunalConditionsMonitor);
|
||||
mCommunalSourceMonitor = new CommunalSourceMonitor(mExecutor, mCommunalConditionsMonitor);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -64,7 +67,7 @@ public class CommunalSourceMonitorTest extends SysuiTestCase {
|
||||
final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
|
||||
final CommunalSource source = mock(CommunalSource.class);
|
||||
|
||||
mCommunalSourceMonitor.setSource(source);
|
||||
setSource(source);
|
||||
mCommunalSourceMonitor.addCallback(callback);
|
||||
setConditionsMet(true);
|
||||
|
||||
@@ -78,7 +81,7 @@ public class CommunalSourceMonitorTest extends SysuiTestCase {
|
||||
|
||||
mCommunalSourceMonitor.addCallback(callback);
|
||||
mCommunalSourceMonitor.removeCallback(callback);
|
||||
mCommunalSourceMonitor.setSource(source);
|
||||
setSource(source);
|
||||
|
||||
verify(callback, never()).onSourceAvailable(any());
|
||||
}
|
||||
@@ -91,7 +94,7 @@ public class CommunalSourceMonitorTest extends SysuiTestCase {
|
||||
mCommunalSourceMonitor.addCallback(callback);
|
||||
setConditionsMet(true);
|
||||
clearInvocations(callback);
|
||||
mCommunalSourceMonitor.setSource(source);
|
||||
setSource(source);
|
||||
|
||||
verifyOnSourceAvailableCalledWith(callback, source);
|
||||
}
|
||||
@@ -103,7 +106,7 @@ public class CommunalSourceMonitorTest extends SysuiTestCase {
|
||||
|
||||
mCommunalSourceMonitor.addCallback(callback);
|
||||
setConditionsMet(false);
|
||||
mCommunalSourceMonitor.setSource(source);
|
||||
setSource(source);
|
||||
|
||||
verify(callback, never()).onSourceAvailable(any());
|
||||
}
|
||||
@@ -114,7 +117,7 @@ public class CommunalSourceMonitorTest extends SysuiTestCase {
|
||||
final CommunalSource source = mock(CommunalSource.class);
|
||||
|
||||
mCommunalSourceMonitor.addCallback(callback);
|
||||
mCommunalSourceMonitor.setSource(source);
|
||||
setSource(source);
|
||||
|
||||
// The callback should not have executed since communal is disabled.
|
||||
verify(callback, never()).onSourceAvailable(any());
|
||||
@@ -130,7 +133,7 @@ public class CommunalSourceMonitorTest extends SysuiTestCase {
|
||||
final CommunalSource source = mock(CommunalSource.class);
|
||||
|
||||
mCommunalSourceMonitor.addCallback(callback);
|
||||
mCommunalSourceMonitor.setSource(source);
|
||||
setSource(source);
|
||||
setConditionsMet(true);
|
||||
verifyOnSourceAvailableCalledWith(callback, source);
|
||||
|
||||
@@ -151,9 +154,16 @@ public class CommunalSourceMonitorTest extends SysuiTestCase {
|
||||
// Pushes an update on whether the communal conditions are met, assuming that a callback has
|
||||
// been registered with the communal conditions monitor.
|
||||
private void setConditionsMet(boolean value) {
|
||||
mExecutor.runAllReady();
|
||||
verify(mCommunalConditionsMonitor).addCallback(mConditionsCallbackCaptor.capture());
|
||||
final CommunalConditionsMonitor.Callback conditionsCallback =
|
||||
mConditionsCallbackCaptor.getValue();
|
||||
conditionsCallback.onConditionsChanged(value);
|
||||
mExecutor.runAllReady();
|
||||
}
|
||||
|
||||
private void setSource(CommunalSource source) {
|
||||
mCommunalSourceMonitor.setSource(source);
|
||||
mExecutor.runAllReady();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.communal;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -26,14 +27,16 @@ import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
|
||||
import androidx.concurrent.futures.CallbackToFutureAdapter;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;;
|
||||
import com.android.systemui.util.ref.GcWeakReference;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -52,6 +55,35 @@ public class CommunalSourcePrimerTest extends SysuiTestCase {
|
||||
private static final int RETRY_DELAY_MS = 1000;
|
||||
private static final int CONNECTION_MIN_DURATION_MS = 5000;
|
||||
|
||||
// A simple implementation of {@link CommunalSource.Observer} to capture a callback value.
|
||||
// Used to ensure the references to a {@link CommunalSource.Observer.Callback} can be fully
|
||||
// removed.
|
||||
private static class FakeObserver implements CommunalSource.Observer {
|
||||
public GcWeakReference<Callback> mLastCallback;
|
||||
|
||||
@Override
|
||||
public void addCallback(Callback callback) {
|
||||
mLastCallback = new GcWeakReference<>(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCallback(Callback callback) {
|
||||
if (mLastCallback.get() == callback) {
|
||||
mLastCallback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A simple implementation of {@link CommunalSource} to capture callback values. This
|
||||
// implementation better emulates the {@link WeakReference} wrapping behavior of
|
||||
// {@link CommunalSource} implementations than a mock.
|
||||
private static class FakeSource implements CommunalSource {
|
||||
@Override
|
||||
public ListenableFuture<CommunalViewResult> requestCommunalView(Context context) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Mock
|
||||
private Context mContext;
|
||||
|
||||
@@ -61,8 +93,7 @@ public class CommunalSourcePrimerTest extends SysuiTestCase {
|
||||
private FakeSystemClock mFakeClock = new FakeSystemClock();
|
||||
private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeClock);
|
||||
|
||||
@Mock
|
||||
private CommunalSource mSource;
|
||||
private FakeSource mSource = new FakeSource();
|
||||
|
||||
@Mock
|
||||
private CommunalSourceMonitor mCommunalSourceMonitor;
|
||||
@@ -71,7 +102,9 @@ public class CommunalSourcePrimerTest extends SysuiTestCase {
|
||||
private CommunalSource.Connector mConnector;
|
||||
|
||||
@Mock
|
||||
private CommunalSource.Observer mObserver;
|
||||
private CommunalSource.Connection mConnection;
|
||||
|
||||
private FakeObserver mObserver = new FakeObserver();
|
||||
|
||||
private CommunalSourcePrimer mPrimer;
|
||||
|
||||
@@ -93,35 +126,35 @@ public class CommunalSourcePrimerTest extends SysuiTestCase {
|
||||
mCommunalSourceMonitor, Optional.of(mConnector), Optional.of(mObserver));
|
||||
}
|
||||
|
||||
private CommunalSource.Connection.Callback captureCallbackAndSend(
|
||||
CommunalSource.Connector connector, Optional<CommunalSource> source) {
|
||||
ArgumentCaptor<CommunalSource.Connection.Callback> connectionCallback =
|
||||
ArgumentCaptor.forClass(CommunalSource.Connection.Callback.class);
|
||||
|
||||
verify(connector).connect(connectionCallback.capture());
|
||||
Mockito.clearInvocations(connector);
|
||||
|
||||
final CommunalSource.Connection.Callback callback = connectionCallback.getValue();
|
||||
callback.onSourceEstablished(source);
|
||||
|
||||
return callback;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnect() {
|
||||
when(mConnector.connect()).thenReturn(
|
||||
CallbackToFutureAdapter.getFuture(completer -> {
|
||||
completer.set(Optional.of(mSource));
|
||||
return "test";
|
||||
}));
|
||||
|
||||
mPrimer.onBootCompleted();
|
||||
mFakeExecutor.runAllReady();
|
||||
captureCallbackAndSend(mConnector, Optional.of(mSource));
|
||||
verify(mCommunalSourceMonitor).setSource(mSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetryOnBindFailure() throws Exception {
|
||||
when(mConnector.connect()).thenReturn(
|
||||
CallbackToFutureAdapter.getFuture(completer -> {
|
||||
completer.set(Optional.empty());
|
||||
return "test";
|
||||
}));
|
||||
|
||||
mPrimer.onBootCompleted();
|
||||
mFakeExecutor.runAllReady();
|
||||
|
||||
// Verify attempts happen. Note that we account for the retries plus initial attempt, which
|
||||
// is not scheduled.
|
||||
for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
|
||||
verify(mConnector, times(1)).connect();
|
||||
clearInvocations(mConnector);
|
||||
captureCallbackAndSend(mConnector, Optional.empty());
|
||||
mFakeExecutor.advanceClockToNext();
|
||||
mFakeExecutor.runAllReady();
|
||||
}
|
||||
@@ -131,76 +164,42 @@ public class CommunalSourcePrimerTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testRetryOnDisconnectFailure() throws Exception {
|
||||
when(mConnector.connect()).thenReturn(
|
||||
CallbackToFutureAdapter.getFuture(completer -> {
|
||||
completer.set(Optional.of(mSource));
|
||||
return "test";
|
||||
}));
|
||||
|
||||
mPrimer.onBootCompleted();
|
||||
mFakeExecutor.runAllReady();
|
||||
|
||||
// Verify attempts happen. Note that we account for the retries plus initial attempt, which
|
||||
// is not scheduled.
|
||||
for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
|
||||
verify(mConnector, times(1)).connect();
|
||||
clearInvocations(mConnector);
|
||||
ArgumentCaptor<CommunalSource.Callback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(CommunalSource.Callback.class);
|
||||
verify(mSource).addCallback(callbackCaptor.capture());
|
||||
clearInvocations(mSource);
|
||||
final CommunalSource.Connection.Callback callback =
|
||||
captureCallbackAndSend(mConnector, Optional.of(mSource));
|
||||
verify(mCommunalSourceMonitor).setSource(Mockito.notNull());
|
||||
clearInvocations(mCommunalSourceMonitor);
|
||||
callbackCaptor.getValue().onDisconnected();
|
||||
callback.onDisconnected();
|
||||
mFakeExecutor.advanceClockToNext();
|
||||
mFakeExecutor.runAllReady();
|
||||
}
|
||||
|
||||
verify(mConnector, never()).connect();
|
||||
verify(mConnector, never()).connect(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttemptOnPackageChange() {
|
||||
when(mConnector.connect()).thenReturn(
|
||||
CallbackToFutureAdapter.getFuture(completer -> {
|
||||
completer.set(Optional.empty());
|
||||
return "test";
|
||||
}));
|
||||
|
||||
mPrimer.onBootCompleted();
|
||||
mFakeExecutor.runAllReady();
|
||||
captureCallbackAndSend(mConnector, Optional.empty());
|
||||
|
||||
final ArgumentCaptor<CommunalSource.Observer.Callback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(CommunalSource.Observer.Callback.class);
|
||||
verify(mObserver).addCallback(callbackCaptor.capture());
|
||||
mObserver.mLastCallback.get().onSourceChanged();
|
||||
|
||||
clearInvocations(mConnector);
|
||||
callbackCaptor.getValue().onSourceChanged();
|
||||
|
||||
verify(mConnector, times(1)).connect();
|
||||
verify(mConnector, times(1)).connect(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisconnect() {
|
||||
final ArgumentCaptor<CommunalSource.Callback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(CommunalSource.Callback.class);
|
||||
|
||||
when(mConnector.connect()).thenReturn(
|
||||
CallbackToFutureAdapter.getFuture(completer -> {
|
||||
completer.set(Optional.of(mSource));
|
||||
return "test";
|
||||
}));
|
||||
|
||||
mPrimer.onBootCompleted();
|
||||
mFakeExecutor.runAllReady();
|
||||
final CommunalSource.Connection.Callback callback =
|
||||
captureCallbackAndSend(mConnector, Optional.of(mSource));
|
||||
verify(mCommunalSourceMonitor).setSource(mSource);
|
||||
verify(mSource).addCallback(callbackCaptor.capture());
|
||||
|
||||
clearInvocations(mConnector);
|
||||
mFakeClock.advanceTime(CONNECTION_MIN_DURATION_MS + 1);
|
||||
callbackCaptor.getValue().onDisconnected();
|
||||
mFakeExecutor.runAllReady();
|
||||
callback.onDisconnected();
|
||||
|
||||
verify(mConnector).connect();
|
||||
verify(mConnector).connect(any());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.ref;
|
||||
|
||||
import com.android.internal.util.GcUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* A WeakReference subclass that forces gc/finalizing on access.
|
||||
*/
|
||||
public class GcWeakReference<T> extends WeakReference<T> {
|
||||
public GcWeakReference(T referent) {
|
||||
super(referent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() throws RuntimeException {
|
||||
GcUtils.runGcAndFinalizersSync();
|
||||
return super.get();
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ public class ObservableServiceConnectionTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void testConnect() {
|
||||
ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
|
||||
mIntent, 0, mExecutor, mTransformer);
|
||||
mIntent, mExecutor, mTransformer);
|
||||
// Register twice to ensure only one callback occurs.
|
||||
connection.addCallback(mCallback);
|
||||
connection.addCallback(mCallback);
|
||||
@@ -119,7 +119,7 @@ public class ObservableServiceConnectionTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void testDisconnect() {
|
||||
ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
|
||||
mIntent, 0, mExecutor, mTransformer);
|
||||
mIntent, mExecutor, mTransformer);
|
||||
connection.addCallback(mCallback);
|
||||
connection.onServiceDisconnected(mComponentName);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user