Added LocusId support to Shortcut and Notification.
Test: atest FrameworksCoreTests:android.app.NotificationTest#testBuilder_setLocusId
Test: atest CtsShortcutManagerTestCases:android.content.pm.cts.shortcutmanager.ShortcutManagerClientApiTest
Test: atest CtsShortcutManagerTestCases # sanity check; some tests are failing, but pass individually
Test: m update-api
Bug: 126945732
Change-Id: Ib819a63aa008ab952b02da8c0be1b528814dfb00
diff --git a/api/current.txt b/api/current.txt
index da8790b..1d40562 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5264,6 +5264,7 @@
method public String getGroup();
method public int getGroupAlertBehavior();
method public android.graphics.drawable.Icon getLargeIcon();
+ method @Nullable public android.content.LocusId getLocusId();
method public CharSequence getSettingsText();
method public String getShortcutId();
method public android.graphics.drawable.Icon getSmallIcon();
@@ -5543,6 +5544,7 @@
method @NonNull public android.app.Notification.Builder setLargeIcon(android.graphics.drawable.Icon);
method @Deprecated public android.app.Notification.Builder setLights(@ColorInt int, int, int);
method @NonNull public android.app.Notification.Builder setLocalOnly(boolean);
+ method @NonNull public android.app.Notification.Builder setLocusId(@Nullable android.content.LocusId);
method @NonNull public android.app.Notification.Builder setNumber(int);
method @NonNull public android.app.Notification.Builder setOngoing(boolean);
method @NonNull public android.app.Notification.Builder setOnlyAlertOnce(boolean);
@@ -11972,6 +11974,7 @@
method @Nullable public android.content.Intent getIntent();
method @Nullable public android.content.Intent[] getIntents();
method public long getLastChangedTimestamp();
+ method @Nullable public android.content.LocusId getLocusId();
method @Nullable public CharSequence getLongLabel();
method @NonNull public String getPackage();
method public int getRank();
@@ -12006,6 +12009,7 @@
method @NonNull public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
method @NonNull public android.content.pm.ShortcutInfo.Builder setIntent(@NonNull android.content.Intent);
method @NonNull public android.content.pm.ShortcutInfo.Builder setIntents(@NonNull android.content.Intent[]);
+ method @NonNull public android.content.pm.ShortcutInfo.Builder setLocusId(@NonNull android.content.LocusId);
method @NonNull public android.content.pm.ShortcutInfo.Builder setLongLabel(@NonNull CharSequence);
method @NonNull public android.content.pm.ShortcutInfo.Builder setLongLived();
method @NonNull public android.content.pm.ShortcutInfo.Builder setPerson(@NonNull android.app.Person);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e0cf561..234db7f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -31,6 +31,7 @@
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
+import android.content.LocusId;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -77,6 +78,7 @@
import android.view.NotificationHeaderView;
import android.view.View;
import android.view.ViewGroup;
+import android.view.contentcapture.ContentCaptureContext;
import android.widget.ProgressBar;
import android.widget.RemoteViews;
@@ -1271,6 +1273,7 @@
private long mTimeout;
private String mShortcutId;
+ private LocusId mLocusId;
private CharSequence mSettingsText;
private BubbleMetadata mBubbleMetadata;
@@ -2267,6 +2270,10 @@
mShortcutId = parcel.readString();
}
+ if (parcel.readInt() != 0) {
+ mLocusId = LocusId.CREATOR.createFromParcel(parcel);
+ }
+
mBadgeIcon = parcel.readInt();
if (parcel.readInt() != 0) {
@@ -2390,6 +2397,7 @@
that.mChannelId = this.mChannelId;
that.mTimeout = this.mTimeout;
that.mShortcutId = this.mShortcutId;
+ that.mLocusId = this.mLocusId;
that.mBadgeIcon = this.mBadgeIcon;
that.mSettingsText = this.mSettingsText;
that.mGroupAlertBehavior = this.mGroupAlertBehavior;
@@ -2705,6 +2713,13 @@
parcel.writeInt(0);
}
+ if (mLocusId != null) {
+ parcel.writeInt(1);
+ mLocusId.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+
parcel.writeInt(mBadgeIcon);
if (mSettingsText != null) {
@@ -3018,6 +3033,10 @@
sb.append(" publicVersion=");
sb.append(publicVersion.toString());
}
+ if (this.mLocusId != null) {
+ sb.append(" locusId=");
+ sb.append(this.mLocusId); // LocusId.toString() is PII safe.
+ }
sb.append(")");
return sb.toString();
}
@@ -3120,6 +3139,16 @@
return mShortcutId;
}
+ /**
+ * Gets the {@link LocusId} associated with this notification.
+ *
+ * <p>Used by the device's intelligence services to correlate objects (such as
+ * {@link ShortcutInfo} and {@link ContentCaptureContext}) that are correlated.
+ */
+ @Nullable
+ public LocusId getLocusId() {
+ return mLocusId;
+ }
/**
* Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
@@ -3478,6 +3507,19 @@
}
/**
+ * Sets the {@link LocusId} associated with this notification.
+ *
+ * <p>This method should be called when the {@link LocusId} is used in other places (such
+ * as {@link ShortcutInfo} and {@link ContentCaptureContext}) so the device's intelligence
+ * services can correlate them.
+ */
+ @NonNull
+ public Builder setLocusId(@Nullable LocusId locusId) {
+ mN.mLocusId = locusId;
+ return this;
+ }
+
+ /**
* Sets which icon to display as a badge for this notification.
*
* Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index d5273db..7b61807 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -22,11 +22,13 @@
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.annotation.UserIdInt;
+import android.app.Notification;
import android.app.Person;
import android.app.TaskStackBuilder;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.LocusId;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
@@ -41,6 +43,7 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import android.view.contentcapture.ContentCaptureContext;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
@@ -354,6 +357,9 @@
@Nullable
private Person[] mPersons;
+ @Nullable
+ private LocusId mLocusId;
+
private int mRank;
/**
@@ -415,6 +421,8 @@
}
mRank = b.mRank;
mExtras = b.mExtras;
+ mLocusId = b.mLocusId;
+
updateTimestamp();
}
@@ -521,6 +529,7 @@
mFlags = source.mFlags;
mLastChangedTimestamp = source.mLastChangedTimestamp;
mDisabledReason = source.mDisabledReason;
+ mLocusId = source.mLocusId;
// Just always keep it since it's cheep.
mIconResId = source.mIconResId;
@@ -876,6 +885,10 @@
if (source.mExtras != null) {
mExtras = source.mExtras;
}
+
+ if (source.mLocusId != null) {
+ mLocusId = source.mLocusId;
+ }
}
/**
@@ -941,6 +954,8 @@
private PersistableBundle mExtras;
+ private LocusId mLocusId;
+
/**
* Old style constructor.
* @hide
@@ -973,6 +988,19 @@
}
/**
+ * Sets the {@link LocusId} associated with this shortcut.
+ *
+ * <p>This method should be called when the {@link LocusId} is used in other places (such
+ * as {@link Notification} and {@link ContentCaptureContext}) so the device's intelligence
+ * services can correlate them.
+ */
+ @NonNull
+ public Builder setLocusId(@NonNull LocusId locusId) {
+ mLocusId = Preconditions.checkNotNull(locusId, "locusId cannot be null");
+ return this;
+ }
+
+ /**
* Sets the target activity. A shortcut will be shown along with this activity's icon
* on the launcher.
*
@@ -1295,6 +1323,17 @@
}
/**
+ * Gets the {@link LocusId} associated with this shortcut.
+ *
+ * <p>Used by the device's intelligence services to correlate objects (such as
+ * {@link Notification} and {@link ContentCaptureContext}) that are correlated.
+ */
+ @Nullable
+ public LocusId getLocusId() {
+ return mLocusId;
+ }
+
+ /**
* Return the package name of the publisher app.
*/
@NonNull
@@ -1999,6 +2038,7 @@
}
mPersons = source.readParcelableArray(cl, Person.class);
+ mLocusId = source.readParcelable(cl);
}
@Override
@@ -2048,6 +2088,7 @@
}
dest.writeParcelableArray(mPersons, flags);
+ dest.writeParcelable(mLocusId, flags);
}
public static final @android.annotation.NonNull Creator<ShortcutInfo> CREATOR =
@@ -2263,6 +2304,10 @@
sb.append(mBitmapPath);
}
+ if (mLocusId != null) {
+ sb.append("locusId="); sb.append(mLocusId); // LocusId.toString() is PII-safe.
+ }
+
sb.append("}");
return sb.toString();
}
@@ -2276,7 +2321,7 @@
Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
long lastChangedTimestamp,
int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason,
- Person[] persons) {
+ Person[] persons, LocusId locusId) {
mUserId = userId;
mId = id;
mPackageName = packageName;
@@ -2303,5 +2348,6 @@
mBitmapPath = bitmapPath;
mDisabledReason = disabledReason;
mPersons = persons;
+ mLocusId = locusId;
}
}
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index c17aa92..109cff9 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -21,12 +21,14 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
+import android.content.LocusId;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
import android.media.session.MediaSession;
@@ -320,6 +322,27 @@
action.clone().getSemanticAction());
}
+ @Test
+ public void testBuilder_setLocusId() {
+ LocusId locusId = new LocusId("4815162342");
+ Notification notification = new Notification.Builder(mContext, "whatever")
+ .setLocusId(locusId).build();
+ assertEquals(locusId, notification.getLocusId());
+
+ Notification clone = writeAndReadParcelable(notification);
+ assertEquals(locusId, clone.getLocusId());
+ }
+
+ @Test
+ public void testBuilder_setLocusId_null() {
+ Notification notification = new Notification.Builder(mContext, "whatever")
+ .setLocusId(null).build();
+ assertNull(notification.getLocusId());
+
+ Notification clone = writeAndReadParcelable(notification);
+ assertNull(clone.getLocusId());
+ }
+
private Notification.Builder getMediaNotification() {
MediaSession session = new MediaSession(mContext, "test");
return new Notification.Builder(mContext, "color")
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index d9a5eb9..eced165 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -22,6 +22,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.LocusId;
import android.content.pm.PackageInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
@@ -1702,15 +1703,17 @@
flags |= ShortcutInfo.FLAG_SHADOW;
}
+ LocusId locusId = null; // LocusId is not set on XML.
+
return new ShortcutInfo(
- userId, id, packageName, activityComponent, /* icon =*/ null,
+ userId, id, packageName, activityComponent, /* icon= */ null,
title, titleResId, titleResName, text, textResId, textResName,
disabledMessage, disabledMessageResId, disabledMessageResName,
categories,
intents.toArray(new Intent[intents.size()]),
rank, extras, lastChangedTimestamp, flags,
iconResId, iconResName, bitmapPath, disabledReason,
- persons.toArray(new Person[persons.size()]));
+ persons.toArray(new Person[persons.size()]), locusId);
}
private static Intent parseIntent(XmlPullParser parser)
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index 668fc88..f9c0db0 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -450,7 +450,8 @@
null, // icon res name
null, // bitmap path
disabledReason,
- null /* persons */);
+ null /* persons */,
+ null /* locusId */);
}
private static String parseCategory(ShortcutService service, AttributeSet attrs) {
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index e36586e..2077ecb 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -36,6 +36,7 @@
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
+import android.content.LocusId;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.Callback;
import android.content.pm.ShortcutInfo;
@@ -369,6 +370,10 @@
return ret;
}
+ public static LocusId locusId(String id) {
+ return new LocusId(id);
+ }
+
public static void resetAll(Collection<?> mocks) {
for (Object o : mocks) {
reset(o);