diff options
| author | 2021-03-04 04:32:52 +0000 | |
|---|---|---|
| committer | 2021-03-04 04:32:52 +0000 | |
| commit | 2c293a43a313e3c73aeb6457cad434f0928da7c4 (patch) | |
| tree | 49e4c691eab42ede0b892019a915ba192ee978ef | |
| parent | 073e3dddea66908404c51c2d8f7a75ae87c82cf7 (diff) | |
| parent | e022510fc15aa276b269348143273381592ef890 (diff) | |
Merge changes I0bd9becd,I1f2967dc into sc-dev
* changes:
Add new component usage event (2/2)
Add new component usage event (1/2)
11 files changed, 205 insertions, 13 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 2a23d60d8af6..c9a184358925 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -273,10 +273,12 @@ public final class JobServiceContext implements ServiceConnection { // another binding flag for that. bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_ALMOST_PERCEPTIBLE - | Context.BIND_ALLOW_NETWORK_ACCESS; + | Context.BIND_ALLOW_NETWORK_ACCESS + | Context.BIND_NOT_APP_COMPONENT_USAGE; } else { bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND - | Context.BIND_NOT_PERCEPTIBLE; + | Context.BIND_NOT_PERCEPTIBLE + | Context.BIND_NOT_APP_COMPONENT_USAGE; } binding = mContext.bindServiceAsUser(intent, this, bindFlags, UserHandle.of(job.getUserId())); diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 081f4fdc1b12..e6a4656bdbc5 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -334,10 +334,19 @@ public final class UsageEvents implements Parcelable { public static final int LOCUS_ID_SET = 30; /** + * An event type denoting that a component in the package has been used (e.g. broadcast + * receiver, service, content provider). This generally matches up with usage that would + * cause an app to leave force stop. The component itself is not provided as we are only + * interested in whether the package is used, not the component itself. + * @hide + */ + public static final int APP_COMPONENT_USED = 31; + + /** * Keep in sync with the greatest event type value. * @hide */ - public static final int MAX_EVENT_TYPE = 30; + public static final int MAX_EVENT_TYPE = 31; /** @hide */ public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index f3a4e1f79955..02e86cd4a863 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -370,6 +370,15 @@ public abstract class Context { /*********** Hidden flags below this line ***********/ /** + * Flag for {@link #bindService}: This flag is only intended to be used by the system to + * indicate that a service binding is not considered as real package component usage and should + * not generate a {@link android.app.usage.UsageEvents.Event#APP_COMPONENT_USED} event in usage + * stats. + * @hide + */ + public static final int BIND_NOT_APP_COMPONENT_USAGE = 0x00008000; + + /** * Flag for {@link #bindService}: allow the process hosting the target service to be treated * as if it's as important as a perceptible app to the user and avoid the oom killer killing * this process in low memory situations until there aren't any other processes left but the diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java index 4d04a7af4693..8de9454ddeda 100644 --- a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java +++ b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java @@ -78,15 +78,15 @@ public class UsageStatsPersistenceTest { "VALID_FLAG_BITS", "UNASSIGNED_TOKEN", "MAX_EVENT_TYPE"}; // All fields in this list are final constants defining event types and not persisted private static final String[] EVENT_TYPES = {"NONE", "ACTIVITY_DESTROYED", "ACTIVITY_PAUSED", - "ACTIVITY_RESUMED", "ACTIVITY_STOPPED", "CHOOSER_ACTION", "CONFIGURATION_CHANGE", - "CONTINUE_PREVIOUS_DAY", "CONTINUING_FOREGROUND_SERVICE", "DEVICE_SHUTDOWN", - "DEVICE_STARTUP", "END_OF_DAY", "FLUSH_TO_DISK", "FOREGROUND_SERVICE_START", - "FOREGROUND_SERVICE_STOP", "KEYGUARD_HIDDEN", "KEYGUARD_SHOWN", "LOCUS_ID_SET", - "MOVE_TO_BACKGROUND", "MOVE_TO_FOREGROUND", "NOTIFICATION_INTERRUPTION", - "NOTIFICATION_SEEN", "ROLLOVER_FOREGROUND_SERVICE", "SCREEN_INTERACTIVE", - "SCREEN_NON_INTERACTIVE", "SHORTCUT_INVOCATION", "SLICE_PINNED", "SLICE_PINNED_PRIV", - "STANDBY_BUCKET_CHANGED", "SYSTEM_INTERACTION", "USER_INTERACTION", "USER_STOPPED", - "USER_UNLOCKED"}; + "ACTIVITY_RESUMED", "ACTIVITY_STOPPED", "APP_COMPONENT_USED", "CHOOSER_ACTION", + "CONFIGURATION_CHANGE", "CONTINUE_PREVIOUS_DAY", "CONTINUING_FOREGROUND_SERVICE", + "DEVICE_SHUTDOWN", "DEVICE_STARTUP", "END_OF_DAY", "FLUSH_TO_DISK", + "FOREGROUND_SERVICE_START", "FOREGROUND_SERVICE_STOP", "KEYGUARD_HIDDEN", + "KEYGUARD_SHOWN", "LOCUS_ID_SET", "MOVE_TO_BACKGROUND", "MOVE_TO_FOREGROUND", + "NOTIFICATION_INTERRUPTION", "NOTIFICATION_SEEN", "ROLLOVER_FOREGROUND_SERVICE", + "SCREEN_INTERACTIVE", "SCREEN_NON_INTERACTIVE", "SHORTCUT_INVOCATION", "SLICE_PINNED", + "SLICE_PINNED_PRIV", "STANDBY_BUCKET_CHANGED", "SYSTEM_INTERACTION", "USER_INTERACTION", + "USER_STOPPED", "USER_UNLOCKED"}; @Test public void testUsageEventsFields() { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index d998ebbf4aff..277cb8c877dd 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -86,6 +86,7 @@ import android.app.Service; import android.app.ServiceStartArgs; import android.app.admin.DevicePolicyEventLogger; import android.app.compat.CompatChanges; +import android.app.usage.UsageEvents; import android.appwidget.AppWidgetManagerInternal; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; @@ -2464,6 +2465,10 @@ public final class ActiveServices { s.setAllowedBgFgsStartsByBinding(true); } + if ((flags & Context.BIND_NOT_APP_COMPONENT_USAGE) != 0) { + s.isNotAppComponentUsage = true; + } + if (s.app != null) { updateServiceClientActivitiesLocked(s.app.mServices, c, true); } @@ -3332,6 +3337,14 @@ public final class ActiveServices { return msg; } + // Report usage if binding is from a different package except for explicitly exempted + // bindings + if (!r.appInfo.packageName.equals(r.mRecentCallingPackage) + && !r.isNotAppComponentUsage) { + mAm.mUsageStatsService.reportEvent( + r.packageName, r.userId, UsageEvents.Event.APP_COMPONENT_USED); + } + // Service is now being launched, its package can't be stopped. try { AppGlobals.getPackageManager().setPackageStoppedState( diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e8a4fa20cd30..874e5272764c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2684,6 +2684,11 @@ public class ActivityManagerService extends IActivityManager.Stub } if (mUsageStatsService != null) { mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode(), taskRoot); + if (event == Event.ACTIVITY_RESUMED) { + // Report component usage as an activity is an app component + mUsageStatsService.reportEvent( + activity.getPackageName(), userId, Event.APP_COMPONENT_USED); + } } ContentCaptureManagerInternal contentCaptureService = mContentCaptureService; if (contentCaptureService != null && (event == Event.ACTIVITY_PAUSED @@ -6099,6 +6104,10 @@ public class ActivityManagerService extends IActivityManager.Stub updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN); } + // Report usage as process is persistent and being started. + mUsageStatsService.reportEvent(info.packageName, UserHandle.getUserId(app.uid), + Event.APP_COMPONENT_USED); + // This package really, really can not be stopped. try { AppGlobals.getPackageManager().setPackageStoppedState( diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 29061930cd84..06cacc70a9b8 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -29,6 +29,7 @@ import android.app.AppOpsManager; import android.app.BroadcastOptions; import android.app.IApplicationThread; import android.app.PendingIntent; +import android.app.usage.UsageEvents.Event; import android.content.ComponentName; import android.content.ContentResolver; import android.content.IIntentReceiver; @@ -52,6 +53,7 @@ import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; import android.permission.IPermissionManager; +import android.text.TextUtils; import android.util.EventLog; import android.util.Slog; import android.util.SparseIntArray; @@ -1634,6 +1636,13 @@ public final class BroadcastQueue { brOptions.getTemporaryAppAllowlistReason()); } + // Report that a component is used for explicit broadcasts. + if (!r.intent.isExcludingStopped() && r.curComponent != null + && !TextUtils.equals(r.curComponent.getPackageName(), r.callerPackage)) { + mService.mUsageStatsService.reportEvent( + r.curComponent.getPackageName(), r.userId, Event.APP_COMPONENT_USED); + } + // Broadcast is being executed, its package can't be stopped. try { AppGlobals.getPackageManager().setPackageStoppedState( diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index f43c7f6278c9..2c8794d75795 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -32,6 +32,7 @@ import android.app.AppOpsManager; import android.app.ApplicationExitInfo; import android.app.ContentProviderHolder; import android.app.IApplicationThread; +import android.app.usage.UsageEvents.Event; import android.content.ComponentName; import android.content.ContentProvider; import android.content.ContentResolver; @@ -57,6 +58,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; @@ -412,6 +414,12 @@ public class ContentProviderHelper { final long origId = Binder.clearCallingIdentity(); try { + if (!TextUtils.equals(cpr.appInfo.packageName, callingPackage)) { + // Report component used since a content provider is being bound. + mService.mUsageStatsService.reportEvent( + cpr.appInfo.packageName, userId, Event.APP_COMPONENT_USED); + } + // Content provider is now in use, its package can't be stopped. try { checkTime(startTime, diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 3ab95d131fad..9cd9902f4995 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -107,6 +107,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN boolean delayed; // are we waiting to start this service in the background? boolean fgRequired; // is the service required to go foreground after starting? boolean fgWaiting; // is a timeout for going foreground already scheduled? + boolean isNotAppComponentUsage; // is service binding not considered component/package usage? boolean isForeground; // is service currently in foreground mode? int foregroundId; // Notification ID of last foreground req. Notification foregroundNoti; // Notification record of foreground state. diff --git a/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java new file mode 100644 index 000000000000..d786a5dac83a --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java @@ -0,0 +1,128 @@ +/* + * 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.server.usage; + +import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; +import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mockitoSession; + +import android.app.usage.UsageEvents; +import android.app.usage.UsageEvents.Event; +import android.content.Context; +import android.os.SystemClock; +import android.text.format.DateUtils; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.usage.UserUsageStatsService.StatsUpdatedListener; + +import org.junit.After; +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.io.File; +import java.util.HashMap; + +@RunWith(AndroidJUnit4.class) +public class UserUsageStatsServiceTest { + private static final int TEST_USER_ID = 0; + private static final String TEST_PACKAGE_NAME = "test.package"; + private static final long TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS; + + private UserUsageStatsService mService; + private MockitoSession mMockitoSession; + + @Mock + private Context mContext; + @Mock + private StatsUpdatedListener mStatsUpdatedListener; + + @Before + public void setUp() { + mMockitoSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .startMocking(); + + File dir = new File(InstrumentationRegistry.getContext().getCacheDir(), "test"); + mService = new UserUsageStatsService(mContext, TEST_USER_ID, dir, mStatsUpdatedListener); + + HashMap<String, Long> installedPkgs = new HashMap<>(); + installedPkgs.put(TEST_PACKAGE_NAME, System.currentTimeMillis()); + + mService.init(System.currentTimeMillis(), installedPkgs); + } + + @After + public void tearDown() { + if (mMockitoSession != null) { + mMockitoSession.finishMocking(); + } + } + + @Test + public void testReportEvent_eventAppearsInQueries() { + Event event = new Event(ACTIVITY_RESUMED, SystemClock.elapsedRealtime()); + event.mPackage = TEST_PACKAGE_NAME; + mService.reportEvent(event); + + long now = System.currentTimeMillis(); + long startTime = now - TIME_INTERVAL_MILLIS; + UsageEvents events = mService.queryEventsForPackage( + startTime, now, TEST_PACKAGE_NAME, false /* includeTaskRoot */); + + boolean hasTestEvent = false; + while (events != null && events.hasNextEvent()) { + Event outEvent = new Event(); + events.getNextEvent(outEvent); + if (outEvent.mEventType == ACTIVITY_RESUMED) { + hasTestEvent = true; + } + } + assertTrue(hasTestEvent); + } + + @Test + public void testReportEvent_packageUsedEventNotTracked() { + Event event = new Event(APP_COMPONENT_USED, SystemClock.elapsedRealtime()); + event.mPackage = TEST_PACKAGE_NAME; + mService.reportEvent(event); + + long now = System.currentTimeMillis(); + long startTime = now - TIME_INTERVAL_MILLIS; + UsageEvents events = mService.queryEventsForPackage( + startTime, now, TEST_PACKAGE_NAME, false /* includeTaskRoot */); + + boolean hasTestEvent = false; + while (events != null && events.hasNextEvent()) { + Event outEvent = new Event(); + events.getNextEvent(outEvent); + if (outEvent.mEventType == APP_COMPONENT_USED) { + hasTestEvent = true; + } + } + assertFalse(hasTestEvent); + } +} diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index b1e6683f0486..f35b9e2ce0ed 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -303,7 +303,9 @@ class UserUsageStatsService { // FLUSH_TO_DISK is a private event. && event.mEventType != Event.FLUSH_TO_DISK // DEVICE_SHUTDOWN is added to event list after reboot. - && event.mEventType != Event.DEVICE_SHUTDOWN) { + && event.mEventType != Event.DEVICE_SHUTDOWN + // We aren't interested in every instance of the APP_COMPONENT_USED event. + && event.mEventType != Event.APP_COMPONENT_USED) { currentDailyStats.addEvent(event); } @@ -1176,6 +1178,8 @@ class UserUsageStatsService { return "USER_STOPPED"; case Event.LOCUS_ID_SET: return "LOCUS_ID_SET"; + case Event.APP_COMPONENT_USED: + return "APP_COMPONENT_USED"; default: return "UNKNOWN_TYPE_" + eventType; } |