blob: dcaea15459a630a97a970b4bcb71c5a074397713 [file] [log] [blame]
Ben Kwa1c0a3892016-01-26 11:50:03 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.documentsui;
18
Felipe Lemee4b60122016-02-24 10:17:41 -080019import static android.os.Environment.STANDARD_DIRECTORIES;
Ben Kwa1c0a3892016-01-26 11:50:03 -080020import static com.android.documentsui.Shared.DEBUG;
Ben Kwafaa27202016-01-28 16:39:57 -080021import android.annotation.IntDef;
22import android.annotation.Nullable;
Felipe Lemee4b60122016-02-24 10:17:41 -080023import android.annotation.StringDef;
24import android.app.Activity;
Ben Kwa1c0a3892016-01-26 11:50:03 -080025import android.content.Context;
26import android.content.Intent;
27import android.content.pm.ResolveInfo;
28import android.net.Uri;
29import android.provider.DocumentsContract;
30import android.util.Log;
31
Ben Kwafaa27202016-01-28 16:39:57 -080032import com.android.documentsui.model.DocumentInfo;
Ben Kwa1c0a3892016-01-26 11:50:03 -080033import com.android.documentsui.model.RootInfo;
Ben Kwafaa27202016-01-28 16:39:57 -080034import com.android.documentsui.services.FileOperationService;
35import com.android.documentsui.services.FileOperationService.OpType;
Ben Kwa1c0a3892016-01-26 11:50:03 -080036import com.android.internal.logging.MetricsLogger;
Felipe Lemee4b60122016-02-24 10:17:41 -080037import com.android.internal.logging.MetricsProto.MetricsEvent;
Ben Kwa1c0a3892016-01-26 11:50:03 -080038
Ben Kwafaa27202016-01-28 16:39:57 -080039import java.lang.annotation.Retention;
40import java.lang.annotation.RetentionPolicy;
41import java.util.List;
42
Ben Kwa1c0a3892016-01-26 11:50:03 -080043/** @hide */
44public final class Metrics {
45 private static final String TAG = "Metrics";
46
47 // These are the native provider authorities that the metrics code is capable of recognizing and
48 // explicitly counting.
49 private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
50 private static final String AUTHORITY_STORAGE = "com.android.externalstorage.documents";
51 private static final String AUTHORITY_DOWNLOADS = "com.android.providers.downloads.documents";
Ben Kwaebaaea42016-01-28 18:15:07 -080052 private static final String AUTHORITY_MTP = "com.android.mtp.documents";
Ben Kwa1c0a3892016-01-26 11:50:03 -080053
54 // These strings have to be whitelisted in tron. Do not change them.
55 private static final String COUNT_LAUNCH_ACTION = "docsui_launch_action";
56 private static final String COUNT_ROOT_VISITED = "docsui_root_visited";
57 private static final String COUNT_OPEN_MIME = "docsui_open_mime";
58 private static final String COUNT_CREATE_MIME = "docsui_create_mime";
59 private static final String COUNT_GET_CONTENT_MIME = "docsui_get_content_mime";
60 private static final String COUNT_BROWSE_ROOT = "docsui_browse_root";
61 private static final String COUNT_MANAGE_ROOT = "docsui_manage_root";
62 private static final String COUNT_MULTI_WINDOW = "docsui_multi_window";
Ben Kwafaa27202016-01-28 16:39:57 -080063 private static final String COUNT_FILEOP_SYSTEM = "docsui_fileop_system";
64 private static final String COUNT_FILEOP_EXTERNAL = "docsui_fileop_external";
65 private static final String COUNT_FILEOP_CANCELED = "docsui_fileop_canceled";
Ben Kwa1c0a3892016-01-26 11:50:03 -080066
67 // Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
68 // root that is not explicitly recognized by the Metrics code (see {@link
Ben Kwab41a5ed2016-02-17 16:06:22 -080069 // #getSanitizedRootIndex}). Apps are also bucketed in this histogram.
Ben Kwaebaaea42016-01-28 18:15:07 -080070 // Do not change or rearrange these values, that will break historical data. Only add to the end
71 // of the list.
Ben Kwab41a5ed2016-02-17 16:06:22 -080072 // Do not use negative numbers or zero; clearcut only handles positive integers.
73 private static final int ROOT_NONE = 1;
74 private static final int ROOT_OTHER = 2;
75 private static final int ROOT_AUDIO = 3;
76 private static final int ROOT_DEVICE_STORAGE = 4;
77 private static final int ROOT_DOWNLOADS = 5;
78 private static final int ROOT_HOME = 6;
79 private static final int ROOT_IMAGES = 7;
80 private static final int ROOT_RECENTS = 8;
81 private static final int ROOT_VIDEOS = 9;
82 private static final int ROOT_MTP = 10;
Ben Kwa1c0a3892016-01-26 11:50:03 -080083 // Apps aren't really "roots", but they are treated as such in the roots fragment UI and so they
Ben Kwab41a5ed2016-02-17 16:06:22 -080084 // are logged analogously to roots.
85 private static final int ROOT_THIRD_PARTY_APP = 100;
Ben Kwa1c0a3892016-01-26 11:50:03 -080086
Ben Kwafaa27202016-01-28 16:39:57 -080087 @IntDef(flag = true, value = {
88 ROOT_NONE,
89 ROOT_OTHER,
90 ROOT_AUDIO,
91 ROOT_DEVICE_STORAGE,
92 ROOT_DOWNLOADS,
93 ROOT_HOME,
94 ROOT_IMAGES,
95 ROOT_RECENTS,
96 ROOT_VIDEOS,
Ben Kwaebaaea42016-01-28 18:15:07 -080097 ROOT_MTP,
Ben Kwafaa27202016-01-28 16:39:57 -080098 ROOT_THIRD_PARTY_APP
99 })
100 @Retention(RetentionPolicy.SOURCE)
101 public @interface Root {}
102
Ben Kwa1c0a3892016-01-26 11:50:03 -0800103 // Indices for bucketing mime types.
Ben Kwab41a5ed2016-02-17 16:06:22 -0800104 // Do not change or rearrange these values, that will break historical data. Only add to the end
105 // of the list.
106 // Do not use negative numbers or zero; clearcut only handles positive integers.
107 private static final int MIME_NONE = 1; // null mime
108 private static final int MIME_ANY = 2; // */*
109 private static final int MIME_APPLICATION = 3; // application/*
110 private static final int MIME_AUDIO = 4; // audio/*
111 private static final int MIME_IMAGE = 5; // image/*
112 private static final int MIME_MESSAGE = 6; // message/*
113 private static final int MIME_MULTIPART = 7; // multipart/*
114 private static final int MIME_TEXT = 8; // text/*
115 private static final int MIME_VIDEO = 9; // video/*
116 private static final int MIME_OTHER = 10; // anything not enumerated below
Ben Kwa1c0a3892016-01-26 11:50:03 -0800117
Ben Kwafaa27202016-01-28 16:39:57 -0800118 @IntDef(flag = true, value = {
Ben Kwafaa27202016-01-28 16:39:57 -0800119 MIME_NONE,
120 MIME_ANY,
121 MIME_APPLICATION,
122 MIME_AUDIO,
123 MIME_IMAGE,
124 MIME_MESSAGE,
125 MIME_MULTIPART,
126 MIME_TEXT,
Ben Kwab41a5ed2016-02-17 16:06:22 -0800127 MIME_VIDEO,
128 MIME_OTHER
Ben Kwafaa27202016-01-28 16:39:57 -0800129 })
130 @Retention(RetentionPolicy.SOURCE)
131 public @interface Mime {}
132
133 // Codes representing different kinds of file operations. These are used for bucketing
134 // operations in the COUNT_FILEOP_{SYSTEM|EXTERNAL} histograms.
Ben Kwab41a5ed2016-02-17 16:06:22 -0800135 // Do not change or rearrange these values, that will break historical data. Only add to the
136 // list.
137 // Do not use negative numbers or zero; clearcut only handles positive integers.
138 private static final int FILEOP_OTHER = 1; // any file operation not listed below
139 private static final int FILEOP_COPY_INTRA_PROVIDER = 2; // Copy within a provider
140 private static final int FILEOP_COPY_SYSTEM_PROVIDER = 3; // Copy to a system provider.
141 private static final int FILEOP_COPY_EXTERNAL_PROVIDER = 4; // Copy to a 3rd-party provider.
142 private static final int FILEOP_MOVE_INTRA_PROVIDER = 5; // Move within a provider.
143 private static final int FILEOP_MOVE_SYSTEM_PROVIDER = 6; // Move to a system provider.
144 private static final int FILEOP_MOVE_EXTERNAL_PROVIDER = 7; // Move to a 3rd-party provider.
145 private static final int FILEOP_DELETE = 8;
146 private static final int FILEOP_OTHER_ERROR = 100;
147 private static final int FILEOP_DELETE_ERROR = 101;
148 private static final int FILEOP_MOVE_ERROR = 102;
149 private static final int FILEOP_COPY_ERROR = 103;
Ben Kwafaa27202016-01-28 16:39:57 -0800150
151 @IntDef(flag = true, value = {
152 FILEOP_OTHER,
153 FILEOP_COPY_INTRA_PROVIDER,
154 FILEOP_COPY_SYSTEM_PROVIDER,
155 FILEOP_COPY_EXTERNAL_PROVIDER,
156 FILEOP_MOVE_INTRA_PROVIDER,
157 FILEOP_MOVE_SYSTEM_PROVIDER,
158 FILEOP_MOVE_EXTERNAL_PROVIDER,
159 FILEOP_DELETE,
160 FILEOP_OTHER_ERROR,
161 FILEOP_COPY_ERROR,
162 FILEOP_MOVE_ERROR,
163 FILEOP_DELETE_ERROR
164 })
165 @Retention(RetentionPolicy.SOURCE)
166 public @interface FileOp {}
167
Ben Kwab41a5ed2016-02-17 16:06:22 -0800168 // Codes representing different kinds of file operations. These are used for bucketing
169 // operations in the COUNT_FILEOP_CANCELED histogram.
170 // Do not change or rearrange these values, that will break historical data. Only add to the
171 // list.
172 // Do not use negative numbers or zero; clearcut only handles positive integers.
173 private static final int OPERATION_UNKNOWN = 1;
174 private static final int OPERATION_COPY = 2;
175 private static final int OPERATION_MOVE = 3;
176 private static final int OPERATION_DELETE= 4;
177
178 @IntDef(flag = true, value = {
179 OPERATION_UNKNOWN,
180 OPERATION_COPY,
181 OPERATION_MOVE,
182 OPERATION_DELETE
183 })
184 @Retention(RetentionPolicy.SOURCE)
185 public @interface MetricsOpType {}
186
187 // Codes representing different launch actions. These are used for bucketing stats in the
188 // COUNT_LAUNCH_ACTION histogram.
189 // Do not change or rearrange these values, that will break historical data. Only add to the
190 // list.
191 // Do not use negative numbers or zero; clearcut only handles positive integers.
192 private static final int ACTION_OTHER = 1;
193 private static final int ACTION_OPEN = 2;
194 private static final int ACTION_CREATE = 3;
195 private static final int ACTION_GET_CONTENT = 4;
196 private static final int ACTION_OPEN_TREE = 5;
197 private static final int ACTION_MANAGE = 6;
198 private static final int ACTION_BROWSE = 7;
199 private static final int ACTION_PICK_COPY_DESTINATION = 8;
200
201 @IntDef(flag = true, value = {
202 ACTION_OTHER,
203 ACTION_OPEN,
204 ACTION_CREATE,
205 ACTION_GET_CONTENT,
206 ACTION_OPEN_TREE,
207 ACTION_MANAGE,
208 ACTION_BROWSE,
209 ACTION_PICK_COPY_DESTINATION
210 })
211 @Retention(RetentionPolicy.SOURCE)
212 public @interface MetricsAction {}
213
Ben Kwafaa27202016-01-28 16:39:57 -0800214 // Codes representing different provider types. Used for sorting file operations when logging.
215 private static final int PROVIDER_INTRA = 0;
216 private static final int PROVIDER_SYSTEM = 1;
217 private static final int PROVIDER_EXTERNAL = 2;
218
219 @IntDef(flag = true, value = {
220 PROVIDER_INTRA,
221 PROVIDER_SYSTEM,
222 PROVIDER_EXTERNAL
223 })
224 @Retention(RetentionPolicy.SOURCE)
225 public @interface Provider {}
226
Ben Kwa1c0a3892016-01-26 11:50:03 -0800227 /**
228 * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up.
229 *
230 * @param context
231 * @param state
232 * @param intent
233 */
234 public static void logActivityLaunch(Context context, State state, Intent intent) {
235 // Log the launch action.
Ben Kwab41a5ed2016-02-17 16:06:22 -0800236 logHistogram(context, COUNT_LAUNCH_ACTION, toMetricsAction(state.action));
Ben Kwa1c0a3892016-01-26 11:50:03 -0800237 // Then log auxiliary data (roots/mime types) associated with some actions.
238 Uri uri = intent.getData();
239 switch (state.action) {
240 case State.ACTION_OPEN:
241 logHistogram(context, COUNT_OPEN_MIME, sanitizeMime(intent.getType()));
242 break;
243 case State.ACTION_CREATE:
244 logHistogram(context, COUNT_CREATE_MIME, sanitizeMime(intent.getType()));
245 break;
246 case State.ACTION_GET_CONTENT:
247 logHistogram(context, COUNT_GET_CONTENT_MIME, sanitizeMime(intent.getType()));
248 break;
249 case State.ACTION_MANAGE:
250 logHistogram(context, COUNT_MANAGE_ROOT, sanitizeRoot(uri));
251 break;
252 case State.ACTION_BROWSE:
253 logHistogram(context, COUNT_BROWSE_ROOT, sanitizeRoot(uri));
254 break;
255 default:
256 break;
257 }
258 }
259
260 /**
261 * Logs a root visited event. Call this when the user clicks on a root in the RootsFragment.
262 *
263 * @param context
264 * @param info
265 */
266 public static void logRootVisited(Context context, RootInfo info) {
267 logHistogram(context, COUNT_ROOT_VISITED, sanitizeRoot(info));
268 }
269
270 /**
271 * Logs an app visited event. Call this when the user clicks on an app in the RootsFragment.
272 *
273 * @param context
274 * @param info
275 */
276 public static void logAppVisited(Context context, ResolveInfo info) {
277 logHistogram(context, COUNT_ROOT_VISITED, sanitizeRoot(info));
278 }
279
280 /**
281 * Logs a multi-window start. Call this when the user spawns a new DocumentsUI window.
282 *
283 * @param context
284 */
285 public static void logMultiWindow(Context context) {
286 logCount(context, COUNT_MULTI_WINDOW);
287 }
288
289 /**
Ben Kwafaa27202016-01-28 16:39:57 -0800290 * Logs file operation stats. Call this when a file operation has completed. The given
291 * DocumentInfo is only used to distinguish broad categories of actions (e.g. copying from one
292 * provider to another vs copying within a given provider). No PII is logged.
293 *
294 * @param context
295 * @param operationType
296 * @param srcs
297 * @param dst
298 */
299 public static void logFileOperation(
300 Context context,
301 @OpType int operationType,
302 List<DocumentInfo> srcs,
303 @Nullable DocumentInfo dst) {
304 ProviderCounts counts = countProviders(srcs, dst);
305
306 if (counts.intraProvider > 0) {
307 logIntraProviderFileOps(context, dst.authority, operationType);
308 }
309 if (counts.systemProvider > 0) {
310 // Log file operations on system providers.
311 logInterProviderFileOps(context, COUNT_FILEOP_SYSTEM, dst, operationType);
312 }
313 if (counts.externalProvider > 0) {
314 // Log file operations on external providers.
315 logInterProviderFileOps(context, COUNT_FILEOP_EXTERNAL, dst, operationType);
316 }
317 }
318
319 /**
320 * Logs some kind of file operation error. Call this when a file operation (e.g. copy, delete)
321 * fails.
322 *
323 * @param context
324 * @param operationType
325 * @param failedFiles
326 */
327 public static void logFileOperationErrors(Context context, @OpType int operationType,
328 List<DocumentInfo> failedFiles) {
329 ProviderCounts counts = countProviders(failedFiles, null);
330
331 @FileOp int opCode = FILEOP_OTHER_ERROR;
332 switch (operationType) {
333 case FileOperationService.OPERATION_COPY:
334 opCode = FILEOP_COPY_ERROR;
335 break;
336 case FileOperationService.OPERATION_DELETE:
337 opCode = FILEOP_DELETE_ERROR;
338 break;
339 case FileOperationService.OPERATION_MOVE:
340 opCode = FILEOP_MOVE_ERROR;
341 break;
342 }
343 if (counts.systemProvider > 0) {
344 logHistogram(context, COUNT_FILEOP_SYSTEM, opCode);
345 }
346 if (counts.externalProvider > 0) {
347 logHistogram(context, COUNT_FILEOP_EXTERNAL, opCode);
348 }
349 }
350
351 /**
352 * Log the cancellation of a file operation. Call this when a Job is canceled.
353 * @param context
354 * @param operationType
355 */
356 public static void logFileOperationCancelled(Context context, @OpType int operationType) {
Ben Kwab41a5ed2016-02-17 16:06:22 -0800357 logHistogram(context, COUNT_FILEOP_CANCELED, toMetricsOpType(operationType));
Ben Kwafaa27202016-01-28 16:39:57 -0800358 }
359
360 private static void logInterProviderFileOps(
361 Context context,
362 String histogram,
363 DocumentInfo dst,
364 @OpType int operationType) {
365 if (operationType == FileOperationService.OPERATION_DELETE) {
366 logHistogram(context, histogram, FILEOP_DELETE);
367 } else {
Steve McKay0af8afd2016-02-25 13:34:03 -0800368 assert(dst != null);
Ben Kwafaa27202016-01-28 16:39:57 -0800369 @Provider int providerType =
370 isSystemProvider(dst.authority) ? PROVIDER_SYSTEM : PROVIDER_EXTERNAL;
371 logHistogram(context, histogram, getOpCode(operationType, providerType));
372 }
373 }
374
375 private static void logIntraProviderFileOps(
376 Context context, String authority, @OpType int operationType) {
377 // Find the right histogram to log to, then log the operation.
378 String histogram = isSystemProvider(authority) ? COUNT_FILEOP_SYSTEM : COUNT_FILEOP_EXTERNAL;
379 logHistogram(context, histogram, getOpCode(operationType, PROVIDER_INTRA));
380 }
381
Felipe Lemee4b60122016-02-24 10:17:41 -0800382 // Types for logInvalidScopedAccessRequest
383 public static final String SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS =
384 "scoped_directory_access_invalid_args";
385 public static final String SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY =
386 "scoped_directory_access_invalid_dir";
387 public static final String SCOPED_DIRECTORY_ACCESS_ERROR =
388 "scoped_directory_access_error";
389
390 @StringDef(value = {
391 SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS,
392 SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY,
393 SCOPED_DIRECTORY_ACCESS_ERROR
394 })
395 @Retention(RetentionPolicy.SOURCE)
396 public @interface InvalidScopedAccess{}
397
398 public static void logInvalidScopedAccessRequest(Context context,
399 @InvalidScopedAccess String type) {
400 MetricsLogger.count(context, type, 1);
401 switch (type) {
402 case SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS:
403 case SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY:
404 case SCOPED_DIRECTORY_ACCESS_ERROR:
405 MetricsLogger.count(context, type, 1);
406 break;
407 default:
408 Log.wtf(TAG, "invalid InvalidScopedAccess: " + type);
409 }
410 }
411
412 // Types for logValidScopedAccessRequest
413 public static final int SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED = 0;
414 public static final int SCOPED_DIRECTORY_ACCESS_GRANTED = 1;
415 public static final int SCOPED_DIRECTORY_ACCESS_DENIED = 2;
416
417 @IntDef(flag = true, value = {
418 SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED,
419 SCOPED_DIRECTORY_ACCESS_GRANTED,
420 SCOPED_DIRECTORY_ACCESS_DENIED
421 })
422 @Retention(RetentionPolicy.SOURCE)
423 public @interface ScopedAccessGrant {}
424
425 public static void logValidScopedAccessRequest(Activity activity, String directory,
426 @ScopedAccessGrant int type) {
427 int index = -1;
428 for (int i = 0; i < STANDARD_DIRECTORIES.length; i++) {
429 if (STANDARD_DIRECTORIES[i].equals(directory)) {
430 index = i;
431 break;
432 }
433 }
434 final String packageName = activity.getCallingPackage();
435 switch (type) {
436 case SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED:
437 MetricsLogger.action(activity,
438 MetricsEvent.ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_PACKAGE,
439 packageName);
440 MetricsLogger.action(activity,
441 MetricsEvent.ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_FOLDER, index);
442 break;
443 case SCOPED_DIRECTORY_ACCESS_GRANTED:
444 MetricsLogger.action(activity,
445 MetricsEvent.ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_PACKAGE, packageName);
446 MetricsLogger.action(activity,
447 MetricsEvent.ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_FOLDER, index);
448 break;
449 case SCOPED_DIRECTORY_ACCESS_DENIED:
450 MetricsLogger.action(activity,
451 MetricsEvent.ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_PACKAGE, packageName);
452 MetricsLogger.action(activity,
453 MetricsEvent.ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_FOLDER, index);
454 break;
455 default:
456 Log.wtf(TAG, "invalid ScopedAccessGrant: " + type);
457 }
458 }
459
Ben Kwafaa27202016-01-28 16:39:57 -0800460 /**
Ben Kwa1c0a3892016-01-26 11:50:03 -0800461 * Internal method for making a MetricsLogger.count call. Increments the given counter by 1.
462 *
463 * @param context
464 * @param name The counter to increment.
465 */
466 private static void logCount(Context context, String name) {
467 if (DEBUG) Log.d(TAG, name + ": " + 1);
468 MetricsLogger.count(context, name, 1);
469 }
470
471 /**
472 * Internal method for making a MetricsLogger.histogram call.
473 *
474 * @param context
475 * @param name The name of the histogram.
476 * @param bucket The bucket to increment.
477 */
478 private static void logHistogram(Context context, String name, int bucket) {
479 if (DEBUG) Log.d(TAG, name + ": " + bucket);
480 MetricsLogger.histogram(context, name, bucket);
481 }
482
483 /**
484 * Generates an integer identifying the given root. For privacy, this function only recognizes a
485 * small set of hard-coded roots (ones provided by the system). Other roots are all grouped into
486 * a single ROOT_OTHER bucket.
487 */
Ben Kwafaa27202016-01-28 16:39:57 -0800488 private static @Root int sanitizeRoot(Uri uri) {
Tomasz Mikolajewski63e2aae2016-02-01 12:01:14 +0900489 if (uri == null || uri.getAuthority() == null || LauncherActivity.isLaunchUri(uri)) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800490 return ROOT_NONE;
491 }
492
493 switch (uri.getAuthority()) {
494 case AUTHORITY_MEDIA:
495 switch (DocumentsContract.getRootId(uri)) {
496 case "audio_root":
497 return ROOT_AUDIO;
498 case "images_root":
499 return ROOT_IMAGES;
500 case "videos_root":
501 return ROOT_VIDEOS;
502 default:
503 return ROOT_OTHER;
504 }
505 case AUTHORITY_STORAGE:
506 if ("home".equals(DocumentsContract.getRootId(uri))) {
507 return ROOT_HOME;
508 } else {
509 return ROOT_DEVICE_STORAGE;
510 }
511 case AUTHORITY_DOWNLOADS:
512 return ROOT_DOWNLOADS;
Ben Kwaebaaea42016-01-28 18:15:07 -0800513 case AUTHORITY_MTP:
514 return ROOT_MTP;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800515 default:
516 return ROOT_OTHER;
517 }
518 }
519
520 /** @see #sanitizeRoot(Uri) */
Ben Kwafaa27202016-01-28 16:39:57 -0800521 private static @Root int sanitizeRoot(RootInfo root) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800522 if (root.isRecents()) {
523 // Recents root is special and only identifiable via this method call. Other roots are
524 // identified by URI.
525 return ROOT_RECENTS;
526 } else {
527 return sanitizeRoot(root.getUri());
528 }
529 }
530
531 /** @see #sanitizeRoot(Uri) */
Ben Kwafaa27202016-01-28 16:39:57 -0800532 private static @Root int sanitizeRoot(ResolveInfo info) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800533 // Log all apps under a single bucket in the roots histogram.
534 return ROOT_THIRD_PARTY_APP;
535 }
536
537 /**
538 * Generates an int identifying a mime type. For privacy, this function only recognizes a small
539 * set of hard-coded types. For any other type, this function returns "other".
540 *
541 * @param mimeType
542 * @return
543 */
Ben Kwafaa27202016-01-28 16:39:57 -0800544 private static @Mime int sanitizeMime(String mimeType) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800545 if (mimeType == null) {
546 return MIME_NONE;
547 } else if ("*/*".equals(mimeType)) {
548 return MIME_ANY;
549 } else {
550 String type = mimeType.substring(0, mimeType.indexOf('/'));
551 switch (type) {
552 case "application":
553 return MIME_APPLICATION;
554 case "audio":
555 return MIME_AUDIO;
556 case "image":
557 return MIME_IMAGE;
558 case "message":
559 return MIME_MESSAGE;
560 case "multipart":
561 return MIME_MULTIPART;
562 case "text":
563 return MIME_TEXT;
564 case "video":
565 return MIME_VIDEO;
566 }
567 }
568 // Bucket all other types into one bucket.
569 return MIME_OTHER;
570 }
Ben Kwafaa27202016-01-28 16:39:57 -0800571
572 private static boolean isSystemProvider(String authority) {
573 switch (authority) {
574 case AUTHORITY_MEDIA:
575 case AUTHORITY_STORAGE:
576 case AUTHORITY_DOWNLOADS:
577 return true;
578 default:
579 return false;
580 }
581 }
582
583 /**
584 * @param operation
585 * @param providerType
586 * @return An opcode, suitable for use as histogram bucket, for the given operation/provider
587 * combination.
588 */
589 private static @FileOp int getOpCode(@OpType int operation, @Provider int providerType) {
590 switch (operation) {
591 case FileOperationService.OPERATION_COPY:
592 switch (providerType) {
593 case PROVIDER_INTRA:
594 return FILEOP_COPY_INTRA_PROVIDER;
595 case PROVIDER_SYSTEM:
596 return FILEOP_COPY_SYSTEM_PROVIDER;
597 case PROVIDER_EXTERNAL:
598 return FILEOP_COPY_EXTERNAL_PROVIDER;
599 }
600 case FileOperationService.OPERATION_MOVE:
601 switch (providerType) {
602 case PROVIDER_INTRA:
603 return FILEOP_MOVE_INTRA_PROVIDER;
604 case PROVIDER_SYSTEM:
605 return FILEOP_MOVE_SYSTEM_PROVIDER;
606 case PROVIDER_EXTERNAL:
607 return FILEOP_MOVE_EXTERNAL_PROVIDER;
608 }
609 case FileOperationService.OPERATION_DELETE:
610 return FILEOP_DELETE;
611 default:
612 Log.w(TAG, "Unrecognized operation type when logging a file operation");
613 return FILEOP_OTHER;
614 }
615 }
616
617 /**
Ben Kwab41a5ed2016-02-17 16:06:22 -0800618 * Maps FileOperationService OpType values, to MetricsOpType values.
619 */
620 private static @MetricsOpType int toMetricsOpType(@OpType int operation) {
621 switch (operation) {
622 case FileOperationService.OPERATION_COPY:
623 return OPERATION_COPY;
624 case FileOperationService.OPERATION_MOVE:
625 return OPERATION_MOVE;
626 case FileOperationService.OPERATION_DELETE:
627 return OPERATION_DELETE;
628 case FileOperationService.OPERATION_UNKNOWN:
629 default:
630 return OPERATION_UNKNOWN;
631 }
632 }
633
634 private static @MetricsAction int toMetricsAction(int action) {
635 switch(action) {
636 case State.ACTION_OPEN:
637 return ACTION_OPEN;
638 case State.ACTION_CREATE:
639 return ACTION_CREATE;
640 case State.ACTION_GET_CONTENT:
641 return ACTION_GET_CONTENT;
642 case State.ACTION_OPEN_TREE:
643 return ACTION_OPEN_TREE;
644 case State.ACTION_MANAGE:
645 return ACTION_MANAGE;
646 case State.ACTION_BROWSE:
647 return ACTION_BROWSE;
648 case State.ACTION_PICK_COPY_DESTINATION:
649 return ACTION_PICK_COPY_DESTINATION;
650 default:
651 return ACTION_OTHER;
652 }
653 }
654
655 /**
Ben Kwafaa27202016-01-28 16:39:57 -0800656 * Count the given src documents and provide a tally of how many come from the same provider as
657 * the dst document (if a dst is provided), how many come from system providers, and how many
658 * come from external 3rd-party providers.
659 */
660 private static ProviderCounts countProviders(
661 List<DocumentInfo> srcs, @Nullable DocumentInfo dst) {
662 ProviderCounts counts = new ProviderCounts();
663 for (DocumentInfo doc: srcs) {
664 if (dst != null && doc.authority.equals(dst.authority)) {
665 counts.intraProvider++;
666 } else if (isSystemProvider(doc.authority)){
667 counts.systemProvider++;
668 } else {
669 counts.externalProvider++;
670 }
671 }
672 return counts;
673 }
674
675 private static class ProviderCounts {
676 int intraProvider;
677 int systemProvider;
678 int externalProvider;
679 }
Ben Kwa1c0a3892016-01-26 11:50:03 -0800680}