Adding proximity endpoints to Attention Service

Test: atest AttentionManagerServiceTest
Bug: 214395649

Change-Id: I821fa883a2a23da664913e7a831b59701393903f
This commit is contained in:
Daniel
2022-01-25 00:29:49 +00:00
parent 33b42f50df
commit 654abc5005
7 changed files with 492 additions and 8 deletions

View File

@@ -10417,6 +10417,8 @@ package android.service.attention {
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onCancelAttentionCheck(@NonNull android.service.attention.AttentionService.AttentionCallback);
method public abstract void onCheckAttention(@NonNull android.service.attention.AttentionService.AttentionCallback);
method public void onStartProximityUpdates(@NonNull android.service.attention.AttentionService.ProximityCallback);
method public void onStopProximityUpdates();
field public static final int ATTENTION_FAILURE_CAMERA_PERMISSION_ABSENT = 6; // 0x6
field public static final int ATTENTION_FAILURE_CANCELLED = 3; // 0x3
field public static final int ATTENTION_FAILURE_PREEMPTED = 4; // 0x4
@@ -10424,6 +10426,7 @@ package android.service.attention {
field public static final int ATTENTION_FAILURE_UNKNOWN = 2; // 0x2
field public static final int ATTENTION_SUCCESS_ABSENT = 0; // 0x0
field public static final int ATTENTION_SUCCESS_PRESENT = 1; // 0x1
field public static final double PROXIMITY_UNKNOWN = -1.0;
field public static final String SERVICE_INTERFACE = "android.service.attention.AttentionService";
}
@@ -10432,6 +10435,10 @@ package android.service.attention {
method public void onSuccess(int, long);
}
public static final class AttentionService.ProximityCallback {
method public void onProximityUpdate(double);
}
}
package android.service.autofill {

View File

@@ -46,6 +46,25 @@ public abstract class AttentionManagerInternal {
*/
public abstract void cancelAttentionCheck(AttentionCallbackInternal callback);
/**
* Requests the continuous updates of proximity signal via the provided callback,
* until the given callback is unregistered. Currently, AttentionManagerService only
* anticipates one client and updates one client at a time. If a new client wants to
* onboard to receiving Proximity updates, please make a feature request to make proximity
* feature multi-client before depending on this feature.
*
* @param callback a callback that receives the proximity updates
* @return {@code true} if the registration should succeed.
*/
public abstract boolean onStartProximityUpdates(ProximityCallbackInternal callback);
/**
* Requests to stop providing continuous updates until the callback is registered.
*
* @param callback a callback that was used in {@link #onStartProximityUpdates}
*/
public abstract void onStopProximityUpdates(ProximityCallbackInternal callback);
/** Internal interface for attention callback. */
public abstract static class AttentionCallbackInternal {
/**
@@ -64,4 +83,13 @@ public abstract class AttentionManagerInternal {
*/
public abstract void onFailure(int error);
}
/** Internal interface for proximity callback. */
public abstract static class ProximityCallbackInternal {
/**
* @param distance the estimated distance of the user (in meter)
* The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive.
*/
public abstract void onProximityUpdate(double distance);
}
}

View File

@@ -24,11 +24,14 @@ import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.Objects;
/**
@@ -51,6 +54,7 @@ import java.lang.annotation.RetentionPolicy;
*/
@SystemApi
public abstract class AttentionService extends Service {
private static final String LOG_TAG = "AttentionService";
/**
* The {@link Intent} that must be declared as handled by the service. To be supported, the
* service must also require the {@link android.Manifest.permission#BIND_ATTENTION_SERVICE}
@@ -80,6 +84,9 @@ public abstract class AttentionService extends Service {
/** Camera permission is not granted. */
public static final int ATTENTION_FAILURE_CAMERA_PERMISSION_ABSENT = 6;
/** Users’ proximity is unknown (proximity sensing was inconclusive and is unsupported). */
public static final double PROXIMITY_UNKNOWN = -1;
/**
* Result codes for when attention check was successful.
*
@@ -118,6 +125,20 @@ public abstract class AttentionService extends Service {
Preconditions.checkNotNull(callback);
AttentionService.this.onCancelAttentionCheck(new AttentionCallback(callback));
}
/** {@inheritDoc} */
@Override
public void onStartProximityUpdates(IProximityCallback callback) {
Objects.requireNonNull(callback);
AttentionService.this.onStartProximityUpdates(new ProximityCallback(callback));
}
/** {@inheritDoc} */
@Override
public void onStopProximityUpdates() {
AttentionService.this.onStopProximityUpdates();
}
};
@Nullable
@@ -143,6 +164,23 @@ public abstract class AttentionService extends Service {
*/
public abstract void onCancelAttentionCheck(@NonNull AttentionCallback callback);
/**
* Requests the continuous updates of proximity signal via the provided callback,
* until the given callback is unregistered.
*
* @param callback the callback to return the result to
*/
public void onStartProximityUpdates(@NonNull ProximityCallback callback) {
Slog.w(LOG_TAG, "Override this method.");
}
/**
* Requests to stop providing continuous updates until the callback is registered.
*/
public void onStopProximityUpdates() {
Slog.w(LOG_TAG, "Override this method.");
}
/** Callbacks for AttentionService results. */
public static final class AttentionCallback {
@NonNull private final IAttentionCallback mCallback;
@@ -174,4 +212,26 @@ public abstract class AttentionService extends Service {
}
}
}
/** Callbacks for ProximityCallback results. */
public static final class ProximityCallback {
@NonNull private final WeakReference<IProximityCallback> mCallback;
private ProximityCallback(@NonNull IProximityCallback callback) {
mCallback = new WeakReference<>(callback);
}
/**
* @param distance the estimated distance of the user (in meter)
* The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive.
*
*/
public void onProximityUpdate(double distance) {
try {
mCallback.get().onProximityUpdate(distance);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
}
}

View File

@@ -17,6 +17,7 @@
package android.service.attention;
import android.service.attention.IAttentionCallback;
import android.service.attention.IProximityCallback;
/**
* Interface for a concrete implementation to provide to the AttentionManagerService.
@@ -26,4 +27,6 @@ import android.service.attention.IAttentionCallback;
oneway interface IAttentionService {
void checkAttention(IAttentionCallback callback);
void cancelAttentionCheck(IAttentionCallback callback);
void onStartProximityUpdates(IProximityCallback callback);
void onStopProximityUpdates();
}

View File

@@ -0,0 +1,10 @@
package android.service.attention;
/**
* Callback for onStartProximityUpdates request.
*
* @hide
*/
oneway interface IProximityCallback {
void onProximityUpdate(double distance);
}

View File

@@ -22,6 +22,7 @@ import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
import static android.service.attention.AttentionService.ATTENTION_FAILURE_CANCELLED;
import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNOWN;
import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN;
import android.Manifest;
import android.annotation.NonNull;
@@ -29,6 +30,7 @@ import android.annotation.Nullable;
import android.app.ActivityThread;
import android.attention.AttentionManagerInternal;
import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
import android.attention.AttentionManagerInternal.ProximityCallbackInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -57,6 +59,7 @@ import android.service.attention.AttentionService.AttentionFailureCodes;
import android.service.attention.AttentionService.AttentionSuccessCodes;
import android.service.attention.IAttentionCallback;
import android.service.attention.IAttentionService;
import android.service.attention.IProximityCallback;
import android.text.TextUtils;
import android.util.Slog;
@@ -134,6 +137,15 @@ public class AttentionManagerService extends SystemService {
@GuardedBy("mLock")
AttentionCheck mCurrentAttentionCheck;
/**
* A proxy for relaying proximity information between the Attention Service and the client.
* The proxy will be initialized when the client calls onStartProximityUpdates and will be
* disabled only when the client calls onStopProximityUpdates.
*/
@VisibleForTesting
@GuardedBy("mLock")
ProximityUpdate mCurrentProximityUpdate;
public AttentionManagerService(Context context) {
this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE),
new Object(), null);
@@ -315,6 +327,77 @@ public class AttentionManagerService extends SystemService {
}
}
/**
* Requests the continuous updates of proximity signal via the provided callback,
* until the given callback is stopped.
*
* Calling this multiple times for duplicate requests will be no-ops, returning true.
*
* @return {@code true} if the framework was able to dispatch the request
*/
@VisibleForTesting
boolean onStartProximityUpdates(ProximityCallbackInternal callbackInternal) {
Objects.requireNonNull(callbackInternal);
if (!mIsServiceEnabled) {
Slog.w(LOG_TAG, "Trying to call onProximityUpdate() on an unsupported device.");
return false;
}
if (!isServiceAvailable()) {
Slog.w(LOG_TAG, "Service is not available at this moment.");
return false;
}
// don't allow proximity request in screen off state.
// This behavior might change in the future.
if (!mPowerManager.isInteractive()) {
Slog.w(LOG_TAG, "Proximity Service is unavailable during screen off at this moment.");
return false;
}
synchronized (mLock) {
// schedule shutting down the connection if no one resets this timer
freeIfInactiveLocked();
// lazily start the service, which should be very lightweight to start
bindLocked();
/*
Prevent spamming with multiple requests, only one at a time is allowed.
If there are use-cases for keeping track of multiple requests, we
can refactor ProximityUpdate object to keep track of multiple internal callbacks.
*/
if (mCurrentProximityUpdate != null && mCurrentProximityUpdate.mStartedUpdates) {
if (mCurrentProximityUpdate.mCallbackInternal == callbackInternal) {
Slog.w(LOG_TAG, "Provided callback is already registered. Skipping.");
return true;
} else {
// reject the new request since the old request is still alive.
Slog.w(LOG_TAG, "New proximity update cannot be processed because there is "
+ "already an ongoing update");
return false;
}
}
mCurrentProximityUpdate = new ProximityUpdate(callbackInternal);
return mCurrentProximityUpdate.startUpdates();
}
}
/** Cancels the specified proximity registration. */
@VisibleForTesting
void onStopProximityUpdates(ProximityCallbackInternal callbackInternal) {
synchronized (mLock) {
if (mCurrentProximityUpdate == null
|| !mCurrentProximityUpdate.mCallbackInternal.equals(callbackInternal)
|| !mCurrentProximityUpdate.mStartedUpdates) {
Slog.w(LOG_TAG, "Cannot stop a non-current callback");
return;
}
mCurrentProximityUpdate.cancelUpdates();
mCurrentProximityUpdate = null;
}
}
@GuardedBy("mLock")
@VisibleForTesting
protected void freeIfInactiveLocked() {
@@ -390,15 +473,18 @@ public class AttentionManagerService extends SystemService {
ipw.println("Class=" + mComponentName.getClassName());
ipw.decreaseIndent();
}
ipw.println("binding=" + mBinding);
ipw.println("current attention check:");
synchronized (mLock) {
ipw.println("binding=" + mBinding);
ipw.println("current attention check:");
if (mCurrentAttentionCheck != null) {
mCurrentAttentionCheck.dump(ipw);
}
if (mAttentionCheckCacheBuffer != null) {
mAttentionCheckCacheBuffer.dump(ipw);
}
if (mCurrentProximityUpdate != null) {
mCurrentProximityUpdate.dump(ipw);
}
}
}
@@ -417,6 +503,17 @@ public class AttentionManagerService extends SystemService {
public void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) {
AttentionManagerService.this.cancelAttentionCheck(callbackInternal);
}
@Override
public boolean onStartProximityUpdates(
ProximityCallbackInternal callback) {
return AttentionManagerService.this.onStartProximityUpdates(callback);
}
@Override
public void onStopProximityUpdates(ProximityCallbackInternal callback) {
AttentionManagerService.this.onStopProximityUpdates(callback);
}
}
@VisibleForTesting
@@ -536,6 +633,71 @@ public class AttentionManagerService extends SystemService {
}
}
@VisibleForTesting
final class ProximityUpdate {
private final ProximityCallbackInternal mCallbackInternal;
private final IProximityCallback mIProximityCallback;
private boolean mStartedUpdates;
ProximityUpdate(ProximityCallbackInternal callbackInternal) {
mCallbackInternal = callbackInternal;
mIProximityCallback = new IProximityCallback.Stub() {
@Override
public void onProximityUpdate(double distance) {
synchronized (mLock) {
mCallbackInternal.onProximityUpdate(distance);
freeIfInactiveLocked();
}
}
};
}
boolean startUpdates() {
synchronized (mLock) {
if (mStartedUpdates) {
Slog.w(LOG_TAG, "Already registered to a proximity service.");
return false;
}
if (mService == null) {
Slog.w(LOG_TAG,
"There is no service bound. Proximity update request rejected.");
return false;
}
try {
mService.onStartProximityUpdates(mIProximityCallback);
mStartedUpdates = true;
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
return false;
}
}
return true;
}
void cancelUpdates() {
synchronized (mLock) {
if (mStartedUpdates) {
if (mService == null) {
mStartedUpdates = false;
return;
}
try {
mService.onStopProximityUpdates();
mStartedUpdates = false;
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
}
}
}
}
void dump(IndentingPrintWriter ipw) {
ipw.increaseIndent();
ipw.println("is StartedUpdates=" + mStartedUpdates);
ipw.decreaseIndent();
}
}
private void appendResultToAttentionCacheBuffer(AttentionCheckCache cache) {
synchronized (mLock) {
if (mAttentionCheckCacheBuffer == null) {
@@ -593,6 +755,18 @@ public class AttentionManagerService extends SystemService {
mCurrentAttentionCheck.mCallbackInternal.onFailure(ATTENTION_FAILURE_UNKNOWN);
}
}
if (mCurrentProximityUpdate != null && mCurrentProximityUpdate.mStartedUpdates) {
if (mService != null) {
try {
mService.onStartProximityUpdates(mCurrentProximityUpdate.mIProximityCallback);
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
}
} else {
mCurrentProximityUpdate.cancelUpdates();
mCurrentProximityUpdate = null;
}
}
}
@VisibleForTesting
@@ -609,7 +783,9 @@ public class AttentionManagerService extends SystemService {
switch (msg.what) {
// Do not occupy resources when not in use - unbind proactively.
case CHECK_CONNECTION_EXPIRATION: {
cancelAndUnbindLocked();
synchronized (mLock) {
cancelAndUnbindLocked();
}
}
break;
@@ -653,10 +829,15 @@ public class AttentionManagerService extends SystemService {
@GuardedBy("mLock")
private void cancelAndUnbindLocked() {
synchronized (mLock) {
if (mCurrentAttentionCheck == null) {
if (mCurrentAttentionCheck == null && mCurrentProximityUpdate == null) {
return;
}
cancel();
if (mCurrentAttentionCheck != null) {
cancel();
}
if (mCurrentProximityUpdate != null) {
mCurrentProximityUpdate.cancelUpdates();
}
if (mService == null) {
return;
}
@@ -702,7 +883,9 @@ public class AttentionManagerService extends SystemService {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
cancelAndUnbindLocked();
synchronized (mLock) {
cancelAndUnbindLocked();
}
}
}
}
@@ -730,8 +913,27 @@ public class AttentionManagerService extends SystemService {
}
}
class TestableProximityCallbackInternal extends ProximityCallbackInternal {
private double mLastCallbackCode = PROXIMITY_UNKNOWN;
@Override
public void onProximityUpdate(double distance) {
mLastCallbackCode = distance;
}
public void reset() {
mLastCallbackCode = PROXIMITY_UNKNOWN;
}
public double getLastCallbackCode() {
return mLastCallbackCode;
}
}
final TestableAttentionCallbackInternal mTestableAttentionCallback =
new TestableAttentionCallbackInternal();
final TestableProximityCallbackInternal mTestableProximityCallback =
new TestableProximityCallbackInternal();
@Override
public int onCommand(@Nullable final String cmd) {
@@ -749,6 +951,10 @@ public class AttentionManagerService extends SystemService {
return cmdCallCheckAttention();
case "cancelCheckAttention":
return cmdCallCancelAttention();
case "onStartProximityUpdates":
return cmdCallOnStartProximityUpdates();
case "onStopProximityUpdates":
return cmdCallOnStopProximityUpdates();
default:
throw new IllegalArgumentException("Invalid argument");
}
@@ -758,6 +964,8 @@ public class AttentionManagerService extends SystemService {
return cmdClearTestableAttentionService();
case "getLastTestCallbackCode":
return cmdGetLastTestCallbackCode();
case "getLastTestProximityCallbackCode":
return cmdGetLastTestProximityCallbackCode();
default:
return handleDefaultCommands(cmd);
}
@@ -782,6 +990,7 @@ public class AttentionManagerService extends SystemService {
private int cmdClearTestableAttentionService() {
sTestAttentionServicePackage = "";
mTestableAttentionCallback.reset();
mTestableProximityCallback.reset();
resetStates();
return 0;
}
@@ -800,6 +1009,20 @@ public class AttentionManagerService extends SystemService {
return 0;
}
private int cmdCallOnStartProximityUpdates() {
final PrintWriter out = getOutPrintWriter();
boolean calledSuccessfully = onStartProximityUpdates(mTestableProximityCallback);
out.println(calledSuccessfully ? "true" : "false");
return 0;
}
private int cmdCallOnStopProximityUpdates() {
final PrintWriter out = getOutPrintWriter();
onStopProximityUpdates(mTestableProximityCallback);
out.println("true");
return 0;
}
private int cmdResolveAttentionServiceComponent() {
final PrintWriter out = getOutPrintWriter();
ComponentName resolvedComponent = resolveAttentionService(mContext);
@@ -813,7 +1036,16 @@ public class AttentionManagerService extends SystemService {
return 0;
}
private int cmdGetLastTestProximityCallbackCode() {
final PrintWriter out = getOutPrintWriter();
out.println(mTestableProximityCallback.getLastCallbackCode());
return 0;
}
private void resetStates() {
synchronized (mLock) {
mCurrentProximityUpdate = null;
}
mComponentName = resolveAttentionService(mContext);
}
@@ -844,11 +1076,24 @@ public class AttentionManagerService extends SystemService {
+ " (to see the result, call getLastTestCallbackCode)");
out.println(" := false, otherwise");
out.println(" call cancelCheckAttention: Cancels check attention");
out.println(" call onStartProximityUpdates: Calls onStartProximityUpdates");
out.println(" ---returns:");
out.println(
" := true, if the request was successfully dispatched to the service "
+ "implementation."
+ " (to see the result, call getLastTestProximityCallbackCode)");
out.println(" := false, otherwise");
out.println(" call onStopProximityUpdates: Cancels proximity updates");
out.println(" getLastTestCallbackCode");
out.println(" ---returns:");
out.println(
" := An integer, representing the last callback code received from the "
+ "bounded implementation. If none, it will return -1");
out.println(" getLastTestProximityCallbackCode");
out.println(" ---returns:");
out.println(
" := A double, representing the last proximity value received from the "
+ "bounded implementation. If none, it will return -1.0");
}
}

View File

@@ -24,14 +24,20 @@ import static com.android.server.attention.AttentionManagerService.KEY_STALE_AFT
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
import android.attention.AttentionManagerInternal.ProximityCallbackInternal;
import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
@@ -42,6 +48,7 @@ import android.os.RemoteException;
import android.provider.DeviceConfig;
import android.service.attention.IAttentionCallback;
import android.service.attention.IAttentionService;
import android.service.attention.IProximityCallback;
import androidx.test.filters.SmallTest;
@@ -49,6 +56,7 @@ import com.android.server.attention.AttentionManagerService.AttentionCheck;
import com.android.server.attention.AttentionManagerService.AttentionCheckCache;
import com.android.server.attention.AttentionManagerService.AttentionCheckCacheBuffer;
import com.android.server.attention.AttentionManagerService.AttentionHandler;
import com.android.server.attention.AttentionManagerService.ProximityUpdate;
import org.junit.Before;
import org.junit.Test;
@@ -59,10 +67,13 @@ import org.mockito.MockitoAnnotations;
/**
* Tests for {@link com.android.server.attention.AttentionManagerService}
*/
@SuppressWarnings("GuardedBy")
@SmallTest
public class AttentionManagerServiceTest {
private static final double PROXIMITY_SUCCESS_STATE = 1.0;
private AttentionManagerService mSpyAttentionManager;
private final int mTimeout = 1000;
private final Object mLock = new Object();
@Mock
private AttentionCallbackInternal mMockAttentionCallbackInternal;
@Mock
@@ -73,6 +84,8 @@ public class AttentionManagerServiceTest {
private IThermalService mMockIThermalService;
@Mock
Context mContext;
@Mock
private ProximityCallbackInternal mMockProximityCallbackInternal;
@Before
public void setUp() throws RemoteException {
@@ -84,7 +97,6 @@ public class AttentionManagerServiceTest {
doReturn(true).when(mMockIPowerManager).isInteractive();
mPowerManager = new PowerManager(mContext, mMockIPowerManager, mMockIThermalService, null);
Object mLock = new Object();
// setup a spy on attention manager
AttentionManagerService attentionManager = new AttentionManagerService(
mContext,
@@ -100,6 +112,119 @@ public class AttentionManagerServiceTest {
mSpyAttentionManager);
mSpyAttentionManager.mCurrentAttentionCheck = attentionCheck;
mSpyAttentionManager.mService = new MockIAttentionService();
doNothing().when(mSpyAttentionManager).freeIfInactiveLocked();
}
@Test
public void testRegisterProximityUpdates_returnFalseWhenServiceDisabled() {
mSpyAttentionManager.mIsServiceEnabled = false;
assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
.isFalse();
}
@Test
public void testRegisterProximityUpdates_returnFalseWhenServiceUnavailable() {
mSpyAttentionManager.mIsServiceEnabled = true;
doReturn(false).when(mSpyAttentionManager).isServiceAvailable();
assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
.isFalse();
}
@Test
public void testRegisterProximityUpdates_returnFalseWhenPowerManagerNotInteract()
throws RemoteException {
mSpyAttentionManager.mIsServiceEnabled = true;
doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
doReturn(false).when(mMockIPowerManager).isInteractive();
assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
.isFalse();
}
@Test
public void testRegisterProximityUpdates_callOnSuccess() throws RemoteException {
mSpyAttentionManager.mIsServiceEnabled = true;
doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
doReturn(true).when(mMockIPowerManager).isInteractive();
assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
.isTrue();
verify(mMockProximityCallbackInternal, times(1))
.onProximityUpdate(PROXIMITY_SUCCESS_STATE);
}
@Test
public void testRegisterProximityUpdates_callOnSuccessTwiceInARow() throws RemoteException {
mSpyAttentionManager.mIsServiceEnabled = true;
doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
doReturn(true).when(mMockIPowerManager).isInteractive();
assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
.isTrue();
ProximityUpdate prevProximityUpdate = mSpyAttentionManager.mCurrentProximityUpdate;
assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
.isTrue();
assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isEqualTo(prevProximityUpdate);
verify(mMockProximityCallbackInternal, times(1))
.onProximityUpdate(PROXIMITY_SUCCESS_STATE);
}
@Test
public void testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered() {
mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
verifyZeroInteractions(mMockProximityCallbackInternal);
}
@Test
public void testUnregisterProximityUpdates_noCrashWhenCallbackMismatched()
throws RemoteException {
mSpyAttentionManager.mIsServiceEnabled = true;
doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
doReturn(true).when(mMockIPowerManager).isInteractive();
mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
verify(mMockProximityCallbackInternal, times(1))
.onProximityUpdate(PROXIMITY_SUCCESS_STATE);
ProximityCallbackInternal mismatchedCallback = new ProximityCallbackInternal() {
@Override
public void onProximityUpdate(double distance) {
fail("Callback shouldn't have responded.");
}
};
mSpyAttentionManager.onStopProximityUpdates(mismatchedCallback);
verifyNoMoreInteractions(mMockProximityCallbackInternal);
}
@Test
public void testUnregisterProximityUpdates_cancelRegistrationWhenMatched()
throws RemoteException {
mSpyAttentionManager.mIsServiceEnabled = true;
doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
doReturn(true).when(mMockIPowerManager).isInteractive();
mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isNull();
}
@Test
public void testUnregisterProximityUpdates_noCrashWhenTwiceInARow() throws RemoteException {
// Attention Service registers proximity updates.
mSpyAttentionManager.mIsServiceEnabled = true;
doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
doReturn(true).when(mMockIPowerManager).isInteractive();
mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
verify(mMockProximityCallbackInternal, times(1))
.onProximityUpdate(PROXIMITY_SUCCESS_STATE);
// Attention Service unregisters the proximity update twice in a row.
mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
verifyNoMoreInteractions(mMockProximityCallbackInternal);
}
@Test
@@ -127,7 +252,6 @@ public class AttentionManagerServiceTest {
mSpyAttentionManager.mIsServiceEnabled = true;
doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
doReturn(true).when(mMockIPowerManager).isInteractive();
doNothing().when(mSpyAttentionManager).freeIfInactiveLocked();
mSpyAttentionManager.mCurrentAttentionCheck = null;
AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class);
@@ -213,6 +337,13 @@ public class AttentionManagerServiceTest {
public void cancelAttentionCheck(IAttentionCallback callback) {
}
public void onStartProximityUpdates(IProximityCallback callback) throws RemoteException {
callback.onProximityUpdate(PROXIMITY_SUCCESS_STATE);
}
public void onStopProximityUpdates() throws RemoteException {
}
public IBinder asBinder() {
return null;
}