Add Sharing Histories to UsageStatsManager.

This CL is for Android O Smart-Sharing (b/30982298). By this CL, sharing
counts are logged with UsageStatsManager.

Bug: 30982298
Test: manual - shared images in Camera and texts in Chrome using a mobile device.

Change-Id: I0b4aa0506f99b3083d140a48f7b4bdd5b1c5afb6
This commit is contained in:
Kang Li
2016-11-14 14:38:25 -08:00
parent 2c0dc3e094
commit 53b4314ad9
12 changed files with 404 additions and 8 deletions

View File

@@ -34,4 +34,6 @@ interface IUsageStatsManager {
boolean isAppInactive(String packageName, int userId);
void whitelistAppTemporarily(String packageName, long duration, int userId);
void onCarrierPrivilegedAppsChanged();
void reportChooserSelection(String packageName, int userId, String contentType,
in String[] annotations, String action);
}

View File

@@ -85,6 +85,12 @@ public final class UsageEvents implements Parcelable {
*/
public static final int SHORTCUT_INVOCATION = 8;
/**
* An event type denoting that a package was selected by the user for ChooserActivity.
* @hide
*/
public static final int CHOOSER_ACTION = 9;
/**
* {@hide}
*/
@@ -118,6 +124,27 @@ public final class UsageEvents implements Parcelable {
*/
public String mShortcutId;
/**
* Action type passed to ChooserActivity
* Only present for {@link #CHOOSER_ACTION} event types.
* {@hide}
*/
public String mAction;
/**
* Content type passed to ChooserActivity.
* Only present for {@link #CHOOSER_ACTION} event types.
* {@hide}
*/
public String mContentType;
/**
* Content annotations passed to ChooserActivity.
* Only present for {@link #CHOOSER_ACTION} event types.
* {@hide}
*/
public String[] mContentAnnotations;
/**
* The package name of the source of this event.
*/
@@ -307,6 +334,11 @@ public final class UsageEvents implements Parcelable {
case Event.SHORTCUT_INVOCATION:
p.writeString(event.mShortcutId);
break;
case Event.CHOOSER_ACTION:
p.writeString(event.mAction);
p.writeString(event.mContentType);
p.writeStringArray(event.mContentAnnotations);
break;
}
}
@@ -333,6 +365,9 @@ public final class UsageEvents implements Parcelable {
// Fill out the event-dependant fields.
eventOut.mConfiguration = null;
eventOut.mShortcutId = null;
eventOut.mAction = null;
eventOut.mContentType = null;
eventOut.mContentAnnotations = null;
switch (eventOut.mEventType) {
case Event.CONFIGURATION_CHANGE:
@@ -342,6 +377,11 @@ public final class UsageEvents implements Parcelable {
case Event.SHORTCUT_INVOCATION:
eventOut.mShortcutId = p.readString();
break;
case Event.CHOOSER_ACTION:
eventOut.mAction = p.readString();
eventOut.mContentType = p.readString();
eventOut.mContentAnnotations = p.createStringArray();
break;
}
}

View File

@@ -16,8 +16,10 @@
package android.app.usage;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
/**
* Contains usage statistics for an app package for a specific
@@ -61,6 +63,11 @@ public final class UsageStats implements Parcelable {
*/
public int mLastEvent;
/**
* {@hide}
*/
public ArrayMap<String, ArrayMap<String, Integer>> mChooserCounts;
/**
* {@hide}
*/
@@ -75,6 +82,7 @@ public final class UsageStats implements Parcelable {
mTotalTimeInForeground = stats.mTotalTimeInForeground;
mLaunchCount = stats.mLaunchCount;
mLastEvent = stats.mLastEvent;
mChooserCounts = stats.mChooserCounts;
}
public String getPackageName() {
@@ -142,6 +150,26 @@ public final class UsageStats implements Parcelable {
mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
mTotalTimeInForeground += right.mTotalTimeInForeground;
mLaunchCount += right.mLaunchCount;
if (mChooserCounts == null) {
mChooserCounts = right.mChooserCounts;
} else if (right.mChooserCounts != null) {
final int chooserCountsSize = right.mChooserCounts.size();
for (int i = 0; i < chooserCountsSize; i++) {
String action = right.mChooserCounts.keyAt(i);
ArrayMap<String, Integer> counts = right.mChooserCounts.valueAt(i);
if (!mChooserCounts.containsKey(action) || mChooserCounts.get(action) == null) {
mChooserCounts.put(action, counts);
continue;
}
final int annotationSize = counts.size();
for (int j = 0; j < annotationSize; j++) {
String key = counts.keyAt(j);
int rightValue = counts.valueAt(j);
int leftValue = mChooserCounts.get(action).getOrDefault(key, 0);
mChooserCounts.get(action).put(key, leftValue + rightValue);
}
}
}
}
@Override
@@ -158,6 +186,21 @@ public final class UsageStats implements Parcelable {
dest.writeLong(mTotalTimeInForeground);
dest.writeInt(mLaunchCount);
dest.writeInt(mLastEvent);
Bundle allCounts = new Bundle();
if (mChooserCounts != null) {
final int chooserCountSize = mChooserCounts.size();
for (int i = 0; i < chooserCountSize; i++) {
String action = mChooserCounts.keyAt(i);
ArrayMap<String, Integer> counts = mChooserCounts.valueAt(i);
Bundle currentCounts = new Bundle();
final int annotationSize = counts.size();
for (int j = 0; j < annotationSize; j++) {
currentCounts.putInt(counts.keyAt(j), counts.valueAt(j));
}
allCounts.putBundle(action, currentCounts);
}
}
dest.writeBundle(allCounts);
}
public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
@@ -171,6 +214,25 @@ public final class UsageStats implements Parcelable {
stats.mTotalTimeInForeground = in.readLong();
stats.mLaunchCount = in.readInt();
stats.mLastEvent = in.readInt();
Bundle allCounts = in.readBundle();
if (allCounts != null) {
stats.mChooserCounts = new ArrayMap<>();
for (String action : allCounts.keySet()) {
if (!stats.mChooserCounts.containsKey(action)) {
ArrayMap<String, Integer> newCounts = new ArrayMap<>();
stats.mChooserCounts.put(action, newCounts);
}
Bundle currentCounts = allCounts.getBundle(action);
if (currentCounts != null) {
for (String key : currentCounts.keySet()) {
int value = currentCounts.getInt(key);
if (value > 0) {
stats.mChooserCounts.get(action).put(key, value);
}
}
}
}
}
return stats;
}

View File

@@ -278,4 +278,23 @@ public final class UsageStatsManager {
} catch (RemoteException re) {
}
}
/**
* Reports a Chooser action to the UsageStatsManager.
*
* @param packageName The package name of the app that is selected.
* @param userId The user id of who makes the selection.
* @param contentType The type of the content, e.g., Image, Video, App.
* @param annotations The annotations of the content, e.g., Game, Selfie.
* @param action The action type of Intent that invokes ChooserActivity.
* {@link UsageEvents}
* @hide
*/
public void reportChooserSelection(String packageName, int userId, String contentType,
String[] annotations, String action) {
try {
mService.reportChooserSelection(packageName, userId, contentType, annotations, action);
} catch (RemoteException re) {
}
}
}

View File

@@ -19,6 +19,7 @@ package com.android.internal.app;
import android.animation.ObjectAnimator;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.usage.UsageStatsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -398,6 +399,7 @@ public class ChooserActivity extends ResolverActivity {
}
}
}
updateChooserCounts(target, mContentType);
return super.onTargetSelected(target, alwaysCheck);
}
@@ -542,20 +544,46 @@ public class ChooserActivity extends ResolverActivity {
// Do nothing. We'll send the voice stuff ourselves.
}
void updateChooserCounts(TargetInfo info, String annotation) {
if (info != null) {
UsageStatsManager usageStatsManager =
(UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
if (usageStatsManager == null) {
if (DEBUG) {
Log.d(TAG, "Can not start UsageStatsManager");
}
return;
}
final ResolveInfo ri = info.getResolveInfo();
if (ri != null && ri.activityInfo != null) {
usageStatsManager.reportChooserSelection(ri.activityInfo.packageName, getUserId(),
annotation, null, info.getResolvedIntent().getAction());
if (DEBUG) {
Log.d(TAG, "ResolveInfo Package is" + ri.activityInfo.packageName);
}
} else if(DEBUG) {
Log.d(TAG, "Can not log Chooser Counts of null ResovleInfo");
}
}
}
void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
if (mRefinementResultReceiver != null) {
mRefinementResultReceiver.destroy();
mRefinementResultReceiver = null;
}
if (selectedTarget == null) {
Log.e(TAG, "Refinement result intent did not match any known targets; canceling");
} else if (!checkTargetSourceIntent(selectedTarget, matchingIntent)) {
Log.e(TAG, "onRefinementResult: Selected target " + selectedTarget
+ " cannot match refined source intent " + matchingIntent);
} else if (super.onTargetSelected(selectedTarget.cloneFilledIn(matchingIntent, 0), false)) {
finish();
return;
} else {
TargetInfo clonedTarget = selectedTarget.cloneFilledIn(matchingIntent, 0);
if (super.onTargetSelected(clonedTarget, false)) {
updateChooserCounts(clonedTarget, mContentType);
finish();
return;
}
}
onRefinementCanceled();
}

View File

@@ -26,6 +26,7 @@ import android.app.VoiceInteractor.PickOptionRequest.Option;
import android.app.VoiceInteractor.Prompt;
import android.content.pm.ComponentInfo;
import android.os.AsyncTask;
import android.os.RemoteException;
import android.provider.MediaStore;
import android.provider.Settings;
import android.text.TextUtils;
@@ -53,7 +54,6 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.UserHandle;
import android.os.UserManager;
@@ -114,6 +114,7 @@ public class ResolverActivity extends Activity {
private ComponentName[] mFilteredComponents;
protected ResolverDrawerLayout mResolverDrawerLayout;
protected String mContentType;
private boolean mRegistered;
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@@ -270,6 +271,7 @@ public class ResolverActivity extends Activity {
final String referrerPackage = getReferrerPackageName();
mResolverComparator = new ResolverComparator(this, getTargetIntent(), referrerPackage);
mContentType = mResolverComparator.mContentType;
if (configureContentView(mIntents, initialIntents, rList, alwaysUseOption)) {
return;

View File

@@ -63,6 +63,8 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
private final long mSinceTime;
private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>();
private final String mReferrerPackage;
public String mContentType;
private String mAction;
public ResolverComparator(Context context, Intent intent, String referrerPackage) {
mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
@@ -76,6 +78,8 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
mCurrentTime = System.currentTimeMillis();
mSinceTime = mCurrentTime - USAGE_STATS_PERIOD;
mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime);
mContentType = intent.getType();
mAction = intent.getAction();
}
public void compute(List<ResolvedComponentInfo> targets) {
@@ -86,6 +90,7 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
long mostRecentlyUsedTime = recentSinceTime + 1;
long mostTimeSpent = 1;
int mostLaunched = 1;
int mostSelected = 1;
for (ResolvedComponentInfo target : targets) {
final ScoredTarget scoredTarget
@@ -114,6 +119,25 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
if (launched > mostLaunched) {
mostLaunched = launched;
}
// TODO(kanlig): get and combine counts of categories.
int selected = 0;
if (pkStats.mChooserCounts != null && mAction != null
&& pkStats.mChooserCounts.get(mAction) != null) {
selected = pkStats.mChooserCounts.get(mAction).getOrDefault(mContentType, 0);
}
if (DEBUG) {
if (mAction == null) {
Log.d(TAG, "Action type is null");
} else {
Log.d(TAG, "Chooser Count of " + mAction + ":" +
target.name.getPackageName() + " is " + Integer.toString(selected));
}
}
scoredTarget.chooserCount = selected;
if (selected > mostSelected) {
mostSelected = selected;
}
}
}
@@ -190,7 +214,15 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
lhs.activityInfo.packageName, lhs.activityInfo.name));
final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName(
rhs.activityInfo.packageName, rhs.activityInfo.name));
final float diff = rhsTarget.score - lhsTarget.score;
final int chooserCountDiff = Long.compare(
rhsTarget.chooserCount, lhsTarget.chooserCount);
if (chooserCountDiff != 0) {
return chooserCountDiff > 0 ? 1 : -1;
}
final int diff = Float.compare(rhsTarget.score, lhsTarget.score);
if (diff != 0) {
return diff > 0 ? 1 : -1;
@@ -220,6 +252,7 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
public long lastTimeUsed;
public long timeSpent;
public long launchCount;
public long chooserCount;
public ScoredTarget(ComponentInfo ci) {
componentInfo = ci;
@@ -232,6 +265,7 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
+ " lastTimeUsed: " + lastTimeUsed
+ " timeSpent: " + timeSpent
+ " launchCount: " + launchCount
+ " chooserCount: " + chooserCount
+ "}";
}
}

View File

@@ -121,6 +121,23 @@ class IntervalStats {
endTime = timeStamp;
}
void updateChooserCounts(String packageName, String category, String action) {
UsageStats usageStats = getOrCreateUsageStats(packageName);
if (usageStats.mChooserCounts == null) {
usageStats.mChooserCounts = new ArrayMap<>();
}
ArrayMap<String, Integer> chooserCounts;
final int idx = usageStats.mChooserCounts.indexOfKey(action);
if (idx < 0) {
chooserCounts = new ArrayMap<>();
usageStats.mChooserCounts.put(action, chooserCounts);
} else {
chooserCounts = usageStats.mChooserCounts.valueAt(idx);
}
int currentCount = chooserCounts.getOrDefault(category, 0);
chooserCounts.put(category, currentCount + 1);
}
void updateConfigurationStats(Configuration config, long timeStamp) {
if (activeConfiguration != null) {
ConfigurationStats activeStats = configurations.get(activeConfiguration);

View File

@@ -17,6 +17,7 @@
package com.android.server.usage;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.os.Build;
import android.util.AtomicFile;
@@ -502,6 +503,12 @@ class UsageStatsDatabase {
pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_DAILY],
mCal.getTimeInMillis());
mCal.setTimeInMillis(currentTimeMillis);
mCal.addDays(-14);
for (int i = 0; i < mIntervalDirs.length; ++i) {
pruneChooserCountsOlderThan(mIntervalDirs[i], mCal.getTimeInMillis());
}
// We must re-index our file list or we will be trying to read
// deleted files.
indexFilesLocked();
@@ -531,6 +538,43 @@ class UsageStatsDatabase {
}
}
private static void pruneChooserCountsOlderThan(File dir, long expiryTime) {
File[] files = dir.listFiles();
if (files != null) {
for (File f : files) {
String path = f.getPath();
if (path.endsWith(BAK_SUFFIX)) {
f = new File(path.substring(0, path.length() - BAK_SUFFIX.length()));
}
long beginTime;
try {
beginTime = UsageStatsXml.parseBeginTime(f);
} catch (IOException e) {
beginTime = 0;
}
if (beginTime < expiryTime) {
try {
final AtomicFile af = new AtomicFile(f);
final IntervalStats stats = new IntervalStats();
UsageStatsXml.read(af, stats);
final int pkgCount = stats.packageStats.size();
for (int i = 0; i < pkgCount; i++) {
UsageStats pkgStats = stats.packageStats.valueAt(i);
if (pkgStats.mChooserCounts != null) {
pkgStats.mChooserCounts.clear();
}
}
UsageStatsXml.write(af, stats);
} catch (IOException e) {
Slog.e(TAG, "Failed to delete chooser counts from usage stats file", e);
}
}
}
}
}
/**
* Update the stats in the database. They may not be written to disk immediately.
*/

View File

@@ -1379,6 +1379,31 @@ public class UsageStatsService extends SystemService implements
}
UsageStatsService.this.dump(args, pw);
}
@Override
public void reportChooserSelection(String packageName, int userId, String contentType,
String[] annotations, String action) {
if (packageName == null) {
Slog.w(TAG, "Event report user selecting a null package");
return;
}
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = packageName;
// This will later be converted to system time.
event.mTimeStamp = SystemClock.elapsedRealtime();
event.mEventType = Event.CHOOSER_ACTION;
event.mAction = action;
event.mContentType = contentType;
event.mContentAnnotations = annotations;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
}
/**

View File

@@ -26,6 +26,7 @@ import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
import android.util.ArrayMap;
import java.io.IOException;
import java.net.ProtocolException;
@@ -37,6 +38,11 @@ final class UsageStatsXmlV1 {
private static final String PACKAGES_TAG = "packages";
private static final String PACKAGE_TAG = "package";
private static final String CHOOSER_COUNT_TAG = "chosen_action";
private static final String CATEGORY_TAG = "category";
private static final String NAME = "name";
private static final String COUNT = "count";
private static final String CONFIGURATIONS_TAG = "configurations";
private static final String CONFIG_TAG = "config";
@@ -59,7 +65,7 @@ final class UsageStatsXmlV1 {
private static final String TIME_ATTR = "time";
private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
throws IOException {
throws XmlPullParserException, IOException {
final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR);
if (pkg == null) {
throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
@@ -72,6 +78,51 @@ final class UsageStatsXmlV1 {
parser, LAST_TIME_ACTIVE_ATTR);
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
int eventCode;
while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT) {
final String tag = parser.getName();
if (eventCode == XmlPullParser.END_TAG && tag.equals(PACKAGE_TAG)) {
break;
}
if (eventCode != XmlPullParser.START_TAG) {
continue;
}
if (tag.equals(CHOOSER_COUNT_TAG)) {
String action = XmlUtils.readStringAttribute(parser, NAME);
loadChooserCounts(parser, stats, action);
}
}
}
private static void loadChooserCounts(
XmlPullParser parser, UsageStats usageStats, String action)
throws XmlPullParserException, IOException {
if (action == null) {
return;
}
if (usageStats.mChooserCounts == null) {
usageStats.mChooserCounts = new ArrayMap<>();
}
if (!usageStats.mChooserCounts.containsKey(action)) {
ArrayMap<String, Integer> counts = new ArrayMap<>();
usageStats.mChooserCounts.put(action, counts);
}
int eventCode;
while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT) {
final String tag = parser.getName();
if (eventCode == XmlPullParser.END_TAG && tag.equals(CHOOSER_COUNT_TAG)) {
break;
}
if (eventCode != XmlPullParser.START_TAG) {
continue;
}
if (tag.equals(CATEGORY_TAG)) {
String category = XmlUtils.readStringAttribute(parser, NAME);
int count = XmlUtils.readIntAttribute(parser, COUNT);
usageStats.mChooserCounts.get(action).put(category, count);
}
}
}
private static void loadConfigStats(XmlPullParser parser, IntervalStats statsOut)
@@ -135,10 +186,45 @@ final class UsageStatsXmlV1 {
XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
writeChooserCounts(xml, usageStats);
xml.endTag(null, PACKAGE_TAG);
}
private static void writeChooserCounts(XmlSerializer xml, final UsageStats usageStats)
throws IOException {
if (usageStats == null || usageStats.mChooserCounts == null ||
usageStats.mChooserCounts.keySet().isEmpty()) {
return;
}
final int chooserCountSize = usageStats.mChooserCounts.size();
for (int i = 0; i < chooserCountSize; i++) {
final String action = usageStats.mChooserCounts.keyAt(i);
final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i);
if (action == null || counts == null || counts.isEmpty()) {
continue;
}
xml.startTag(null, CHOOSER_COUNT_TAG);
XmlUtils.writeStringAttribute(xml, NAME, action);
writeCountsForAction(xml, counts);
xml.endTag(null, CHOOSER_COUNT_TAG);
}
}
private static void writeCountsForAction(XmlSerializer xml, ArrayMap<String, Integer> counts)
throws IOException {
final int countsSize = counts.size();
for (int i = 0; i < countsSize; i++) {
String key = counts.keyAt(i);
int count = counts.valueAt(i);
if (count > 0) {
xml.startTag(null, CATEGORY_TAG);
XmlUtils.writeStringAttribute(xml, NAME, key);
XmlUtils.writeIntAttribute(xml, COUNT, count);
xml.endTag(null, CATEGORY_TAG);
}
}
}
private static void writeConfigStats(XmlSerializer xml, final IntervalStats stats,
final ConfigurationStats configStats, boolean isActive) throws IOException {
xml.startTag(null, CONFIG_TAG);

View File

@@ -183,6 +183,15 @@ class UserUsageStatsService {
for (IntervalStats stats : mCurrentStats) {
if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
} else if (event.mEventType == UsageEvents.Event.CHOOSER_ACTION) {
stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
String[] annotations = event.mContentAnnotations;
if (annotations != null) {
for (String annotation : annotations) {
// TODO(kanlig): update with confidences of annotations.
stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
}
}
} else {
stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
}
@@ -520,6 +529,32 @@ class UserUsageStatsService {
}
pw.decreaseIndent();
pw.println();
pw.increaseIndent();
pw.println("ChooserCounts");
pw.increaseIndent();
for (UsageStats usageStats : pkgStats.values()) {
pw.printPair("package", usageStats.mPackageName);
if (usageStats.mChooserCounts != null) {
final int chooserCountSize = usageStats.mChooserCounts.size();
for (int i = 0; i < chooserCountSize; i++) {
final String action = usageStats.mChooserCounts.keyAt(i);
final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i);
final int annotationSize = counts.size();
for (int j = 0; j < annotationSize; j++) {
final String key = counts.keyAt(j);
final int count = counts.valueAt(j);
if (count != 0) {
pw.printPair("ChooserCounts", action + ":" + key + " is " +
Integer.toString(count));
pw.println();
}
}
}
}
pw.println();
}
pw.println("configurations");
pw.increaseIndent();
final ArrayMap<Configuration, ConfigurationStats> configStats = stats.configurations;
@@ -593,6 +628,8 @@ class UserUsageStatsService {
return "USER_INTERACTION";
case UsageEvents.Event.SHORTCUT_INVOCATION:
return "SHORTCUT_INVOCATION";
case UsageEvents.Event.CHOOSER_ACTION:
return "CHOOSER_ACTION";
default:
return "UNKNOWN";
}