blob: eb90b753a4ae88a4fc6069a7c1a9867fbfa6e3db [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.documentsui;
import static com.android.documentsui.Shared.DEBUG;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.util.Log;
import com.android.documentsui.model.RootInfo;
import com.android.internal.logging.MetricsLogger;
/** @hide */
public final class Metrics {
private static final String TAG = "Metrics";
// These are the native provider authorities that the metrics code is capable of recognizing and
// explicitly counting.
private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
private static final String AUTHORITY_STORAGE = "com.android.externalstorage.documents";
private static final String AUTHORITY_DOWNLOADS = "com.android.providers.downloads.documents";
// These strings have to be whitelisted in tron. Do not change them.
private static final String COUNT_LAUNCH_ACTION = "docsui_launch_action";
private static final String COUNT_ROOT_VISITED = "docsui_root_visited";
private static final String COUNT_OPEN_MIME = "docsui_open_mime";
private static final String COUNT_CREATE_MIME = "docsui_create_mime";
private static final String COUNT_GET_CONTENT_MIME = "docsui_get_content_mime";
private static final String COUNT_BROWSE_ROOT = "docsui_browse_root";
private static final String COUNT_MANAGE_ROOT = "docsui_manage_root";
private static final String COUNT_MULTI_WINDOW = "docsui_multi_window";
// Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
// root that is not explicitly recognized by the Metrics code (see {@link
// #getSanitizedRootIndex}). Apps are also bucketed in this histogram using negative indices
// (see below).
private static final int ROOT_NONE = 0;
private static final int ROOT_OTHER = 1;
private static final int ROOT_AUDIO = 2;
private static final int ROOT_DEVICE_STORAGE = 3;
private static final int ROOT_DOWNLOADS = 4;
private static final int ROOT_HOME = 5;
private static final int ROOT_IMAGES = 6;
private static final int ROOT_RECENTS = 7;
private static final int ROOT_VIDEOS = 8;
// Apps aren't really "roots", but they are treated as such in the roots fragment UI and so they
// are logged analogously to roots. Use negative numbers to identify apps.
private static final int ROOT_THIRD_PARTY_APP = -1;
// Indices for bucketing mime types.
private static final int MIME_OTHER = -2; // anything not enumerated below
private static final int MIME_NONE = -1; // null mime
private static final int MIME_ANY = 0; // */*
private static final int MIME_APPLICATION = 1; // application/*
private static final int MIME_AUDIO = 2; // audio/*
private static final int MIME_IMAGE = 3; // image/*
private static final int MIME_MESSAGE = 4; // message/*
private static final int MIME_MULTIPART = 5; // multipart/*
private static final int MIME_TEXT = 6; // text/*
private static final int MIME_VIDEO = 7; // video/*
/**
* Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up.
*
* @param context
* @param state
* @param intent
*/
public static void logActivityLaunch(Context context, State state, Intent intent) {
// Log the launch action.
logHistogram(context, COUNT_LAUNCH_ACTION, state.action);
// Then log auxiliary data (roots/mime types) associated with some actions.
Uri uri = intent.getData();
switch (state.action) {
case State.ACTION_OPEN:
logHistogram(context, COUNT_OPEN_MIME, sanitizeMime(intent.getType()));
break;
case State.ACTION_CREATE:
logHistogram(context, COUNT_CREATE_MIME, sanitizeMime(intent.getType()));
break;
case State.ACTION_GET_CONTENT:
logHistogram(context, COUNT_GET_CONTENT_MIME, sanitizeMime(intent.getType()));
break;
case State.ACTION_MANAGE:
logHistogram(context, COUNT_MANAGE_ROOT, sanitizeRoot(uri));
break;
case State.ACTION_BROWSE:
logHistogram(context, COUNT_BROWSE_ROOT, sanitizeRoot(uri));
break;
default:
break;
}
}
/**
* Logs a root visited event. Call this when the user clicks on a root in the RootsFragment.
*
* @param context
* @param info
*/
public static void logRootVisited(Context context, RootInfo info) {
logHistogram(context, COUNT_ROOT_VISITED, sanitizeRoot(info));
}
/**
* Logs an app visited event. Call this when the user clicks on an app in the RootsFragment.
*
* @param context
* @param info
*/
public static void logAppVisited(Context context, ResolveInfo info) {
logHistogram(context, COUNT_ROOT_VISITED, sanitizeRoot(info));
}
/**
* Logs a multi-window start. Call this when the user spawns a new DocumentsUI window.
*
* @param context
*/
public static void logMultiWindow(Context context) {
logCount(context, COUNT_MULTI_WINDOW);
}
/**
* Internal method for making a MetricsLogger.count call. Increments the given counter by 1.
*
* @param context
* @param name The counter to increment.
*/
private static void logCount(Context context, String name) {
if (DEBUG) Log.d(TAG, name + ": " + 1);
MetricsLogger.count(context, name, 1);
}
/**
* Internal method for making a MetricsLogger.histogram call.
*
* @param context
* @param name The name of the histogram.
* @param bucket The bucket to increment.
*/
private static void logHistogram(Context context, String name, int bucket) {
if (DEBUG) Log.d(TAG, name + ": " + bucket);
MetricsLogger.histogram(context, name, bucket);
}
/**
* Generates an integer identifying the given root. For privacy, this function only recognizes a
* small set of hard-coded roots (ones provided by the system). Other roots are all grouped into
* a single ROOT_OTHER bucket.
*/
private static int sanitizeRoot(Uri uri) {
if (LauncherActivity.isLaunchUri(uri)) {
return ROOT_NONE;
}
switch (uri.getAuthority()) {
case AUTHORITY_MEDIA:
switch (DocumentsContract.getRootId(uri)) {
case "audio_root":
return ROOT_AUDIO;
case "images_root":
return ROOT_IMAGES;
case "videos_root":
return ROOT_VIDEOS;
default:
return ROOT_OTHER;
}
case AUTHORITY_STORAGE:
if ("home".equals(DocumentsContract.getRootId(uri))) {
return ROOT_HOME;
} else {
return ROOT_DEVICE_STORAGE;
}
case AUTHORITY_DOWNLOADS:
return ROOT_DOWNLOADS;
default:
return ROOT_OTHER;
}
}
/** @see #sanitizeRoot(Uri) */
private static int sanitizeRoot(RootInfo root) {
if (root.isRecents()) {
// Recents root is special and only identifiable via this method call. Other roots are
// identified by URI.
return ROOT_RECENTS;
} else {
return sanitizeRoot(root.getUri());
}
}
/** @see #sanitizeRoot(Uri) */
private static int sanitizeRoot(ResolveInfo info) {
// Log all apps under a single bucket in the roots histogram.
return ROOT_THIRD_PARTY_APP;
}
/**
* Generates an int identifying a mime type. For privacy, this function only recognizes a small
* set of hard-coded types. For any other type, this function returns "other".
*
* @param mimeType
* @return
*/
private static int sanitizeMime(String mimeType) {
if (mimeType == null) {
return MIME_NONE;
} else if ("*/*".equals(mimeType)) {
return MIME_ANY;
} else {
String type = mimeType.substring(0, mimeType.indexOf('/'));
switch (type) {
case "application":
return MIME_APPLICATION;
case "audio":
return MIME_AUDIO;
case "image":
return MIME_IMAGE;
case "message":
return MIME_MESSAGE;
case "multipart":
return MIME_MULTIPART;
case "text":
return MIME_TEXT;
case "video":
return MIME_VIDEO;
}
}
// Bucket all other types into one bucket.
return MIME_OTHER;
}
}