Collects metrics for the new Sharing Shortcuts api
Bug: 122904954
Test: atest com.android.server.pm.ShortcutManagerTest1 \
com.android.server.pm.ShortcutManagerTest2 \
com.android.server.pm.ShortcutManagerTest3 \
com.android.server.pm.ShortcutManagerTest4 \
com.android.server.pm.ShortcutManagerTest5 \
com.android.server.pm.ShortcutManagerTest6 \
com.android.server.pm.ShortcutManagerTest7 \
com.android.server.pm.ShortcutManagerTest8 \
com.android.server.pm.ShortcutManagerTest9 \
com.android.server.pm.ShortcutManagerTest10
Change-Id: Ifafcaa789814618846d41431d6190e4d79e871e5
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -705,6 +708,45 @@ class ShortcutPackage extends ShortcutPackageItem {
|
||||
return !mShareTargets.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1583,6 +1583,22 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -1817,6 +1833,17 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
|
||||
return p == null ? null : p.getAllShareTargetsForTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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}.
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user