diff options
| author | 2018-03-14 09:20:39 -0400 | |
|---|---|---|
| committer | 2018-03-21 10:03:29 -0400 | |
| commit | 1918ef7569e90c70246e535478b26732b82d92d3 (patch) | |
| tree | 1f0e89e4d85c2d5f531fff7fc74d2b9471171215 | |
| parent | fc4ae7ec0eea2351afcff2a08a21247e10533c79 (diff) | |
UsageEvents for slices pinning
Test: atest frameworks/base/services/tests/servicestests
Bug: 73455786
Change-Id: I6a37f5525c29d3f47aa37e262c8834840309ba44
7 files changed, 105 insertions, 29 deletions
| diff --git a/api/system-current.txt b/api/system-current.txt index a5be12fdfc9c..1e2fe3df0d2c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -725,6 +725,8 @@ package android.app.usage {      method public java.lang.String getNotificationChannelId();      field public static final int NOTIFICATION_INTERRUPTION = 12; // 0xc      field public static final int NOTIFICATION_SEEN = 10; // 0xa +    field public static final int SLICE_PINNED = 14; // 0xe +    field public static final int SLICE_PINNED_PRIV = 13; // 0xd      field public static final int SYSTEM_INTERACTION = 6; // 0x6    } diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 8550ac0f32cd..b354e8122a98 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -125,6 +125,20 @@ public final class UsageEvents implements Parcelable {          @SystemApi          public static final int NOTIFICATION_INTERRUPTION = 12; +        /** +         * A Slice was pinned by the default launcher or the default assistant. +         * @hide +         */ +        @SystemApi +        public static final int SLICE_PINNED_PRIV = 13; + +        /** +         * A Slice was pinned by an app. +         * @hide +         */ +        @SystemApi +        public static final int SLICE_PINNED = 14; +          /** @hide */          public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0; diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index a7dfd35acf1b..0b7d9d02f1cb 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -16,6 +16,8 @@  package com.android.server.slice; +import static android.app.usage.UsageEvents.Event.SLICE_PINNED; +import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV;  import static android.content.ContentProvider.getUriWithoutUserId;  import static android.content.ContentProvider.getUserIdFromUri;  import static android.content.ContentProvider.maybeAddUserId; @@ -31,6 +33,7 @@ import android.app.IActivityManager;  import android.app.slice.ISliceManager;  import android.app.slice.SliceManager;  import android.app.slice.SliceSpec; +import android.app.usage.UsageStatsManagerInternal;  import android.content.BroadcastReceiver;  import android.content.ComponentName;  import android.content.Context; @@ -95,6 +98,7 @@ public class SliceManagerService extends ISliceManager.Stub {      private final AtomicFile mSliceAccessFile;      @GuardedBy("mAccessList")      private final SliceFullAccessList mAccessList; +    private final UsageStatsManagerInternal mAppUsageStats;      public SliceManagerService(Context context) {          this(context, createHandler().getLooper()); @@ -112,6 +116,7 @@ public class SliceManagerService extends ISliceManager.Stub {          final File systemDir = new File(Environment.getDataDirectory(), "system");          mSliceAccessFile = new AtomicFile(new File(systemDir, "slice_access.xml"));          mAccessList = new SliceFullAccessList(mContext); +        mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);          synchronized (mSliceAccessFile) {              if (!mSliceAccessFile.exists()) return; @@ -166,8 +171,19 @@ public class SliceManagerService extends ISliceManager.Stub {      public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token) throws RemoteException {          verifyCaller(pkg);          enforceAccess(pkg, uri); -        uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier()); +        int user = Binder.getCallingUserHandle().getIdentifier(); +        uri = maybeAddUserId(uri, user);          getOrCreatePinnedSlice(uri, pkg).pin(pkg, specs, token); + +        Uri finalUri = uri; +        mHandler.post(() -> { +            String slicePkg = getProviderPkg(finalUri, user); +            if (slicePkg != null && !Objects.equals(pkg, slicePkg)) { +                mAppUsageStats.reportEvent(slicePkg, user, +                        isAssistant(pkg, user) || isDefaultHomeApp(pkg, user) +                                ? SLICE_PINNED_PRIV : SLICE_PINNED); +            } +        });      }      @Override @@ -352,36 +368,43 @@ public class SliceManagerService extends ISliceManager.Stub {              if (getContext().checkUriPermission(uri, pid, uid,                      Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PERMISSION_GRANTED) {                  // Last fallback (if the calling app owns the authority, then it can have access). -                long ident = Binder.clearCallingIdentity(); +                if (!Objects.equals(getProviderPkg(uri, user), pkg)) { +                    return PERMISSION_DENIED; +                } +            } +        } +        return PERMISSION_GRANTED; +    } + +    private String getProviderPkg(Uri uri, int user) { +        long ident = Binder.clearCallingIdentity(); +        try { +            IBinder token = new Binder(); +            IActivityManager activityManager = ActivityManager.getService(); +            ContentProviderHolder holder = null; +            String providerName = getUriWithoutUserId(uri).getAuthority(); +            try {                  try { -                    IBinder token = new Binder(); -                    IActivityManager activityManager = ActivityManager.getService(); -                    ContentProviderHolder holder = null; -                    String providerName = getUriWithoutUserId(uri).getAuthority(); -                    try { -                        try { -                            holder = activityManager.getContentProviderExternal( -                                    providerName, getUserIdFromUri(uri, user), token); -                            if (holder == null || holder.info == null -                                    || !Objects.equals(holder.info.packageName, pkg)) { -                                return PERMISSION_DENIED; -                            } -                        } finally { -                            if (holder != null && holder.provider != null) { -                                activityManager.removeContentProviderExternal(providerName, token); -                            } -                        } -                    } catch (RemoteException e) { -                        // Can't happen. -                        e.rethrowAsRuntimeException(); +                    holder = activityManager.getContentProviderExternal( +                            providerName, getUserIdFromUri(uri, user), token); +                    if (holder != null && holder.info != null) { +                        return holder.info.packageName; +                    } else { +                        return null;                      }                  } finally { -                    // I know, the double finally seems ugly, but seems safest for the identity. -                    Binder.restoreCallingIdentity(ident); +                    if (holder != null && holder.provider != null) { +                        activityManager.removeContentProviderExternal(providerName, token); +                    }                  } +            } catch (RemoteException e) { +                // Can't happen. +                throw e.rethrowAsRuntimeException();              } +        } finally { +            // I know, the double finally seems ugly, but seems safest for the identity. +            Binder.restoreCallingIdentity(ident);          } -        return PERMISSION_GRANTED;      }      private void enforceCrossUser(String pkg, Uri uri) { diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index edf1f746fb4b..552c915f8480 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -17,6 +17,8 @@  package com.android.server.usage;  import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN; +import static android.app.usage.UsageEvents.Event.SLICE_PINNED; +import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV;  import static android.app.usage.UsageEvents.Event.USER_INTERACTION;  import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;  import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED; @@ -406,6 +408,30 @@ public class AppStandbyControllerTests {      }      @Test +    public void testSlicePinnedEvent() throws Exception { +        setChargingState(mController, false); + +        reportEvent(mController, USER_INTERACTION, 0); +        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); +        mInjector.mElapsedRealtime = 1; +        reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime); +        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); + +        mController.forceIdleState(PACKAGE_1, USER_ID, true); +        reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime); +        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController)); +    } + +    @Test +    public void testSlicePinnedPrivEvent() throws Exception { +        setChargingState(mController, false); + +        mController.forceIdleState(PACKAGE_1, USER_ID, true); +        reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime); +        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); +    } + +    @Test      public void testPredictionTimedout() throws Exception {          setChargingState(mController, false);          // Set it to timeout or usage, so that prediction can override it diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java index 4f446a9afb98..1073a800527a 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.when;  import android.app.AppOpsManager;  import android.app.slice.SliceSpec; +import android.app.usage.UsageStatsManagerInternal;  import android.content.pm.PackageManagerInternal;  import android.net.Uri;  import android.os.Binder; @@ -66,6 +67,8 @@ public class SliceManagerServiceTest extends UiServiceTestCase {      @Before      public void setup() {          LocalServices.addService(PackageManagerInternal.class, mock(PackageManagerInternal.class)); +        LocalServices.addService(UsageStatsManagerInternal.class, +                mock(UsageStatsManagerInternal.class));          mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));          mContext.getTestablePermissions().setPermission(TEST_URI, PERMISSION_GRANTED); @@ -77,6 +80,7 @@ public class SliceManagerServiceTest extends UiServiceTestCase {      @After      public void teardown() {          LocalServices.removeServiceForTest(PackageManagerInternal.class); +        LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);      }      @Test diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index e836677e38fc..1af5f469c0f0 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -36,6 +36,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;  import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;  import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;  import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; +  import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;  import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; @@ -43,8 +44,8 @@ import android.annotation.UserIdInt;  import android.app.ActivityManager;  import android.app.AppGlobals;  import android.app.usage.AppStandbyInfo; -import android.app.usage.UsageStatsManager.StandbyBuckets;  import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManager.StandbyBuckets;  import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;  import android.appwidget.AppWidgetManager;  import android.content.BroadcastReceiver; @@ -100,7 +101,6 @@ import java.time.format.DateTimeParseException;  import java.util.ArrayList;  import java.util.Arrays;  import java.util.List; -import java.util.Map;  import java.util.Set;  import java.util.concurrent.CountDownLatch; @@ -690,7 +690,9 @@ public class AppStandbyController {                      || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND                      || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION                      || event.mEventType == UsageEvents.Event.USER_INTERACTION -                    || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN)) { +                    || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN +                    || event.mEventType == UsageEvents.Event.SLICE_PINNED +                    || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV)) {                  final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(                          event.mPackage, userId, elapsedRealtime); @@ -699,7 +701,8 @@ public class AppStandbyController {                  final long nextCheckTime;                  final int subReason = usageEventToSubReason(event.mEventType);                  final int reason = REASON_MAIN_USAGE | subReason; -                if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) { +                if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN +                        || event.mEventType == UsageEvents.Event.SLICE_PINNED) {                      // Mild usage elevates to WORKING_SET but doesn't change usage time.                      mAppIdleHistory.reportUsage(appHistory, event.mPackage,                              STANDBY_BUCKET_WORKING_SET, subReason, diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index d9742825ac70..c2e38f2f0bd8 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -761,6 +761,10 @@ class UserUsageStatsService {                  return "STANDBY_BUCKET_CHANGED";              case UsageEvents.Event.NOTIFICATION_INTERRUPTION:                  return "NOTIFICATION_INTERRUPTION"; +            case UsageEvents.Event.SLICE_PINNED: +                return "SLICE_PINNED"; +            case UsageEvents.Event.SLICE_PINNED_PRIV: +                return "SLICE_PINNED_PRIV";              default:                  return "UNKNOWN";          } |