Merge "Create a UsageEventListener."
This commit is contained in:
committed by
Android (Google) Code Review
commit
8d5f661df5
@@ -3,7 +3,6 @@ package com.android.server.usage;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.usage.AppStandbyInfo;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStatsManager.StandbyBuckets;
|
||||
import android.app.usage.UsageStatsManager.SystemForcedReasons;
|
||||
import android.content.Context;
|
||||
@@ -68,8 +67,6 @@ public interface AppStandbyInternal {
|
||||
*/
|
||||
void postOneTimeCheckIdleStates();
|
||||
|
||||
void reportEvent(UsageEvents.Event event, int userId);
|
||||
|
||||
void setLastJobRunTime(String packageName, int userId, long elapsedRealtime);
|
||||
|
||||
long getTimeSinceLastJobRun(String packageName, int userId);
|
||||
|
||||
@@ -62,6 +62,7 @@ import android.app.usage.AppStandbyInfo;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStatsManager.StandbyBuckets;
|
||||
import android.app.usage.UsageStatsManager.SystemForcedReasons;
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
@@ -128,9 +129,10 @@ import java.util.concurrent.CountDownLatch;
|
||||
* Manages the standby state of an app, listening to various events.
|
||||
*
|
||||
* Unit test:
|
||||
atest com.android.server.usage.AppStandbyControllerTests
|
||||
* atest com.android.server.usage.AppStandbyControllerTests
|
||||
*/
|
||||
public class AppStandbyController implements AppStandbyInternal {
|
||||
public class AppStandbyController
|
||||
implements AppStandbyInternal, UsageStatsManagerInternal.UsageEventListener {
|
||||
|
||||
private static final String TAG = "AppStandbyController";
|
||||
// Do not submit with true.
|
||||
@@ -468,10 +470,21 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
|
||||
@VisibleForTesting
|
||||
void setAppIdleEnabled(boolean enabled) {
|
||||
// Don't call out to USM with the lock held. Also, register the listener before we
|
||||
// change our internal state so no events fall through the cracks.
|
||||
final UsageStatsManagerInternal usmi =
|
||||
LocalServices.getService(UsageStatsManagerInternal.class);
|
||||
if (enabled) {
|
||||
usmi.registerListener(this);
|
||||
} else {
|
||||
usmi.unregisterListener(this);
|
||||
}
|
||||
|
||||
synchronized (mAppIdleLock) {
|
||||
if (mAppIdleEnabled != enabled) {
|
||||
final boolean oldParoleState = isInParole();
|
||||
mAppIdleEnabled = enabled;
|
||||
|
||||
if (isInParole() != oldParoleState) {
|
||||
postParoleStateChanged();
|
||||
}
|
||||
@@ -489,6 +502,11 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
mInjector.onBootPhase(phase);
|
||||
if (phase == PHASE_SYSTEM_SERVICES_READY) {
|
||||
Slog.d(TAG, "Setting app idle enabled state");
|
||||
|
||||
if (mAppIdleEnabled) {
|
||||
LocalServices.getService(UsageStatsManagerInternal.class).registerListener(this);
|
||||
}
|
||||
|
||||
// Observe changes to the threshold
|
||||
ConstantsObserver settingsObserver = new ConstantsObserver(mHandler);
|
||||
settingsObserver.start();
|
||||
@@ -912,8 +930,10 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportEvent(UsageEvents.Event event, int userId) {
|
||||
/**
|
||||
* Callback to inform listeners of a new event.
|
||||
*/
|
||||
public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) {
|
||||
if (!mAppIdleEnabled) return;
|
||||
final int eventType = event.getEventType();
|
||||
if ((eventType == UsageEvents.Event.ACTIVITY_RESUMED
|
||||
|
||||
@@ -326,4 +326,18 @@ public abstract class UsageStatsManagerInternal {
|
||||
* @return {@code true} if the updating was successful, {@code false} otherwise
|
||||
*/
|
||||
public abstract boolean updatePackageMappingsData();
|
||||
|
||||
/**
|
||||
* Listener interface for usage events.
|
||||
*/
|
||||
public interface UsageEventListener {
|
||||
/** Callback to inform listeners of a new usage event. */
|
||||
void onUsageEvent(@UserIdInt int userId, @NonNull UsageEvents.Event event);
|
||||
}
|
||||
|
||||
/** Register a listener that will be notified of every new usage event. */
|
||||
public abstract void registerListener(@NonNull UsageEventListener listener);
|
||||
|
||||
/** Unregister a listener from being notified of every new usage event. */
|
||||
public abstract void unregisterListener(@NonNull UsageEventListener listener);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ android_test {
|
||||
static_libs: [
|
||||
"services.core",
|
||||
"services.net",
|
||||
"services.usage",
|
||||
"service-jobscheduler",
|
||||
"service-permission.impl",
|
||||
"service-blobstore",
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.server.usage;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.content.Context;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoSession;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class UsageStatsServiceTest {
|
||||
private static final long TIMEOUT = 5000;
|
||||
|
||||
private UsageStatsService mService;
|
||||
|
||||
private MockitoSession mMockingSession;
|
||||
@Mock
|
||||
private Context mContext;
|
||||
|
||||
private static class TestInjector extends UsageStatsService.Injector {
|
||||
AppStandbyInternal getAppStandbyController(Context context) {
|
||||
return mock(AppStandbyInternal.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mMockingSession = mockitoSession()
|
||||
.initMocks(this)
|
||||
.strictness(Strictness.LENIENT)
|
||||
.startMocking();
|
||||
IActivityManager activityManager = ActivityManager.getService();
|
||||
spyOn(activityManager);
|
||||
try {
|
||||
doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any());
|
||||
} catch (RemoteException e) {
|
||||
fail("registerUidObserver threw exception: " + e.getMessage());
|
||||
}
|
||||
mService = new UsageStatsService(mContext, new TestInjector());
|
||||
spyOn(mService);
|
||||
doNothing().when(mService).publishBinderServices();
|
||||
mService.onStart();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsageEventListener() throws Exception {
|
||||
TestUsageEventListener listener = new TestUsageEventListener();
|
||||
UsageStatsManagerInternal usmi = LocalServices.getService(UsageStatsManagerInternal.class);
|
||||
usmi.registerListener(listener);
|
||||
|
||||
UsageEvents.Event event = new UsageEvents.Event(UsageEvents.Event.CONFIGURATION_CHANGE, 10);
|
||||
usmi.reportEvent("com.android.test", 10, event.getEventType());
|
||||
listener.setExpectation(10, event);
|
||||
listener.mCountDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
|
||||
usmi.unregisterListener(listener);
|
||||
listener.reset();
|
||||
|
||||
usmi.reportEvent("com.android.test", 0, UsageEvents.Event.CHOOSER_ACTION);
|
||||
Thread.sleep(TIMEOUT);
|
||||
assertNull(listener.mLastReceivedEvent);
|
||||
}
|
||||
|
||||
private static class TestUsageEventListener implements
|
||||
UsageStatsManagerInternal.UsageEventListener {
|
||||
UsageEvents.Event mLastReceivedEvent;
|
||||
int mLastReceivedUserId;
|
||||
UsageEvents.Event mExpectedEvent;
|
||||
int mExpectedUserId;
|
||||
CountDownLatch mCountDownLatch;
|
||||
|
||||
@Override
|
||||
public void onUsageEvent(int userId, UsageEvents.Event event) {
|
||||
mLastReceivedUserId = userId;
|
||||
mLastReceivedEvent = event;
|
||||
if (mCountDownLatch != null && userId == mExpectedUserId
|
||||
&& event.getEventType() == mExpectedEvent.getEventType()) {
|
||||
mCountDownLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
private void setExpectation(int userId, UsageEvents.Event event) {
|
||||
mExpectedUserId = userId;
|
||||
mExpectedEvent = event;
|
||||
mCountDownLatch = new CountDownLatch(1);
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
mLastReceivedUserId = mExpectedUserId = -1;
|
||||
mLastReceivedEvent = mExpectedEvent = null;
|
||||
mCountDownLatch = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,7 @@ import android.annotation.NonNull;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.usage.AppStandbyInfo;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
@@ -86,9 +87,11 @@ import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
@@ -425,11 +428,18 @@ public class AppStandbyControllerTests {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
LocalServices.addService(
|
||||
UsageStatsManagerInternal.class, mock(UsageStatsManagerInternal.class));
|
||||
MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
|
||||
mInjector = new MyInjector(myContext, Looper.getMainLooper());
|
||||
mController = setupController();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoundWidgetPackageExempt() throws Exception {
|
||||
assumeTrue(mInjector.getContext().getSystemService(AppWidgetManager.class) != null);
|
||||
@@ -562,7 +572,7 @@ public class AppStandbyControllerTests {
|
||||
UsageEvents.Event ev = new UsageEvents.Event();
|
||||
ev.mPackage = packageName;
|
||||
ev.mEventType = eventType;
|
||||
controller.reportEvent(ev, USER_ID);
|
||||
controller.onUsageEvent(USER_ID, ev);
|
||||
}
|
||||
|
||||
private int getStandbyBucket(AppStandbyController controller, String packageName) {
|
||||
|
||||
@@ -82,6 +82,7 @@ import android.util.SparseArray;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
import com.android.internal.util.CollectionUtils;
|
||||
@@ -90,7 +91,6 @@ import com.android.internal.util.FrameworkStatsLog;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.SystemService.TargetUser;
|
||||
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -178,6 +178,8 @@ public class UsageStatsService extends SystemService implements
|
||||
private final SparseArray<LinkedList<Event>> mReportedEvents = new SparseArray<>();
|
||||
final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray();
|
||||
final SparseArray<ActivityData> mVisibleActivities = new SparseArray();
|
||||
private final ArraySet<UsageStatsManagerInternal.UsageEventListener> mUsageEventListeners =
|
||||
new ArraySet<>();
|
||||
|
||||
private static class ActivityData {
|
||||
private final String mTaskRootPackage;
|
||||
@@ -202,8 +204,24 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
};
|
||||
|
||||
@VisibleForTesting
|
||||
static class Injector {
|
||||
AppStandbyInternal getAppStandbyController(Context context) {
|
||||
return AppStandbyInternal.newAppStandbyController(
|
||||
UsageStatsService.class.getClassLoader(), context);
|
||||
}
|
||||
}
|
||||
|
||||
private final Injector mInjector;
|
||||
|
||||
public UsageStatsService(Context context) {
|
||||
this(context, new Injector());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
UsageStatsService(Context context, Injector injector) {
|
||||
super(context);
|
||||
mInjector = injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -214,8 +232,7 @@ public class UsageStatsService extends SystemService implements
|
||||
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
|
||||
mHandler = new H(BackgroundThread.get().getLooper());
|
||||
|
||||
mAppStandby = AppStandbyInternal.newAppStandbyController(
|
||||
UsageStatsService.class.getClassLoader(), getContext());
|
||||
mAppStandby = mInjector.getAppStandbyController(getContext());
|
||||
|
||||
mAppTimeLimit = new AppTimeLimitController(
|
||||
new AppTimeLimitController.TimeLimitCallbackListener() {
|
||||
@@ -262,6 +279,11 @@ public class UsageStatsService extends SystemService implements
|
||||
|
||||
publishLocalService(UsageStatsManagerInternal.class, new LocalService());
|
||||
publishLocalService(AppStandbyInternal.class, mAppStandby);
|
||||
publishBinderServices();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void publishBinderServices() {
|
||||
publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
|
||||
}
|
||||
|
||||
@@ -928,7 +950,10 @@ public class UsageStatsService extends SystemService implements
|
||||
service.reportEvent(event);
|
||||
}
|
||||
|
||||
mAppStandby.reportEvent(event, userId);
|
||||
final int size = mUsageEventListeners.size();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
mUsageEventListeners.valueAt(i).onUsageEvent(userId, event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1151,6 +1176,25 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called via the local interface.
|
||||
*/
|
||||
private void registerListener(@NonNull UsageStatsManagerInternal.UsageEventListener listener) {
|
||||
synchronized (mLock) {
|
||||
mUsageEventListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called via the local interface.
|
||||
*/
|
||||
private void unregisterListener(
|
||||
@NonNull UsageStatsManagerInternal.UsageEventListener listener) {
|
||||
synchronized (mLock) {
|
||||
mUsageEventListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildFullToken(String packageName, String token) {
|
||||
final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1);
|
||||
sb.append(packageName);
|
||||
@@ -2317,6 +2361,22 @@ public class UsageStatsService extends SystemService implements
|
||||
public boolean updatePackageMappingsData() {
|
||||
return UsageStatsService.this.updatePackageMappingsData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener that will be notified of every new usage event.
|
||||
*/
|
||||
@Override
|
||||
public void registerListener(@NonNull UsageEventListener listener) {
|
||||
UsageStatsService.this.registerListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a listener from being notified of every new usage event.
|
||||
*/
|
||||
@Override
|
||||
public void unregisterListener(@NonNull UsageEventListener listener) {
|
||||
UsageStatsService.this.unregisterListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyPackageMonitor extends PackageMonitor {
|
||||
|
||||
Reference in New Issue
Block a user