summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/app/Activity.java34
-rw-r--r--core/java/android/app/IActivityManager.aidl10
-rw-r--r--core/java/android/app/usage/UsageEvents.java38
-rw-r--r--core/java/android/content/LocusId.aidl19
-rw-r--r--core/proto/android/server/usagestatsservice_v2.proto2
-rw-r--r--services/core/java/android/app/usage/UsageStatsManagerInternal.java18
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java18
-rw-r--r--services/usage/java/com/android/server/usage/IntervalStats.java17
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsProtoV2.java9
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java15
13 files changed, 196 insertions, 4 deletions
diff --git a/api/current.txt b/api/current.txt
index 1cfc99acca32..d9cb9dfcc4ea 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3901,6 +3901,7 @@ package android.app {
method public void setImmersive(boolean);
method public void setInheritShowWhenLocked(boolean);
method public void setIntent(android.content.Intent);
+ method public void setLocusContext(@Nullable android.content.LocusId, @Nullable android.os.Bundle);
method public final void setMediaController(android.media.session.MediaController);
method public void setPictureInPictureParams(@NonNull android.app.PictureInPictureParams);
method @Deprecated public final void setProgress(int);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d952be5218a4..d8b5e7f3b5b0 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -45,6 +45,7 @@ import android.content.CursorLoader;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.LocusId;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -1025,6 +1026,39 @@ public class Activity extends ContextThemeWrapper
mIntent = newIntent;
}
+ /**
+ * Sets the {@link android.content.LocusId} for this activity. The locus id
+ * helps identify different instances of the same {@code Activity} class.
+ * <p> For example, a locus id based on a specific conversation could be set on a
+ * conversation app's chat {@code Activity}. The system can then use this locus id
+ * along with app's contents to provide ranking signals in various UI surfaces
+ * including sharing, notifications, shortcuts and so on.
+ * <p> It is recommended to set the same locus id in the shortcut's locus id using
+ * {@link android.content.pm.ShortcutInfo.Builder#setLocusId(android.content.LocusId)
+ * setLocusId}
+ * so that the system can learn appropriate ranking signals linking the activity's
+ * locus id with the matching shortcut.
+ *
+ * @param locusId a unique, stable id that identifies this {@code Activity} instance from
+ * others. This can be linked to a shortcut using
+ * {@link android.content.pm.ShortcutInfo.Builder#setLocusId(android.content.LocusId)
+ * setLocusId} with the same locus id string.
+ * @param bundle extras set or updated as part of this locus context. This may help provide
+ * additional metadata such as URLs, conversation participants specific to this
+ * {@code Activity}'s context.
+ *
+ * @see android.view.contentcapture.ContentCaptureManager
+ * @see android.view.contentcapture.ContentCaptureContext
+ */
+ public void setLocusContext(@Nullable LocusId locusId, @Nullable Bundle bundle) {
+ try {
+ ActivityManager.getService().setActivityLocusContext(mComponent, locusId, mToken);
+ } catch (RemoteException re) {
+ re.rethrowFromSystemServer();
+ }
+ // TODO(b/147750355): Pass locusId and bundle to the Content Capture.
+ }
+
/** Return the application that owns this activity. */
public final Application getApplication() {
return mApplication;
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 7d04ca0afe7e..3ffd7c70b40d 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -53,6 +53,7 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
+import android.content.LocusId;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.Point;
@@ -637,4 +638,13 @@ interface IActivityManager {
* and the given process is imperceptible.
*/
void killProcessesWhenImperceptible(in int[] pids, String reason);
+
+ /**
+ * Set locus context for a given activity.
+ * @param activity
+ * @param locusId a unique, stable id that identifies this activity instance from others.
+ * @param appToken ActivityRecord's appToken.
+ */
+ void setActivityLocusContext(in ComponentName activity, in LocusId locusId,
+ in IBinder appToken);
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 6ab880dfb36d..ab71e73fd58c 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -290,10 +290,16 @@ public final class UsageEvents implements Parcelable {
public static final int USER_STOPPED = 29;
/**
+ * An event type denoting that new locusId has been set for a given activity.
+ * @hide
+ */
+ public static final int LOCUS_ID_SET = 30;
+
+ /**
* Keep in sync with the greatest event type value.
* @hide
*/
- public static final int MAX_EVENT_TYPE = 29;
+ public static final int MAX_EVENT_TYPE = 30;
/** @hide */
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
@@ -436,6 +442,18 @@ public final class UsageEvents implements Parcelable {
*/
public int mNotificationChannelIdToken = UNASSIGNED_TOKEN;
+ /**
+ * LocusId.
+ * Currently LocusId only present for {@link #LOCUS_ID_SET} event types.
+ * {@hide}
+ */
+ public String mLocusId;
+
+ /**
+ * {@hide}
+ */
+ public int mLocusIdToken = UNASSIGNED_TOKEN;
+
/** @hide */
@EventFlags
public int mFlags;
@@ -609,6 +627,16 @@ public final class UsageEvents implements Parcelable {
return ret;
}
+ /**
+ * Returns the locusId for this event if the event is of type {@link #LOCUS_ID_SET},
+ * otherwise it returns null.
+ * @hide
+ */
+ @Nullable
+ public String getLocusId() {
+ return mLocusId;
+ }
+
private void copyFrom(Event orig) {
mPackage = orig.mPackage;
mClass = orig.mClass;
@@ -625,6 +653,7 @@ public final class UsageEvents implements Parcelable {
mFlags = orig.mFlags;
mBucketAndReason = orig.mBucketAndReason;
mNotificationChannelId = orig.mNotificationChannelId;
+ mLocusId = orig.mLocusId;
}
}
@@ -823,6 +852,9 @@ public final class UsageEvents implements Parcelable {
case Event.NOTIFICATION_INTERRUPTION:
p.writeString(event.mNotificationChannelId);
break;
+ case Event.LOCUS_ID_SET:
+ p.writeString(event.mLocusId);
+ break;
}
p.writeInt(event.mFlags);
}
@@ -871,6 +903,7 @@ public final class UsageEvents implements Parcelable {
eventOut.mContentType = null;
eventOut.mContentAnnotations = null;
eventOut.mNotificationChannelId = null;
+ eventOut.mLocusId = null;
switch (eventOut.mEventType) {
case Event.CONFIGURATION_CHANGE:
@@ -891,6 +924,9 @@ public final class UsageEvents implements Parcelable {
case Event.NOTIFICATION_INTERRUPTION:
eventOut.mNotificationChannelId = p.readString();
break;
+ case Event.LOCUS_ID_SET:
+ eventOut.mLocusId = p.readString();
+ break;
}
eventOut.mFlags = p.readInt();
}
diff --git a/core/java/android/content/LocusId.aidl b/core/java/android/content/LocusId.aidl
new file mode 100644
index 000000000000..eb98db06ccbf
--- /dev/null
+++ b/core/java/android/content/LocusId.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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 android.content;
+
+parcelable LocusId;
diff --git a/core/proto/android/server/usagestatsservice_v2.proto b/core/proto/android/server/usagestatsservice_v2.proto
index a28fcf3589f1..24b0728c29ec 100644
--- a/core/proto/android/server/usagestatsservice_v2.proto
+++ b/core/proto/android/server/usagestatsservice_v2.proto
@@ -99,6 +99,7 @@ message EventObfuscatedProto {
optional int32 instance_id = 10;
optional int32 task_root_package_token = 11;
optional int32 task_root_class_token = 12;
+ optional int32 locus_id_token = 13;
}
/**
@@ -117,6 +118,7 @@ message PendingEventProto {
optional int32 instance_id = 10;
optional string task_root_package = 11;
optional string task_root_class = 12;
+ optional string locus_id = 13;
}
/**
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index f3647602e69b..a8be66990fff 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -16,10 +16,14 @@
package android.app.usage;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.content.ComponentName;
+import android.content.LocusId;
import android.content.res.Configuration;
+import android.os.IBinder;
import android.os.UserHandle;
import android.os.UserManager;
@@ -111,6 +115,20 @@ public abstract class UsageStatsManagerInternal {
public abstract void reportContentProviderUsage(String name, String pkgName,
@UserIdInt int userId);
+
+ /**
+ * Reports locusId update for a given activity.
+ *
+ * @param activity The component name of the app.
+ * @param userId The user id of who uses the app.
+ * @param locusId The locusId a unique, stable id that identifies this activity.
+ * @param appToken ActivityRecord's appToken.
+ * {@link UsageEvents}
+ * @hide
+ */
+ public abstract void reportLocusUpdate(@NonNull ComponentName activity, @UserIdInt int userId,
+ @Nullable LocusId locusId, @NonNull IBinder appToken);
+
/**
* Prepares the UsageStatsService for shutdown.
*/
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5596b2fcb762..41a4bd433be5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -199,6 +199,7 @@ import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityPresentationInfo;
import android.content.pm.ApplicationInfo;
@@ -19642,4 +19643,19 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
}
+
+ @Override
+ public void setActivityLocusContext(ComponentName activity, LocusId locusId, IBinder appToken) {
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getCallingUserId();
+ if (getPackageManagerInternalLocked().getPackageUid(activity.getPackageName(),
+ /*flags=*/ 0, userId) != callingUid) {
+ throw new SecurityException("Calling uid " + callingUid + " cannot set locusId"
+ + "for package " + activity.getPackageName());
+ }
+
+ if (mUsageStatsService != null) {
+ mUsageStatsService.reportLocusUpdate(activity, userId, locusId, appToken);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java b/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
index f1b2ef811885..5d849c114e69 100644
--- a/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
@@ -92,6 +92,9 @@ public class IntervalStatsTests {
case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
event.mNotificationChannelId = "channel" + (i % 5); //"random" channel
break;
+ case UsageEvents.Event.LOCUS_ID_SET:
+ event.mLocusId = "locus" + (i % 7); //"random" locus
+ break;
}
intervalStats.addEvent(event);
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index e6bb244ef05b..f1c39067994c 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -52,6 +52,7 @@ import java.util.Set;
public class UsageStatsDatabaseTest {
private static final int MAX_TESTED_VERSION = 5;
+ private static final int OLDER_VERSION_MAX_EVENT_TYPE = 29;
protected Context mContext;
private UsageStatsDatabase mUsageStatsDatabase;
private File mTestDir;
@@ -79,7 +80,7 @@ public class UsageStatsDatabaseTest {
mUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
mUsageStatsDatabase.readMappingsLocked();
mUsageStatsDatabase.init(1);
- populateIntervalStats();
+ populateIntervalStats(MAX_TESTED_VERSION);
clearUsageStatsFiles();
}
@@ -117,7 +118,7 @@ public class UsageStatsDatabaseTest {
return sb.toString();
}
- private void populateIntervalStats() {
+ private void populateIntervalStats(int minVersion) {
final int numberOfEvents = 3000;
final int timeProgression = 23;
long time = System.currentTimeMillis() - (numberOfEvents*timeProgression);
@@ -147,9 +148,12 @@ public class UsageStatsDatabaseTest {
final int instanceId = i % 11;
event.mClass = ".fake.class.name" + instanceId;
event.mTimeStamp = time;
- event.mEventType = i % (MAX_EVENT_TYPE + 1); //"random" event type
event.mInstanceId = instanceId;
+ int maxEventType = (minVersion < 5) ? OLDER_VERSION_MAX_EVENT_TYPE : MAX_EVENT_TYPE;
+ event.mEventType = i % (maxEventType + 1); //"random" event type
+
+
final int rootPackageInt = (i % 5); // 5 "apps" start each task
event.mTaskRootPackage = "fake.package.name" + rootPackageInt;
@@ -174,6 +178,9 @@ public class UsageStatsDatabaseTest {
//"random" channel
event.mNotificationChannelId = "channel" + (i % 5);
break;
+ case Event.LOCUS_ID_SET:
+ event.mLocusId = "locus" + (i % 7); //"random" locus
+ break;
}
mIntervalStats.addEvent(event);
@@ -281,6 +288,10 @@ public class UsageStatsDatabaseTest {
assertEquals(e1.mNotificationChannelIdToken, e2.mNotificationChannelIdToken,
"Usage event " + debugId);
break;
+ case Event.LOCUS_ID_SET:
+ assertEquals(e1.mLocusIdToken, e2.mLocusIdToken,
+ "Usage event " + debugId);
+ break;
}
// fallthrough
case 4: // test fields added in version 4
@@ -392,6 +403,7 @@ public class UsageStatsDatabaseTest {
* version and read the automatically upgraded files on disk in the new file format.
*/
void runVersionChangeTest(int oldVersion, int newVersion, int interval) throws IOException {
+ populateIntervalStats(oldVersion);
// Write IntervalStats to disk in old version format
UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, oldVersion);
prevDB.readMappingsLocked();
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 8fb283adc740..8fadf5eb9333 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -28,6 +28,7 @@ import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
import static android.app.usage.UsageEvents.Event.KEYGUARD_HIDDEN;
import static android.app.usage.UsageEvents.Event.KEYGUARD_SHOWN;
+import static android.app.usage.UsageEvents.Event.LOCUS_ID_SET;
import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
import static android.app.usage.UsageEvents.Event.SCREEN_INTERACTIVE;
@@ -568,6 +569,16 @@ public class IntervalStats {
continue;
}
break;
+ case LOCUS_ID_SET:
+ event.mLocusId = packagesTokenData.getString(packageToken, event.mLocusIdToken);
+ if (event.mLocusId == null) {
+ Slog.e(TAG, "Unable to parse locus " + event.mLocusIdToken
+ + " for package " + packageToken);
+ this.events.remove(i);
+ dataOmitted = true;
+ continue;
+ }
+ break;
}
}
return dataOmitted;
@@ -675,6 +686,12 @@ public class IntervalStats {
packageToken, event.mPackage, event.mNotificationChannelId);
}
break;
+ case LOCUS_ID_SET:
+ if (!TextUtils.isEmpty(event.mLocusId)) {
+ event.mLocusIdToken = packagesTokenData.getTokenOrAdd(packageToken,
+ event.mPackage, event.mLocusId);
+ }
+ break;
}
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
index fe5da923597e..e4aa9fe0dc1e 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
@@ -277,6 +277,10 @@ final class UsageStatsProtoV2 {
event.mTaskRootClassToken = proto.readInt(
EventObfuscatedProto.TASK_ROOT_CLASS_TOKEN) - 1;
break;
+ case (int) EventObfuscatedProto.LOCUS_ID_TOKEN:
+ event.mLocusIdToken = proto.readInt(
+ EventObfuscatedProto.LOCUS_ID_TOKEN) - 1;
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
// timeStamp was not read, assume default value 0 plus beginTime
if (event.mTimeStamp == 0) {
@@ -398,6 +402,11 @@ final class UsageStatsProtoV2 {
proto.write(EventObfuscatedProto.SHORTCUT_ID_TOKEN, event.mShortcutIdToken + 1);
}
break;
+ case UsageEvents.Event.LOCUS_ID_SET:
+ if (event.mLocusIdToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+ proto.write(EventObfuscatedProto.LOCUS_ID_TOKEN, event.mLocusIdToken + 1);
+ }
+ break;
case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
if (event.mNotificationChannelIdToken != PackagesTokenData.UNASSIGNED_TOKEN) {
proto.write(EventObfuscatedProto.NOTIFICATION_CHANNEL_ID_TOKEN,
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 5119e5824f7f..9a18f8cd3a46 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -21,6 +21,7 @@ import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE;
import static android.app.usage.UsageEvents.Event.DEVICE_EVENT_PACKAGE_NAME;
import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
+import static android.app.usage.UsageEvents.Event.LOCUS_ID_SET;
import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
import static android.app.usage.UsageEvents.Event.USER_STOPPED;
@@ -30,6 +31,8 @@ import static android.app.usage.UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVIT
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.IUidObserver;
@@ -52,6 +55,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.LocusId;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -1987,6 +1991,17 @@ public class UsageStatsService extends SystemService implements
}
@Override
+ public void reportLocusUpdate(@NonNull ComponentName activity, @UserIdInt int userId,
+ @Nullable LocusId locusId, @NonNull IBinder appToken) {
+ Event event = new Event(LOCUS_ID_SET, SystemClock.elapsedRealtime());
+ event.mLocusId = locusId.getId();
+ event.mPackage = activity.getPackageName();
+ event.mClass = activity.getClassName();
+ event.mInstanceId = appToken.hashCode();
+ reportEventOrAddToQueue(userId, event);
+ }
+
+ @Override
public void reportContentProviderUsage(String name, String packageName, int userId) {
mAppStandby.postReportContentProviderUsage(name, packageName, userId);
}