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