Merge "UsageEvents for slices pinning" into pi-dev

This commit is contained in:
TreeHugger Robot
2018-03-21 16:07:32 +00:00
committed by Android (Google) Code Review
7 changed files with 107 additions and 31 deletions

View File

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

View File

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

View File

@@ -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,38 +368,45 @@ 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();
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();
}
} finally {
// I know, the double finally seems ugly, but seems safest for the identity.
Binder.restoreCallingIdentity(ident);
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 {
holder = activityManager.getContentProviderExternal(
providerName, getUserIdFromUri(uri, user), token);
if (holder != null && holder.info != null) {
return holder.info.packageName;
} else {
return null;
}
} finally {
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);
}
}
private void enforceCrossUser(String pkg, Uri uri) {
int user = Binder.getCallingUserHandle().getIdentifier();
if (getUserIdFromUri(uri, user) != user) {

View File

@@ -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;
@@ -405,6 +407,30 @@ public class AppStandbyControllerTests {
assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
}
@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);

View File

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

View File

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

View File

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