Merge "Collects metrics for the new Sharing Shortcuts api" into qt-dev
am: 573b3162a2
Change-Id: I15797e72d4369c84ed42a1c867787f2ab222036d
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 9a34ffa..b7d838e 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -202,6 +202,9 @@
private long mChooserShownTime;
protected boolean mIsSuccessfullySelected;
+ private long mQueriedTargetServicesTimeMs;
+ private long mQueriedSharingShortcutsTimeMs;
+
private ChooserListAdapter mChooserListAdapter;
private ChooserRowAdapter mChooserRowAdapter;
private int mChooserRowServiceSpacing;
@@ -273,6 +276,8 @@
sri.connection.destroy();
mServiceConnections.remove(sri.connection);
if (mServiceConnections.isEmpty()) {
+ logDirectShareTargetReceived(
+ MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_CHOOSER_SERVICE);
sendVoiceChoicesIfNeeded();
}
break;
@@ -283,6 +288,8 @@
}
unbindRemainingServices();
+ logDirectShareTargetReceived(
+ MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_CHOOSER_SERVICE);
sendVoiceChoicesIfNeeded();
mChooserListAdapter.completeServiceTargetLoading();
break;
@@ -305,6 +312,8 @@
break;
case SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED:
+ logDirectShareTargetReceived(
+ MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER);
sendVoiceChoicesIfNeeded();
break;
@@ -1155,6 +1164,8 @@
}
void queryTargetServices(ChooserListAdapter adapter) {
+ mQueriedTargetServicesTimeMs = System.currentTimeMillis();
+
final PackageManager pm = getPackageManager();
ShortcutManager sm = (ShortcutManager) getSystemService(ShortcutManager.class);
int targetsToQuery = 0;
@@ -1281,6 +1292,7 @@
private void queryDirectShareTargets(
ChooserListAdapter adapter, boolean skipAppPredictionService) {
+ mQueriedSharingShortcutsTimeMs = System.currentTimeMillis();
if (!skipAppPredictionService) {
AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
if (appPredictor != null) {
@@ -1391,6 +1403,14 @@
// Do nothing. We'll send the voice stuff ourselves.
}
+ private void logDirectShareTargetReceived(int logCategory) {
+ final long queryTime =
+ logCategory == MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER
+ ? mQueriedSharingShortcutsTimeMs : mQueriedTargetServicesTimeMs;
+ final int apiLatency = (int) (System.currentTimeMillis() - queryTime);
+ getMetricsLogger().write(new LogMaker(logCategory).setSubtype(apiLatency));
+ }
+
void updateModelAndChooserCounts(TargetInfo info) {
if (info != null) {
sendClickToAppPredictor(info);
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 65338cb..af8c631 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -263,6 +263,14 @@
PREVIOUSLY_VISIBLE = 2;
}
+ // Types for ACTION_SHORTCUTS_CHANGED
+ enum ShortcutsChangesInfo {
+ SHORTCUTS_CHANGED_UNKNOWN = 0;
+ SHORTCUTS_CHANGED_USER_ID = 1;
+ SHORTCUTS_CHANGED_PACKAGE_COUNT = 2;
+ SHORTCUTS_CHANGED_SHORTCUT_COUNT = 3;
+ }
+
// Explanations for notification importance, derived from
// NotificationRecord.mImportanceExplanation.
enum NotificationImportanceExplanation {
@@ -7222,6 +7230,19 @@
// OS: Q
ASSISTANT = 1716;
+ // ACTION: Published shortcuts in ShortcutManager changed
+ // TYPE: All the SHORTCUTS_CHANGED_* values in ShortcutsChangesInfo
+ // OS: Q
+ ACTION_SHORTCUTS_CHANGED = 1717;
+
+ // ACTION: Direct share targets loaded via ShortcutManager
+ // OS: Q
+ ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER = 1718;
+
+ // ACTION: Direct share targets loaded via ChooserService
+ // OS: Q
+ ACTION_DIRECT_SHARE_TARGETS_LOADED_CHOOSER_SERVICE = 1719;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 9782648..eec4b70 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -680,20 +680,23 @@
final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
for (int i = 0; i < shortcuts.size(); i++) {
- final ShortcutInfo si = shortcuts.get(i);
+ final Set<String> categories = shortcuts.get(i).getCategories();
+ if (categories == null || categories.isEmpty()) {
+ continue;
+ }
for (int j = 0; j < matchedTargets.size(); j++) {
// Shortcut must have all of share target categories
boolean hasAllCategories = true;
final ShareTargetInfo target = matchedTargets.get(j);
for (int q = 0; q < target.mCategories.length; q++) {
- if (!si.getCategories().contains(target.mCategories[q])) {
+ if (!categories.contains(target.mCategories[q])) {
hasAllCategories = false;
break;
}
}
if (hasAllCategories) {
- result.add(new ShortcutManager.ShareShortcutInfo(si, new ComponentName(
- getPackageName(), target.mTargetClass)));
+ result.add(new ShortcutManager.ShareShortcutInfo(shortcuts.get(i),
+ new ComponentName(getPackageName(), target.mTargetClass)));
break;
}
}
@@ -706,6 +709,45 @@
}
/**
+ * Returns the number of shortcuts that can be used as a share target in the ShareSheet. Such
+ * shortcuts must have a matching category with at least one of the defined ShareTargets from
+ * the app's Xml resource.
+ */
+ int getSharingShortcutCount() {
+ if (mShortcuts.isEmpty() || mShareTargets.isEmpty()) {
+ return 0;
+ }
+
+ // Get the list of all dynamic shortcuts in this package
+ final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+ findAll(shortcuts, ShortcutInfo::isDynamicVisible, ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+ int sharingShortcutCount = 0;
+ for (int i = 0; i < shortcuts.size(); i++) {
+ final Set<String> categories = shortcuts.get(i).getCategories();
+ if (categories == null || categories.isEmpty()) {
+ continue;
+ }
+ for (int j = 0; j < mShareTargets.size(); j++) {
+ // A SharingShortcut must have all of share target categories
+ boolean hasAllCategories = true;
+ final ShareTargetInfo target = mShareTargets.get(j);
+ for (int q = 0; q < target.mCategories.length; q++) {
+ if (!categories.contains(target.mCategories[q])) {
+ hasAllCategories = false;
+ break;
+ }
+ }
+ if (hasAllCategories) {
+ sharingShortcutCount++;
+ break;
+ }
+ }
+ }
+ return sharingShortcutCount;
+ }
+
+ /**
* Return the filenames (excluding path names) of icon bitmap files from this package.
*/
public ArraySet<String> getUsedBitmapFiles() {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index daef4e0..2d8a2ac 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -95,6 +95,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
@@ -413,6 +414,9 @@
@GuardedBy("mLock")
private Exception mLastWtfStacktrace;
+ @GuardedBy("mLock")
+ private final MetricsLogger mMetricsLogger = new MetricsLogger();
+
static class InvalidFileFormatException extends Exception {
public InvalidFileFormatException(String message, Throwable cause) {
super(message, cause);
@@ -981,6 +985,8 @@
Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
file.failWrite(os);
}
+
+ getUserShortcutsLocked(userId).logSharingShortcutStats(mMetricsLogger);
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 1fd9b69..8c207a8 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -20,6 +20,7 @@
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.pm.ShortcutManager;
+import android.metrics.LogMaker;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.ArrayMap;
@@ -27,6 +28,8 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.Preconditions;
import com.android.server.pm.ShortcutService.DumpFilter;
import com.android.server.pm.ShortcutService.InvalidFileFormatException;
@@ -647,4 +650,23 @@
return result;
}
+
+ void logSharingShortcutStats(MetricsLogger logger) {
+ int packageWithShareTargetsCount = 0;
+ int totalSharingShortcutCount = 0;
+ for (int i = 0; i < mPackages.size(); i++) {
+ if (mPackages.valueAt(i).hasShareTargets()) {
+ packageWithShareTargetsCount++;
+ totalSharingShortcutCount += mPackages.valueAt(i).getSharingShortcutCount();
+ }
+ }
+
+ final LogMaker logMaker = new LogMaker(MetricsEvent.ACTION_SHORTCUTS_CHANGED);
+ logger.write(logMaker.setType(MetricsEvent.SHORTCUTS_CHANGED_USER_ID)
+ .setSubtype(mUserId));
+ logger.write(logMaker.setType(MetricsEvent.SHORTCUTS_CHANGED_PACKAGE_COUNT)
+ .setSubtype(packageWithShareTargetsCount));
+ logger.write(logMaker.setType(MetricsEvent.SHORTCUTS_CHANGED_SHORTCUT_COUNT)
+ .setSubtype(totalSharingShortcutCount));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 6845f15..b806180 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -1584,6 +1584,22 @@
}
/**
+ * Make a shortcut with an ID and Category.
+ */
+ protected ShortcutInfo makeShortcutWithCategory(String id, Set<String> categories) {
+ final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id)
+ .setActivity(new ComponentName(mClientContext.getPackageName(), "main"))
+ .setShortLabel("title-" + id)
+ .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class))
+ .setCategories(categories);
+ final ShortcutInfo s = b.build();
+
+ s.setTimestamp(mInjectedCurrentTimeMillis); // HACK
+
+ return s;
+ }
+
+ /**
* Make an intent.
*/
protected Intent makeIntent(String action, Class<?> clazz, Object... bundleKeysAndValues) {
@@ -1818,6 +1834,17 @@
}
/**
+ * @return the number of shortcuts stored internally for the caller that can be used as a share
+ * target in the ShareSheet. Such shortcuts have a matching category with at least one of the
+ * defined ShareTargets from the app's Xml resource.
+ */
+ protected int getCallerSharingShortcutCount() {
+ final ShortcutPackage p = mService.getPackageShortcutForTest(
+ getCallingPackage(), getCallingUserId());
+ return p == null ? 0 : p.getSharingShortcutCount();
+ }
+
+ /**
* @return all shortcuts owned by caller that are actually visible via ShortcutManager.
* See also {@link #getCallerShortcuts}.
*/
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java
index eb4db7a..ba26f79 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java
@@ -17,6 +17,7 @@
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
import android.content.ComponentName;
import android.content.pm.ShortcutInfo;
@@ -25,6 +26,8 @@
import com.android.frameworks.servicestests.R;
import com.android.server.pm.ShortcutService.ConfigConstants;
+import java.util.Set;
+
/**
* Tests related to shortcut rank auto-adjustment.
*/
@@ -50,6 +53,10 @@
return makeShortcutWithActivityAndRank(id, activity, ShortcutInfo.RANK_NOT_SET);
}
+ private ShortcutInfo shortcut(String id, Set<String> categories) {
+ return makeShortcutWithCategory(id, categories);
+ }
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -502,4 +509,30 @@
runTestWithManifestShortcuts(() -> testDisableShortcuts_noManifestShortcuts());
}
+ public void testGetSharingShortcutCount() {
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_share_targets);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // There are two valid <share-target> definitions in the test manifest with two different
+ // categories: {"com.test.category.CATEGORY1", "com.test.category.CATEGORY2"} and
+ // {"com.test.category.CATEGORY5", "com.test.category.CATEGORY6"}.
+ //
+ // Note that a shortcut is a match, only if it has ALL of the categories of at least one
+ // of the share-target definitions from the manifest.
+
+ mManager.addDynamicShortcuts(list(
+ shortcut("s1", set("com.test.category.CATEGORY1", "com.test.category.CATEGORY2")),
+ shortcut("s2", set("com.test.category.CATEGORY5")),
+ shortcut("s3", set("com.test.category.CATEGORY5", "com.test.category.CATEGORY6")),
+ shortcut("s4", set("com.test.category.CATEGORY1", "com.test.category.CATEGORY2",
+ "com.test.category.CATEGORY5", "com.test.category.CATEGORY6")),
+ shortcut("s5", A1)
+ ));
+
+ assertEquals(3, getCallerSharingShortcutCount());
+ }
}