diff options
7 files changed, 175 insertions, 4 deletions
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 9a34ffae2a2e..b7d838edadc5 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -202,6 +202,9 @@ public class ChooserActivity extends ResolverActivity { 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 @@ public class ChooserActivity extends ResolverActivity { 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 @@ public class ChooserActivity extends ResolverActivity { } unbindRemainingServices(); + logDirectShareTargetReceived( + MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_CHOOSER_SERVICE); sendVoiceChoicesIfNeeded(); mChooserListAdapter.completeServiceTargetLoading(); break; @@ -305,6 +312,8 @@ public class ChooserActivity extends ResolverActivity { break; case SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED: + logDirectShareTargetReceived( + MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER); sendVoiceChoicesIfNeeded(); break; @@ -1155,6 +1164,8 @@ public class ChooserActivity extends ResolverActivity { } void queryTargetServices(ChooserListAdapter adapter) { + mQueriedTargetServicesTimeMs = System.currentTimeMillis(); + final PackageManager pm = getPackageManager(); ShortcutManager sm = (ShortcutManager) getSystemService(ShortcutManager.class); int targetsToQuery = 0; @@ -1281,6 +1292,7 @@ public class ChooserActivity extends ResolverActivity { private void queryDirectShareTargets( ChooserListAdapter adapter, boolean skipAppPredictionService) { + mQueriedSharingShortcutsTimeMs = System.currentTimeMillis(); if (!skipAppPredictionService) { AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); if (appPredictor != null) { @@ -1391,6 +1403,14 @@ public class ChooserActivity extends ResolverActivity { // 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 65338cb2126f..af8c631b0cc5 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -263,6 +263,14 @@ message MetricsEvent { 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 @@ message MetricsEvent { // 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 9782648efb6b..eec4b70880a5 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 @@ class ShortcutPackage extends ShortcutPackageItem { 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 @@ class ShortcutPackage extends ShortcutPackageItem { } /** + * 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 daef4e064004..2d8a2acd575f 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 android.view.IWindowManager; 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 @@ public class ShortcutService extends IShortcutService.Stub { @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 @@ public class ShortcutService extends IShortcutService.Stub { 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 1fd9b69e521d..8c207a8e1a6f 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.Nullable; 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.Log; 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 @@ class ShortcutUser { 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 6845f15f6a28..b806180a8584 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 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } /** + * 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 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } /** + * @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 eb4db7a0f4af..ba26f7930dcb 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 @@ package com.android.server.pm; 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 android.test.suitebuilder.annotation.SmallTest; 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 @@ public class ShortcutManagerTest3 extends BaseShortcutManagerTest { 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 @@ public class ShortcutManagerTest3 extends BaseShortcutManagerTest { 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()); + } } |