Make AppOpsUidStateTracker callbacks more testable
The unit tests will become easier to write when validating callbacks and when having delayed behaviors. This is done by 1: using an executor instead of a handler so that we only need to check if the execute mock is invoked and 2: a custom executor interface that supports delayed execution. Executors are preferred so that this interface will be more consistent with public api guidelines and also because post/send methods in Handler are final so it's impossible to decouple a subclass/mocked Handler's behavior from the current Looper. Finally the test clock implementation takes the custom executor in the constructor so it can tell the executor when time changes so it can execute delayed runnables. Test: AppOpsUidStateTrackerTest Bug: 247111208 Change-Id: I930e7e56630408440f6ca03b4c2ab84b19b20eb9
This commit is contained in:
@@ -110,6 +110,7 @@ import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.IBinder;
|
||||
import android.os.PackageTagsList;
|
||||
import android.os.Process;
|
||||
@@ -374,10 +375,17 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
|
||||
public AppOpsUidStateTracker getUidStateTracker() {
|
||||
if (mUidStateTracker == null) {
|
||||
mUidStateTracker = new AppOpsUidStateTrackerImpl(
|
||||
LocalServices.getService(ActivityManagerInternal.class), mHandler,
|
||||
LocalServices.getService(ActivityManagerInternal.class),
|
||||
mHandler,
|
||||
r -> {
|
||||
synchronized (AppOpsService.this) {
|
||||
r.run();
|
||||
}
|
||||
},
|
||||
Clock.SYSTEM_CLOCK, mConstants);
|
||||
|
||||
mUidStateTracker.addUidStateChangedCallback(mHandler, this::onUidStateChanged);
|
||||
mUidStateTracker.addUidStateChangedCallback(new HandlerExecutor(mHandler),
|
||||
this::onUidStateChanged);
|
||||
}
|
||||
return mUidStateTracker;
|
||||
}
|
||||
@@ -5596,6 +5604,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
|
||||
pw.println(" Only output the watcher sections.");
|
||||
pw.println(" --history");
|
||||
pw.println(" Only output history.");
|
||||
pw.println(" --uid-state-change-logs");
|
||||
pw.println(" Include logs about uid state changes.");
|
||||
}
|
||||
|
||||
private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterAttributionTag,
|
||||
@@ -5732,6 +5742,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
|
||||
// TODO ntmyren: Remove the dumpHistory and dumpFilter
|
||||
boolean dumpHistory = false;
|
||||
boolean includeDiscreteOps = false;
|
||||
boolean dumpUidStateChangeLogs = false;
|
||||
int nDiscreteOps = 10;
|
||||
@HistoricalOpsRequestFilter int dumpFilter = 0;
|
||||
boolean dumpAll = false;
|
||||
@@ -5814,6 +5825,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
|
||||
} else if (arg.length() > 0 && arg.charAt(0) == '-') {
|
||||
pw.println("Unknown option: " + arg);
|
||||
return;
|
||||
} else if ("--uid-state-change-logs".equals(arg)) {
|
||||
dumpUidStateChangeLogs = true;
|
||||
} else {
|
||||
pw.println("Unknown command: " + arg);
|
||||
return;
|
||||
@@ -6265,6 +6278,12 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
|
||||
pw.println(" AppOps policy not set.");
|
||||
}
|
||||
}
|
||||
|
||||
if (dumpAll || dumpUidStateChangeLogs) {
|
||||
pw.println();
|
||||
pw.println("Uid State Changes Event Log:");
|
||||
getUidStateTracker().dumpEvents(pw);
|
||||
}
|
||||
}
|
||||
|
||||
// Must not hold the appops lock
|
||||
@@ -6277,14 +6296,6 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
|
||||
mHistoricalRegistry.dumpDiscreteData(pw, dumpUid, dumpPackage, dumpAttributionTag,
|
||||
dumpFilter, dumpOp, sdf, date, " ", nDiscreteOps);
|
||||
}
|
||||
|
||||
if (dumpAll) {
|
||||
pw.println();
|
||||
pw.println("Uid State Changes Event Log:");
|
||||
if (mUidStateTracker != null) {
|
||||
mUidStateTracker.dumpEvents(pw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,10 +30,11 @@ import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
|
||||
import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
|
||||
import static android.app.AppOpsManager.UID_STATE_TOP;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
interface AppOpsUidStateTracker {
|
||||
|
||||
@@ -95,7 +96,7 @@ interface AppOpsUidStateTracker {
|
||||
/**
|
||||
* Listen to changes in {@link android.app.AppOpsManager.UidState}
|
||||
*/
|
||||
void addUidStateChangedCallback(Handler handler,
|
||||
void addUidStateChangedCallback(@CallbackExecutor Executor executor,
|
||||
UidStateChangedCallback callback);
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,17 +44,18 @@ import android.util.SparseIntArray;
|
||||
import android.util.SparseLongArray;
|
||||
import android.util.TimeUtils;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.os.Clock;
|
||||
import com.android.internal.util.function.pooled.PooledLambda;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
|
||||
private static final String LOG_TAG = AppOpsUidStateTrackerImpl.class.getSimpleName();
|
||||
|
||||
private final Handler mHandler;
|
||||
private final DelayableExecutor mExecutor;
|
||||
private final Clock mClock;
|
||||
private ActivityManagerInternal mActivityManagerInternal;
|
||||
private AppOpsService.Constants mConstants;
|
||||
@@ -68,18 +69,46 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
private SparseLongArray mPendingCommitTime = new SparseLongArray();
|
||||
private SparseBooleanArray mPendingGone = new SparseBooleanArray();
|
||||
|
||||
private ArrayMap<UidStateChangedCallback, Handler> mUidStateChangedCallbacks = new ArrayMap<>();
|
||||
private ArrayMap<UidStateChangedCallback, Executor>
|
||||
mUidStateChangedCallbacks = new ArrayMap<>();
|
||||
|
||||
private final EventLog mEventLog;
|
||||
|
||||
@VisibleForTesting
|
||||
interface DelayableExecutor extends Executor {
|
||||
|
||||
void execute(Runnable runnable);
|
||||
|
||||
void executeDelayed(Runnable runnable, long delay);
|
||||
}
|
||||
|
||||
AppOpsUidStateTrackerImpl(ActivityManagerInternal activityManagerInternal,
|
||||
Handler handler, Clock clock, AppOpsService.Constants constants) {
|
||||
Handler handler, Executor lockingExecutor, Clock clock,
|
||||
AppOpsService.Constants constants) {
|
||||
|
||||
this(activityManagerInternal, new DelayableExecutor() {
|
||||
@Override
|
||||
public void execute(Runnable runnable) {
|
||||
handler.post(() -> lockingExecutor.execute(runnable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeDelayed(Runnable runnable, long delay) {
|
||||
handler.postDelayed(() -> lockingExecutor.execute(runnable), delay);
|
||||
}
|
||||
}, clock, constants, handler.getLooper().getThread());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
AppOpsUidStateTrackerImpl(ActivityManagerInternal activityManagerInternal,
|
||||
DelayableExecutor executor, Clock clock, AppOpsService.Constants constants,
|
||||
Thread executorThread) {
|
||||
mActivityManagerInternal = activityManagerInternal;
|
||||
mHandler = handler;
|
||||
mExecutor = executor;
|
||||
mClock = clock;
|
||||
mConstants = constants;
|
||||
|
||||
mEventLog = new EventLog(handler);
|
||||
mEventLog = new EventLog(executor, executorThread);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -157,11 +186,12 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUidStateChangedCallback(Handler handler, UidStateChangedCallback callback) {
|
||||
public void addUidStateChangedCallback(Executor executor, UidStateChangedCallback callback) {
|
||||
if (mUidStateChangedCallbacks.containsKey(callback)) {
|
||||
throw new IllegalStateException("Callback is already registered.");
|
||||
}
|
||||
mUidStateChangedCallbacks.put(callback, handler);
|
||||
|
||||
mUidStateChangedCallbacks.put(callback, executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -232,7 +262,7 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
final long commitTime = mClock.elapsedRealtime() + settleTime;
|
||||
mPendingCommitTime.put(uid, commitTime);
|
||||
|
||||
mHandler.sendMessageDelayed(PooledLambda.obtainMessage(
|
||||
mExecutor.executeDelayed(PooledLambda.obtainRunnable(
|
||||
AppOpsUidStateTrackerImpl::updateUidPendingStateIfNeeded, this,
|
||||
uid), settleTime + 1);
|
||||
}
|
||||
@@ -323,10 +353,11 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
|
||||
for (int i = 0; i < mUidStateChangedCallbacks.size(); i++) {
|
||||
UidStateChangedCallback cb = mUidStateChangedCallbacks.keyAt(i);
|
||||
Handler h = mUidStateChangedCallbacks.valueAt(i);
|
||||
Executor executor = mUidStateChangedCallbacks.valueAt(i);
|
||||
|
||||
h.sendMessage(PooledLambda.obtainMessage(UidStateChangedCallback::onUidStateChanged,
|
||||
cb, uid, pendingUidState, foregroundChange));
|
||||
executor.execute(PooledLambda.obtainRunnable(
|
||||
UidStateChangedCallback::onUidStateChanged, cb, uid, pendingUidState,
|
||||
foregroundChange));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +397,8 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
// Memory usage: 24 * size bytes
|
||||
private static final int EVAL_FOREGROUND_MODE_MAX_SIZE = 200;
|
||||
|
||||
private final Handler mHandler;
|
||||
private final DelayableExecutor mExecutor;
|
||||
private final Thread mExecutorThread;
|
||||
|
||||
private int[][] mUpdateUidProcStateLog = new int[UPDATE_UID_PROC_STATE_LOG_MAX_SIZE][3];
|
||||
private long[] mUpdateUidProcStateLogTimestamps =
|
||||
@@ -384,15 +416,16 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
private int mEvalForegroundModeLogSize = 0;
|
||||
private int mEvalForegroundModeLogHead = 0;
|
||||
|
||||
EventLog(Handler handler) {
|
||||
mHandler = handler;
|
||||
EventLog(DelayableExecutor executor, Thread executorThread) {
|
||||
mExecutor = executor;
|
||||
mExecutorThread = executorThread;
|
||||
}
|
||||
|
||||
void logUpdateUidProcState(int uid, int procState, int capability) {
|
||||
if (UPDATE_UID_PROC_STATE_LOG_MAX_SIZE == 0) {
|
||||
return;
|
||||
}
|
||||
mHandler.sendMessage(PooledLambda.obtainMessage(EventLog::logUpdateUidProcStateAsync,
|
||||
mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logUpdateUidProcStateAsync,
|
||||
this, System.currentTimeMillis(), uid, procState, capability));
|
||||
}
|
||||
|
||||
@@ -416,7 +449,7 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
if (COMMIT_UID_STATE_LOG_MAX_SIZE == 0) {
|
||||
return;
|
||||
}
|
||||
mHandler.sendMessage(PooledLambda.obtainMessage(EventLog::logCommitUidStateAsync,
|
||||
mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logCommitUidStateAsync,
|
||||
this, System.currentTimeMillis(), uid, uidState, capability, visible));
|
||||
}
|
||||
|
||||
@@ -442,7 +475,7 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
if (EVAL_FOREGROUND_MODE_MAX_SIZE == 0) {
|
||||
return;
|
||||
}
|
||||
mHandler.sendMessage(PooledLambda.obtainMessage(EventLog::logEvalForegroundModeAsync,
|
||||
mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logEvalForegroundModeAsync,
|
||||
this, System.currentTimeMillis(), uid, uidState, capability, code, result));
|
||||
}
|
||||
|
||||
@@ -466,22 +499,6 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
}
|
||||
|
||||
void dumpEvents(PrintWriter pw) {
|
||||
if (Thread.currentThread() != mHandler.getLooper().getThread()) {
|
||||
// All operations are done on the handler's thread
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
mHandler.post(() -> {
|
||||
dumpEvents(pw);
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int updateIdx = 0;
|
||||
int commitIdx = 0;
|
||||
int evalIdx = 0;
|
||||
@@ -536,13 +553,13 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
pw.print(" UPDATE_UID_PROC_STATE");
|
||||
|
||||
pw.print(" uid=");
|
||||
pw.print(uid);
|
||||
pw.print(String.format("%-8d", uid));
|
||||
|
||||
pw.print(" procState=");
|
||||
pw.print(String.format("%-30s", ActivityManager.procStateToString(procState)));
|
||||
|
||||
pw.print(" capability=");
|
||||
pw.print(ActivityManager.getCapabilitiesSummary(capability));
|
||||
pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
|
||||
|
||||
pw.println();
|
||||
}
|
||||
@@ -559,13 +576,13 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
pw.print(" COMMIT_UID_STATE ");
|
||||
|
||||
pw.print(" uid=");
|
||||
pw.print(uid);
|
||||
pw.print(String.format("%-8d", uid));
|
||||
|
||||
pw.print(" uidState=");
|
||||
pw.print(String.format("%-30s", AppOpsManager.uidStateToString(uidState)));
|
||||
|
||||
pw.print(" capability=");
|
||||
pw.print(ActivityManager.getCapabilitiesSummary(capability));
|
||||
pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
|
||||
|
||||
pw.print(" visibleAppWidget=");
|
||||
pw.print(visibleAppWidget);
|
||||
@@ -586,13 +603,13 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
|
||||
pw.print(" EVAL_FOREGROUND_MODE ");
|
||||
|
||||
pw.print(" uid=");
|
||||
pw.print(uid);
|
||||
pw.print(String.format("%-8d", uid));
|
||||
|
||||
pw.print(" uidState=");
|
||||
pw.print(String.format("%-30s", AppOpsManager.uidStateToString(uidState)));
|
||||
|
||||
pw.print(" capability=");
|
||||
pw.print(ActivityManager.getCapabilitiesSummary(capability));
|
||||
pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
|
||||
|
||||
pw.print(" code=");
|
||||
pw.print(String.format("%-20s", AppOpsManager.opToName(code)));
|
||||
|
||||
@@ -34,12 +34,9 @@ import static android.app.AppOpsManager.UID_STATE_TOP;
|
||||
import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atLeast;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
@@ -49,25 +46,22 @@ import static org.mockito.Mockito.verify;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.AppOpsManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.dx.mockito.inline.extended.ExtendedMockito;
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
|
||||
import com.android.internal.os.Clock;
|
||||
import com.android.server.appop.AppOpsUidStateTracker.UidStateChangedCallback;
|
||||
import com.android.server.appop.AppOpsUidStateTrackerImpl.DelayableExecutor;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
public class AppOpsUidStateTrackerTest {
|
||||
|
||||
@@ -80,13 +74,12 @@ public class AppOpsUidStateTrackerTest {
|
||||
@Mock
|
||||
ActivityManagerInternal mAmi;
|
||||
|
||||
@Mock
|
||||
Handler mHandler;
|
||||
|
||||
@Mock
|
||||
AppOpsService.Constants mConstants;
|
||||
|
||||
AppOpsUidStateTrackerTestClock mClock = new AppOpsUidStateTrackerTestClock();
|
||||
AppOpsUidStateTrackerTestExecutor mExecutor = new AppOpsUidStateTrackerTestExecutor();
|
||||
|
||||
AppOpsUidStateTrackerTestClock mClock = new AppOpsUidStateTrackerTestClock(mExecutor);
|
||||
|
||||
AppOpsUidStateTracker mIntf;
|
||||
|
||||
@@ -101,7 +94,8 @@ public class AppOpsUidStateTrackerTest {
|
||||
mConstants.TOP_STATE_SETTLE_TIME = 10 * 1000L;
|
||||
mConstants.FG_SERVICE_STATE_SETTLE_TIME = 5 * 1000L;
|
||||
mConstants.BG_STATE_SETTLE_TIME = 1 * 1000L;
|
||||
mIntf = new AppOpsUidStateTrackerImpl(mAmi, mHandler, mClock, mConstants);
|
||||
mIntf = new AppOpsUidStateTrackerImpl(mAmi, mExecutor, mClock, mConstants,
|
||||
Thread.currentThread());
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -263,18 +257,10 @@ public class AppOpsUidStateTrackerTest {
|
||||
// Still in foreground due to settle time
|
||||
assertForeground(UID);
|
||||
|
||||
AtomicReference<Message> messageAtomicReference = new AtomicReference<>();
|
||||
AtomicLong delayAtomicReference = new AtomicLong();
|
||||
mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME - 1);
|
||||
assertForeground(UID);
|
||||
|
||||
getPostDelayedMessageArguments(messageAtomicReference, delayAtomicReference);
|
||||
Message message = messageAtomicReference.get();
|
||||
long delay = delayAtomicReference.get();
|
||||
|
||||
assertNotNull(message);
|
||||
assertEquals(mConstants.TOP_STATE_SETTLE_TIME + 1, delay);
|
||||
|
||||
mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME + 1);
|
||||
message.getCallback().run();
|
||||
mClock.advanceTime(1);
|
||||
assertBackground(UID);
|
||||
}
|
||||
|
||||
@@ -291,18 +277,10 @@ public class AppOpsUidStateTrackerTest {
|
||||
// Still in foreground due to settle time
|
||||
assertForeground(UID);
|
||||
|
||||
AtomicReference<Message> messageAtomicReference = new AtomicReference<>();
|
||||
AtomicLong delayAtomicReference = new AtomicLong();
|
||||
mClock.advanceTime(mConstants.FG_SERVICE_STATE_SETTLE_TIME - 1);
|
||||
assertForeground(UID);
|
||||
|
||||
getPostDelayedMessageArguments(messageAtomicReference, delayAtomicReference);
|
||||
Message message = messageAtomicReference.get();
|
||||
long delay = delayAtomicReference.get();
|
||||
|
||||
assertNotNull(message);
|
||||
assertEquals(mConstants.FG_SERVICE_STATE_SETTLE_TIME + 1, delay);
|
||||
|
||||
mClock.advanceTime(mConstants.FG_SERVICE_STATE_SETTLE_TIME + 1);
|
||||
message.getCallback().run();
|
||||
mClock.advanceTime(1);
|
||||
assertBackground(UID);
|
||||
}
|
||||
|
||||
@@ -319,14 +297,8 @@ public class AppOpsUidStateTrackerTest {
|
||||
// Still in foreground due to settle time
|
||||
assertForeground(UID);
|
||||
|
||||
AtomicReference<Message> messageAtomicReference = new AtomicReference<>();
|
||||
|
||||
getPostDelayedMessageArguments(messageAtomicReference, null);
|
||||
Message message = messageAtomicReference.get();
|
||||
|
||||
// 1 ms short of settle time
|
||||
mClock.advanceTime(mConstants.FG_SERVICE_STATE_SETTLE_TIME - 1);
|
||||
message.getCallback().run();
|
||||
assertForeground(UID);
|
||||
}
|
||||
|
||||
@@ -471,8 +443,6 @@ public class AppOpsUidStateTrackerTest {
|
||||
.topState()
|
||||
.update();
|
||||
|
||||
getLatestPostMessageArgument().getCallback().run();
|
||||
|
||||
verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_TOP), eq(true));
|
||||
}
|
||||
|
||||
@@ -484,8 +454,6 @@ public class AppOpsUidStateTrackerTest {
|
||||
.foregroundServiceState()
|
||||
.update();
|
||||
|
||||
getLatestPostMessageArgument().getCallback().run();
|
||||
|
||||
verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_FOREGROUND_SERVICE), eq(true));
|
||||
}
|
||||
|
||||
@@ -497,8 +465,6 @@ public class AppOpsUidStateTrackerTest {
|
||||
.foregroundState()
|
||||
.update();
|
||||
|
||||
getLatestPostMessageArgument().getCallback().run();
|
||||
|
||||
verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_FOREGROUND), eq(true));
|
||||
}
|
||||
|
||||
@@ -510,8 +476,6 @@ public class AppOpsUidStateTrackerTest {
|
||||
.backgroundState()
|
||||
.update();
|
||||
|
||||
getLatestPostMessageArgument().getCallback().run();
|
||||
|
||||
verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_BACKGROUND), eq(false));
|
||||
}
|
||||
|
||||
@@ -679,7 +643,6 @@ public class AppOpsUidStateTrackerTest {
|
||||
.nonExistentState()
|
||||
.update();
|
||||
|
||||
verify(mHandler, never()).post(any());
|
||||
verify(cb, never()).onUidStateChanged(anyInt(), anyInt(), anyBoolean());
|
||||
}
|
||||
|
||||
@@ -695,7 +658,6 @@ public class AppOpsUidStateTrackerTest {
|
||||
.nonExistentState()
|
||||
.update();
|
||||
|
||||
getLatestPostMessageArgument().getCallback().run();
|
||||
verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(false));
|
||||
}
|
||||
|
||||
@@ -711,7 +673,6 @@ public class AppOpsUidStateTrackerTest {
|
||||
.nonExistentState()
|
||||
.update();
|
||||
|
||||
getLatestPostMessageArgument().getCallback().run();
|
||||
verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
|
||||
}
|
||||
|
||||
@@ -727,7 +688,6 @@ public class AppOpsUidStateTrackerTest {
|
||||
.nonExistentState()
|
||||
.update();
|
||||
|
||||
getLatestPostMessageArgument().getCallback().run();
|
||||
verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
|
||||
}
|
||||
|
||||
@@ -743,10 +703,32 @@ public class AppOpsUidStateTrackerTest {
|
||||
.nonExistentState()
|
||||
.update();
|
||||
|
||||
getLatestPostMessageArgument().getCallback().run();
|
||||
verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUidStateChangedBackgroundThenForegroundImmediately() {
|
||||
procStateBuilder(UID)
|
||||
.topState()
|
||||
.update();
|
||||
|
||||
UidStateChangedCallback cb = addUidStateChangeCallback();
|
||||
|
||||
procStateBuilder(UID)
|
||||
.backgroundState()
|
||||
.update();
|
||||
|
||||
mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME - 1);
|
||||
|
||||
procStateBuilder(UID)
|
||||
.topState()
|
||||
.update();
|
||||
|
||||
mClock.advanceTime(1);
|
||||
|
||||
verify(cb, never()).onUidStateChanged(anyInt(), anyInt(), anyBoolean());
|
||||
}
|
||||
|
||||
public void testUidStateChangedCallback(int initialState, int finalState) {
|
||||
int initialUidState = processStateToUidState(initialState);
|
||||
int finalUidState = processStateToUidState(finalState);
|
||||
@@ -767,13 +749,9 @@ public class AppOpsUidStateTrackerTest {
|
||||
.update();
|
||||
|
||||
if (finalUidStateIsBackgroundAndLessImportant) {
|
||||
AtomicReference<Message> delayedMessage = new AtomicReference<>();
|
||||
getPostDelayedMessageArguments(delayedMessage, new AtomicLong());
|
||||
mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME + 1);
|
||||
delayedMessage.get().getCallback().run();
|
||||
}
|
||||
|
||||
getLatestPostMessageArgument().getCallback().run();
|
||||
verify(cb, atLeastOnce())
|
||||
.onUidStateChanged(eq(UID), eq(finalUidState), eq(foregroundChange));
|
||||
}
|
||||
@@ -781,7 +759,7 @@ public class AppOpsUidStateTrackerTest {
|
||||
private UidStateChangedCallback addUidStateChangeCallback() {
|
||||
UidStateChangedCallback cb =
|
||||
Mockito.mock(UidStateChangedCallback.class);
|
||||
mIntf.addUidStateChangedCallback(mHandler, cb);
|
||||
mIntf.addUidStateChangedCallback(r -> r.run(), cb);
|
||||
return cb;
|
||||
}
|
||||
|
||||
@@ -795,30 +773,6 @@ public class AppOpsUidStateTrackerTest {
|
||||
assertEquals(MODE_IGNORED, mIntf.evalMode(uid, OP_NO_CAPABILITIES, MODE_FOREGROUND));
|
||||
}
|
||||
|
||||
private void getPostDelayedMessageArguments(AtomicReference<Message> message,
|
||||
AtomicLong delay) {
|
||||
|
||||
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
|
||||
ArgumentCaptor<Long> delayCaptor = ArgumentCaptor.forClass(Long.class);
|
||||
|
||||
verify(mHandler).sendMessageDelayed(messageCaptor.capture(), delayCaptor.capture());
|
||||
|
||||
if (message != null) {
|
||||
message.set(messageCaptor.getValue());
|
||||
}
|
||||
if (delay != null) {
|
||||
delay.set(delayCaptor.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private Message getLatestPostMessageArgument() {
|
||||
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
|
||||
|
||||
verify(mHandler, atLeast(1)).sendMessage(messageCaptor.capture());
|
||||
|
||||
return messageCaptor.getValue();
|
||||
}
|
||||
|
||||
private UidProcStateUpdateBuilder procStateBuilder(int uid) {
|
||||
return new UidProcStateUpdateBuilder(mIntf, uid);
|
||||
}
|
||||
@@ -896,8 +850,14 @@ public class AppOpsUidStateTrackerTest {
|
||||
|
||||
private static class AppOpsUidStateTrackerTestClock extends Clock {
|
||||
|
||||
private AppOpsUidStateTrackerTestExecutor mExecutor;
|
||||
long mElapsedRealTime = 0x5f3759df;
|
||||
|
||||
AppOpsUidStateTrackerTestClock(AppOpsUidStateTrackerTestExecutor executor) {
|
||||
mExecutor = executor;
|
||||
executor.setUptime(mElapsedRealTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long elapsedRealtime() {
|
||||
return mElapsedRealTime;
|
||||
@@ -905,6 +865,53 @@ public class AppOpsUidStateTrackerTest {
|
||||
|
||||
void advanceTime(long time) {
|
||||
mElapsedRealTime += time;
|
||||
mExecutor.setUptime(mElapsedRealTime); // assume uptime == elapsedtime
|
||||
}
|
||||
}
|
||||
|
||||
private static class AppOpsUidStateTrackerTestExecutor implements DelayableExecutor {
|
||||
|
||||
private static class QueueElement implements Comparable<QueueElement> {
|
||||
|
||||
private long mExecutionTime;
|
||||
private Runnable mRunnable;
|
||||
|
||||
private QueueElement(long executionTime, Runnable runnable) {
|
||||
mExecutionTime = executionTime;
|
||||
mRunnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(QueueElement queueElement) {
|
||||
return Long.compare(mExecutionTime, queueElement.mExecutionTime);
|
||||
}
|
||||
}
|
||||
|
||||
private long mUptime = 0;
|
||||
|
||||
private PriorityQueue<QueueElement> mDelayedMessages = new PriorityQueue();
|
||||
|
||||
@Override
|
||||
public void execute(Runnable runnable) {
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeDelayed(Runnable runnable, long delay) {
|
||||
if (delay <= 0) {
|
||||
execute(runnable);
|
||||
}
|
||||
|
||||
mDelayedMessages.add(new QueueElement(mUptime + delay, runnable));
|
||||
}
|
||||
|
||||
private void setUptime(long uptime) {
|
||||
while (!mDelayedMessages.isEmpty()
|
||||
&& mDelayedMessages.peek().mExecutionTime <= uptime) {
|
||||
mDelayedMessages.poll().mRunnable.run();
|
||||
}
|
||||
|
||||
mUptime = uptime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user