Merge "Create a UsageEventListener."

This commit is contained in:
TreeHugger Robot
2020-11-23 21:22:34 +00:00
committed by Android (Google) Code Review
7 changed files with 248 additions and 12 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
}

View File

@@ -20,6 +20,7 @@ android_test {
static_libs: [
"services.core",
"services.net",
"services.usage",
"service-jobscheduler",
"service-permission.impl",
"service-blobstore",

View File

@@ -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;
}
}
}

View File

@@ -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) {

View File

@@ -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 {