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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user