blob: 7ad4a094b32748232017884e6ed05fb7fd08cf7b [file] [log] [blame]
Ben Kwa72379982016-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 Leme3e166b22016-02-24 10:17:41 -080019import static android.os.Environment.STANDARD_DIRECTORIES;
Ben Kwa72379982016-01-26 11:50:03 -080020import static com.android.documentsui.Shared.DEBUG;
Steve McKay6ce903d2016-03-09 15:20:00 -080021
Ben Kwad5b2af12016-01-28 16:39:57 -080022import android.annotation.IntDef;
23import android.annotation.Nullable;
Felipe Leme3e166b22016-02-24 10:17:41 -080024import android.annotation.StringDef;
25import android.app.Activity;
Ben Kwa72379982016-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
Aga Wronska973168c2016-03-28 17:27:02 -070033import com.android.documentsui.State.ActionType;
Ben Kwad5b2af12016-01-28 16:39:57 -080034import com.android.documentsui.model.DocumentInfo;
Ben Kwa72379982016-01-26 11:50:03 -080035import com.android.documentsui.model.RootInfo;
Ben Kwad5b2af12016-01-28 16:39:57 -080036import com.android.documentsui.services.FileOperationService;
37import com.android.documentsui.services.FileOperationService.OpType;
Ben Kwa72379982016-01-26 11:50:03 -080038import com.android.internal.logging.MetricsLogger;
Felipe Leme3e166b22016-02-24 10:17:41 -080039import com.android.internal.logging.MetricsProto.MetricsEvent;
Ben Kwa72379982016-01-26 11:50:03 -080040
Ben Kwad5b2af12016-01-28 16:39:57 -080041import java.lang.annotation.Retention;
42import java.lang.annotation.RetentionPolicy;
43import java.util.List;
44
Ben Kwa72379982016-01-26 11:50:03 -080045/** @hide */
46public final class Metrics {
47 private static final String TAG = "Metrics";
48
49 // These are the native provider authorities that the metrics code is capable of recognizing and
50 // explicitly counting.
51 private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
52 private static final String AUTHORITY_STORAGE = "com.android.externalstorage.documents";
53 private static final String AUTHORITY_DOWNLOADS = "com.android.providers.downloads.documents";
Ben Kwada518c92016-01-28 18:15:07 -080054 private static final String AUTHORITY_MTP = "com.android.mtp.documents";
Ben Kwa72379982016-01-26 11:50:03 -080055
56 // These strings have to be whitelisted in tron. Do not change them.
57 private static final String COUNT_LAUNCH_ACTION = "docsui_launch_action";
58 private static final String COUNT_ROOT_VISITED = "docsui_root_visited";
59 private static final String COUNT_OPEN_MIME = "docsui_open_mime";
60 private static final String COUNT_CREATE_MIME = "docsui_create_mime";
61 private static final String COUNT_GET_CONTENT_MIME = "docsui_get_content_mime";
62 private static final String COUNT_BROWSE_ROOT = "docsui_browse_root";
Steve McKay6ce903d2016-03-09 15:20:00 -080063 @Deprecated private static final String COUNT_MANAGE_ROOT = "docsui_manage_root";
Ben Kwa72379982016-01-26 11:50:03 -080064 private static final String COUNT_MULTI_WINDOW = "docsui_multi_window";
Ben Kwad5b2af12016-01-28 16:39:57 -080065 private static final String COUNT_FILEOP_SYSTEM = "docsui_fileop_system";
66 private static final String COUNT_FILEOP_EXTERNAL = "docsui_fileop_external";
67 private static final String COUNT_FILEOP_CANCELED = "docsui_fileop_canceled";
Daichi Hirono7352c552016-03-25 19:04:39 +090068 private static final String COUNT_STARTUP_MS = "docsui_startup_ms";
Ben Kwa72379982016-01-26 11:50:03 -080069
70 // Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
71 // root that is not explicitly recognized by the Metrics code (see {@link
Ben Kwaa87e9f92016-02-17 16:06:22 -080072 // #getSanitizedRootIndex}). Apps are also bucketed in this histogram.
Ben Kwada518c92016-01-28 18:15:07 -080073 // Do not change or rearrange these values, that will break historical data. Only add to the end
74 // of the list.
Ben Kwaa87e9f92016-02-17 16:06:22 -080075 // Do not use negative numbers or zero; clearcut only handles positive integers.
76 private static final int ROOT_NONE = 1;
77 private static final int ROOT_OTHER = 2;
78 private static final int ROOT_AUDIO = 3;
79 private static final int ROOT_DEVICE_STORAGE = 4;
80 private static final int ROOT_DOWNLOADS = 5;
81 private static final int ROOT_HOME = 6;
82 private static final int ROOT_IMAGES = 7;
83 private static final int ROOT_RECENTS = 8;
84 private static final int ROOT_VIDEOS = 9;
85 private static final int ROOT_MTP = 10;
Ben Kwa72379982016-01-26 11:50:03 -080086 // Apps aren't really "roots", but they are treated as such in the roots fragment UI and so they
Ben Kwaa87e9f92016-02-17 16:06:22 -080087 // are logged analogously to roots.
88 private static final int ROOT_THIRD_PARTY_APP = 100;
Ben Kwa72379982016-01-26 11:50:03 -080089
Ben Kwad5b2af12016-01-28 16:39:57 -080090 @IntDef(flag = true, value = {
91 ROOT_NONE,
92 ROOT_OTHER,
93 ROOT_AUDIO,
94 ROOT_DEVICE_STORAGE,
95 ROOT_DOWNLOADS,
96 ROOT_HOME,
97 ROOT_IMAGES,
98 ROOT_RECENTS,
99 ROOT_VIDEOS,
Ben Kwada518c92016-01-28 18:15:07 -0800100 ROOT_MTP,
Ben Kwad5b2af12016-01-28 16:39:57 -0800101 ROOT_THIRD_PARTY_APP
102 })
103 @Retention(RetentionPolicy.SOURCE)
104 public @interface Root {}
105
Ben Kwa72379982016-01-26 11:50:03 -0800106 // Indices for bucketing mime types.
Ben Kwaa87e9f92016-02-17 16:06:22 -0800107 // Do not change or rearrange these values, that will break historical data. Only add to the end
108 // of the list.
109 // Do not use negative numbers or zero; clearcut only handles positive integers.
110 private static final int MIME_NONE = 1; // null mime
111 private static final int MIME_ANY = 2; // */*
112 private static final int MIME_APPLICATION = 3; // application/*
113 private static final int MIME_AUDIO = 4; // audio/*
114 private static final int MIME_IMAGE = 5; // image/*
115 private static final int MIME_MESSAGE = 6; // message/*
116 private static final int MIME_MULTIPART = 7; // multipart/*
117 private static final int MIME_TEXT = 8; // text/*
118 private static final int MIME_VIDEO = 9; // video/*
119 private static final int MIME_OTHER = 10; // anything not enumerated below
Ben Kwa72379982016-01-26 11:50:03 -0800120
Ben Kwad5b2af12016-01-28 16:39:57 -0800121 @IntDef(flag = true, value = {
Ben Kwad5b2af12016-01-28 16:39:57 -0800122 MIME_NONE,
123 MIME_ANY,
124 MIME_APPLICATION,
125 MIME_AUDIO,
126 MIME_IMAGE,
127 MIME_MESSAGE,
128 MIME_MULTIPART,
129 MIME_TEXT,
Ben Kwaa87e9f92016-02-17 16:06:22 -0800130 MIME_VIDEO,
131 MIME_OTHER
Ben Kwad5b2af12016-01-28 16:39:57 -0800132 })
133 @Retention(RetentionPolicy.SOURCE)
134 public @interface Mime {}
135
136 // Codes representing different kinds of file operations. These are used for bucketing
137 // operations in the COUNT_FILEOP_{SYSTEM|EXTERNAL} histograms.
Ben Kwaa87e9f92016-02-17 16:06:22 -0800138 // Do not change or rearrange these values, that will break historical data. Only add to the
139 // list.
140 // Do not use negative numbers or zero; clearcut only handles positive integers.
141 private static final int FILEOP_OTHER = 1; // any file operation not listed below
142 private static final int FILEOP_COPY_INTRA_PROVIDER = 2; // Copy within a provider
143 private static final int FILEOP_COPY_SYSTEM_PROVIDER = 3; // Copy to a system provider.
144 private static final int FILEOP_COPY_EXTERNAL_PROVIDER = 4; // Copy to a 3rd-party provider.
145 private static final int FILEOP_MOVE_INTRA_PROVIDER = 5; // Move within a provider.
146 private static final int FILEOP_MOVE_SYSTEM_PROVIDER = 6; // Move to a system provider.
147 private static final int FILEOP_MOVE_EXTERNAL_PROVIDER = 7; // Move to a 3rd-party provider.
148 private static final int FILEOP_DELETE = 8;
149 private static final int FILEOP_OTHER_ERROR = 100;
150 private static final int FILEOP_DELETE_ERROR = 101;
151 private static final int FILEOP_MOVE_ERROR = 102;
152 private static final int FILEOP_COPY_ERROR = 103;
Ben Kwad5b2af12016-01-28 16:39:57 -0800153
154 @IntDef(flag = true, value = {
155 FILEOP_OTHER,
156 FILEOP_COPY_INTRA_PROVIDER,
157 FILEOP_COPY_SYSTEM_PROVIDER,
158 FILEOP_COPY_EXTERNAL_PROVIDER,
159 FILEOP_MOVE_INTRA_PROVIDER,
160 FILEOP_MOVE_SYSTEM_PROVIDER,
161 FILEOP_MOVE_EXTERNAL_PROVIDER,
162 FILEOP_DELETE,
163 FILEOP_OTHER_ERROR,
164 FILEOP_COPY_ERROR,
165 FILEOP_MOVE_ERROR,
166 FILEOP_DELETE_ERROR
167 })
168 @Retention(RetentionPolicy.SOURCE)
169 public @interface FileOp {}
170
Ben Kwaa87e9f92016-02-17 16:06:22 -0800171 // Codes representing different kinds of file operations. These are used for bucketing
172 // operations in the COUNT_FILEOP_CANCELED histogram.
173 // Do not change or rearrange these values, that will break historical data. Only add to the
174 // list.
175 // Do not use negative numbers or zero; clearcut only handles positive integers.
176 private static final int OPERATION_UNKNOWN = 1;
177 private static final int OPERATION_COPY = 2;
178 private static final int OPERATION_MOVE = 3;
179 private static final int OPERATION_DELETE= 4;
180
181 @IntDef(flag = true, value = {
182 OPERATION_UNKNOWN,
183 OPERATION_COPY,
184 OPERATION_MOVE,
185 OPERATION_DELETE
186 })
187 @Retention(RetentionPolicy.SOURCE)
188 public @interface MetricsOpType {}
189
190 // Codes representing different launch actions. These are used for bucketing stats in the
191 // COUNT_LAUNCH_ACTION histogram.
192 // Do not change or rearrange these values, that will break historical data. Only add to the
193 // list.
194 // Do not use negative numbers or zero; clearcut only handles positive integers.
195 private static final int ACTION_OTHER = 1;
196 private static final int ACTION_OPEN = 2;
197 private static final int ACTION_CREATE = 3;
198 private static final int ACTION_GET_CONTENT = 4;
199 private static final int ACTION_OPEN_TREE = 5;
Steve McKay6ce903d2016-03-09 15:20:00 -0800200 @Deprecated private static final int ACTION_MANAGE = 6;
Ben Kwaa87e9f92016-02-17 16:06:22 -0800201 private static final int ACTION_BROWSE = 7;
202 private static final int ACTION_PICK_COPY_DESTINATION = 8;
203
204 @IntDef(flag = true, value = {
205 ACTION_OTHER,
206 ACTION_OPEN,
207 ACTION_CREATE,
208 ACTION_GET_CONTENT,
209 ACTION_OPEN_TREE,
210 ACTION_MANAGE,
211 ACTION_BROWSE,
212 ACTION_PICK_COPY_DESTINATION
213 })
214 @Retention(RetentionPolicy.SOURCE)
215 public @interface MetricsAction {}
216
Ben Kwad5b2af12016-01-28 16:39:57 -0800217 // Codes representing different provider types. Used for sorting file operations when logging.
218 private static final int PROVIDER_INTRA = 0;
219 private static final int PROVIDER_SYSTEM = 1;
220 private static final int PROVIDER_EXTERNAL = 2;
221
222 @IntDef(flag = true, value = {
223 PROVIDER_INTRA,
224 PROVIDER_SYSTEM,
225 PROVIDER_EXTERNAL
226 })
227 @Retention(RetentionPolicy.SOURCE)
228 public @interface Provider {}
229
Ben Kwa72379982016-01-26 11:50:03 -0800230 /**
231 * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up.
232 *
233 * @param context
234 * @param state
235 * @param intent
236 */
237 public static void logActivityLaunch(Context context, State state, Intent intent) {
238 // Log the launch action.
Ben Kwaa87e9f92016-02-17 16:06:22 -0800239 logHistogram(context, COUNT_LAUNCH_ACTION, toMetricsAction(state.action));
Ben Kwa72379982016-01-26 11:50:03 -0800240 // Then log auxiliary data (roots/mime types) associated with some actions.
241 Uri uri = intent.getData();
242 switch (state.action) {
243 case State.ACTION_OPEN:
244 logHistogram(context, COUNT_OPEN_MIME, sanitizeMime(intent.getType()));
245 break;
246 case State.ACTION_CREATE:
247 logHistogram(context, COUNT_CREATE_MIME, sanitizeMime(intent.getType()));
248 break;
249 case State.ACTION_GET_CONTENT:
250 logHistogram(context, COUNT_GET_CONTENT_MIME, sanitizeMime(intent.getType()));
251 break;
Ben Kwa72379982016-01-26 11:50:03 -0800252 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 Kwad5b2af12016-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 /**
Daichi Hirono7352c552016-03-25 19:04:39 +0900352 * Logs the cancellation of a file operation. Call this when a Job is canceled.
Ben Kwad5b2af12016-01-28 16:39:57 -0800353 * @param context
354 * @param operationType
355 */
356 public static void logFileOperationCancelled(Context context, @OpType int operationType) {
Ben Kwaa87e9f92016-02-17 16:06:22 -0800357 logHistogram(context, COUNT_FILEOP_CANCELED, toMetricsOpType(operationType));
Ben Kwad5b2af12016-01-28 16:39:57 -0800358 }
359
Daichi Hirono7352c552016-03-25 19:04:39 +0900360 /**
361 * Logs startup time in milliseconds.
362 * @param context
363 * @param startupMs Startup time in milliseconds.
364 */
365 public static void logStartupMs(Context context, int startupMs) {
366 logHistogram(context, COUNT_STARTUP_MS, startupMs);
367 }
368
Ben Kwad5b2af12016-01-28 16:39:57 -0800369 private static void logInterProviderFileOps(
370 Context context,
371 String histogram,
372 DocumentInfo dst,
373 @OpType int operationType) {
374 if (operationType == FileOperationService.OPERATION_DELETE) {
375 logHistogram(context, histogram, FILEOP_DELETE);
376 } else {
Steve McKaya1f76802016-02-25 13:34:03 -0800377 assert(dst != null);
Ben Kwad5b2af12016-01-28 16:39:57 -0800378 @Provider int providerType =
379 isSystemProvider(dst.authority) ? PROVIDER_SYSTEM : PROVIDER_EXTERNAL;
380 logHistogram(context, histogram, getOpCode(operationType, providerType));
381 }
382 }
383
384 private static void logIntraProviderFileOps(
385 Context context, String authority, @OpType int operationType) {
386 // Find the right histogram to log to, then log the operation.
387 String histogram = isSystemProvider(authority) ? COUNT_FILEOP_SYSTEM : COUNT_FILEOP_EXTERNAL;
388 logHistogram(context, histogram, getOpCode(operationType, PROVIDER_INTRA));
389 }
390
Felipe Leme3e166b22016-02-24 10:17:41 -0800391 // Types for logInvalidScopedAccessRequest
392 public static final String SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS =
393 "scoped_directory_access_invalid_args";
394 public static final String SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY =
395 "scoped_directory_access_invalid_dir";
396 public static final String SCOPED_DIRECTORY_ACCESS_ERROR =
397 "scoped_directory_access_error";
398
399 @StringDef(value = {
400 SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS,
401 SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY,
402 SCOPED_DIRECTORY_ACCESS_ERROR
403 })
404 @Retention(RetentionPolicy.SOURCE)
405 public @interface InvalidScopedAccess{}
406
407 public static void logInvalidScopedAccessRequest(Context context,
408 @InvalidScopedAccess String type) {
409 MetricsLogger.count(context, type, 1);
410 switch (type) {
411 case SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS:
412 case SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY:
413 case SCOPED_DIRECTORY_ACCESS_ERROR:
414 MetricsLogger.count(context, type, 1);
415 break;
416 default:
417 Log.wtf(TAG, "invalid InvalidScopedAccess: " + type);
418 }
419 }
420
421 // Types for logValidScopedAccessRequest
422 public static final int SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED = 0;
423 public static final int SCOPED_DIRECTORY_ACCESS_GRANTED = 1;
424 public static final int SCOPED_DIRECTORY_ACCESS_DENIED = 2;
Felipe Lemeadccb992016-03-09 17:40:49 -0800425 public static final int SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST = 3;
426 public static final int SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED = 4;
Felipe Leme3e166b22016-02-24 10:17:41 -0800427
428 @IntDef(flag = true, value = {
429 SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED,
430 SCOPED_DIRECTORY_ACCESS_GRANTED,
Felipe Lemeadccb992016-03-09 17:40:49 -0800431 SCOPED_DIRECTORY_ACCESS_DENIED,
432 SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST,
433 SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED
Felipe Leme3e166b22016-02-24 10:17:41 -0800434 })
435 @Retention(RetentionPolicy.SOURCE)
436 public @interface ScopedAccessGrant {}
437
438 public static void logValidScopedAccessRequest(Activity activity, String directory,
439 @ScopedAccessGrant int type) {
440 int index = -1;
Felipe Lemedb892b82016-03-17 18:56:20 -0700441 if (OpenExternalDirectoryActivity.DIRECTORY_ROOT.equals(directory)) {
442 index = -2;
443 } else {
444 for (int i = 0; i < STANDARD_DIRECTORIES.length; i++) {
445 if (STANDARD_DIRECTORIES[i].equals(directory)) {
446 index = i;
447 break;
448 }
Felipe Leme3e166b22016-02-24 10:17:41 -0800449 }
450 }
451 final String packageName = activity.getCallingPackage();
452 switch (type) {
453 case SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED:
Felipe Lemeadccb992016-03-09 17:40:49 -0800454 MetricsLogger.action(activity, MetricsEvent
455 .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_PACKAGE, packageName);
456 MetricsLogger.action(activity, MetricsEvent
457 .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_FOLDER, index);
Felipe Leme3e166b22016-02-24 10:17:41 -0800458 break;
459 case SCOPED_DIRECTORY_ACCESS_GRANTED:
Felipe Lemeadccb992016-03-09 17:40:49 -0800460 MetricsLogger.action(activity, MetricsEvent
461 .ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_PACKAGE, packageName);
462 MetricsLogger.action(activity, MetricsEvent
463 .ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_FOLDER, index);
Felipe Leme3e166b22016-02-24 10:17:41 -0800464 break;
465 case SCOPED_DIRECTORY_ACCESS_DENIED:
Felipe Lemeadccb992016-03-09 17:40:49 -0800466 MetricsLogger.action(activity, MetricsEvent
467 .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_PACKAGE, packageName);
468 MetricsLogger.action(activity, MetricsEvent
469 .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_FOLDER, index);
470 break;
471 case SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST:
472 MetricsLogger.action(activity, MetricsEvent
473 .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST_BY_PACKAGE, packageName);
474 MetricsLogger.action(activity, MetricsEvent
475 .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST_BY_FOLDER, index);
476 break;
477 case SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED:
478 MetricsLogger.action(activity, MetricsEvent
479 .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED_BY_PACKAGE, packageName);
480 MetricsLogger.action(activity, MetricsEvent
481 .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED_BY_FOLDER, index);
Felipe Leme3e166b22016-02-24 10:17:41 -0800482 break;
483 default:
484 Log.wtf(TAG, "invalid ScopedAccessGrant: " + type);
485 }
486 }
487
Ben Kwad5b2af12016-01-28 16:39:57 -0800488 /**
Ben Kwa72379982016-01-26 11:50:03 -0800489 * Internal method for making a MetricsLogger.count call. Increments the given counter by 1.
490 *
491 * @param context
492 * @param name The counter to increment.
493 */
494 private static void logCount(Context context, String name) {
495 if (DEBUG) Log.d(TAG, name + ": " + 1);
496 MetricsLogger.count(context, name, 1);
497 }
498
499 /**
500 * Internal method for making a MetricsLogger.histogram call.
501 *
502 * @param context
503 * @param name The name of the histogram.
504 * @param bucket The bucket to increment.
505 */
Aga Wronska973168c2016-03-28 17:27:02 -0700506 private static void logHistogram(Context context, String name, @ActionType int bucket) {
Ben Kwa72379982016-01-26 11:50:03 -0800507 if (DEBUG) Log.d(TAG, name + ": " + bucket);
508 MetricsLogger.histogram(context, name, bucket);
509 }
510
511 /**
512 * Generates an integer identifying the given root. For privacy, this function only recognizes a
513 * small set of hard-coded roots (ones provided by the system). Other roots are all grouped into
514 * a single ROOT_OTHER bucket.
515 */
Ben Kwad5b2af12016-01-28 16:39:57 -0800516 private static @Root int sanitizeRoot(Uri uri) {
Tomasz Mikolajewskicd270152016-02-01 12:01:14 +0900517 if (uri == null || uri.getAuthority() == null || LauncherActivity.isLaunchUri(uri)) {
Ben Kwa72379982016-01-26 11:50:03 -0800518 return ROOT_NONE;
519 }
520
521 switch (uri.getAuthority()) {
522 case AUTHORITY_MEDIA:
523 switch (DocumentsContract.getRootId(uri)) {
524 case "audio_root":
525 return ROOT_AUDIO;
526 case "images_root":
527 return ROOT_IMAGES;
528 case "videos_root":
529 return ROOT_VIDEOS;
530 default:
531 return ROOT_OTHER;
532 }
533 case AUTHORITY_STORAGE:
534 if ("home".equals(DocumentsContract.getRootId(uri))) {
535 return ROOT_HOME;
536 } else {
537 return ROOT_DEVICE_STORAGE;
538 }
539 case AUTHORITY_DOWNLOADS:
540 return ROOT_DOWNLOADS;
Ben Kwada518c92016-01-28 18:15:07 -0800541 case AUTHORITY_MTP:
542 return ROOT_MTP;
Ben Kwa72379982016-01-26 11:50:03 -0800543 default:
544 return ROOT_OTHER;
545 }
546 }
547
548 /** @see #sanitizeRoot(Uri) */
Ben Kwad5b2af12016-01-28 16:39:57 -0800549 private static @Root int sanitizeRoot(RootInfo root) {
Ben Kwa72379982016-01-26 11:50:03 -0800550 if (root.isRecents()) {
551 // Recents root is special and only identifiable via this method call. Other roots are
552 // identified by URI.
553 return ROOT_RECENTS;
554 } else {
555 return sanitizeRoot(root.getUri());
556 }
557 }
558
559 /** @see #sanitizeRoot(Uri) */
Ben Kwad5b2af12016-01-28 16:39:57 -0800560 private static @Root int sanitizeRoot(ResolveInfo info) {
Ben Kwa72379982016-01-26 11:50:03 -0800561 // Log all apps under a single bucket in the roots histogram.
562 return ROOT_THIRD_PARTY_APP;
563 }
564
565 /**
566 * Generates an int identifying a mime type. For privacy, this function only recognizes a small
567 * set of hard-coded types. For any other type, this function returns "other".
568 *
569 * @param mimeType
570 * @return
571 */
Ben Kwad5b2af12016-01-28 16:39:57 -0800572 private static @Mime int sanitizeMime(String mimeType) {
Ben Kwa72379982016-01-26 11:50:03 -0800573 if (mimeType == null) {
574 return MIME_NONE;
575 } else if ("*/*".equals(mimeType)) {
576 return MIME_ANY;
577 } else {
578 String type = mimeType.substring(0, mimeType.indexOf('/'));
579 switch (type) {
580 case "application":
581 return MIME_APPLICATION;
582 case "audio":
583 return MIME_AUDIO;
584 case "image":
585 return MIME_IMAGE;
586 case "message":
587 return MIME_MESSAGE;
588 case "multipart":
589 return MIME_MULTIPART;
590 case "text":
591 return MIME_TEXT;
592 case "video":
593 return MIME_VIDEO;
594 }
595 }
596 // Bucket all other types into one bucket.
597 return MIME_OTHER;
598 }
Ben Kwad5b2af12016-01-28 16:39:57 -0800599
600 private static boolean isSystemProvider(String authority) {
601 switch (authority) {
602 case AUTHORITY_MEDIA:
603 case AUTHORITY_STORAGE:
604 case AUTHORITY_DOWNLOADS:
605 return true;
606 default:
607 return false;
608 }
609 }
610
611 /**
612 * @param operation
613 * @param providerType
614 * @return An opcode, suitable for use as histogram bucket, for the given operation/provider
615 * combination.
616 */
617 private static @FileOp int getOpCode(@OpType int operation, @Provider int providerType) {
618 switch (operation) {
619 case FileOperationService.OPERATION_COPY:
620 switch (providerType) {
621 case PROVIDER_INTRA:
622 return FILEOP_COPY_INTRA_PROVIDER;
623 case PROVIDER_SYSTEM:
624 return FILEOP_COPY_SYSTEM_PROVIDER;
625 case PROVIDER_EXTERNAL:
626 return FILEOP_COPY_EXTERNAL_PROVIDER;
627 }
628 case FileOperationService.OPERATION_MOVE:
629 switch (providerType) {
630 case PROVIDER_INTRA:
631 return FILEOP_MOVE_INTRA_PROVIDER;
632 case PROVIDER_SYSTEM:
633 return FILEOP_MOVE_SYSTEM_PROVIDER;
634 case PROVIDER_EXTERNAL:
635 return FILEOP_MOVE_EXTERNAL_PROVIDER;
636 }
637 case FileOperationService.OPERATION_DELETE:
638 return FILEOP_DELETE;
639 default:
640 Log.w(TAG, "Unrecognized operation type when logging a file operation");
641 return FILEOP_OTHER;
642 }
643 }
644
645 /**
Ben Kwaa87e9f92016-02-17 16:06:22 -0800646 * Maps FileOperationService OpType values, to MetricsOpType values.
647 */
648 private static @MetricsOpType int toMetricsOpType(@OpType int operation) {
649 switch (operation) {
650 case FileOperationService.OPERATION_COPY:
651 return OPERATION_COPY;
652 case FileOperationService.OPERATION_MOVE:
653 return OPERATION_MOVE;
654 case FileOperationService.OPERATION_DELETE:
655 return OPERATION_DELETE;
656 case FileOperationService.OPERATION_UNKNOWN:
657 default:
658 return OPERATION_UNKNOWN;
659 }
660 }
661
662 private static @MetricsAction int toMetricsAction(int action) {
663 switch(action) {
664 case State.ACTION_OPEN:
665 return ACTION_OPEN;
666 case State.ACTION_CREATE:
667 return ACTION_CREATE;
668 case State.ACTION_GET_CONTENT:
669 return ACTION_GET_CONTENT;
670 case State.ACTION_OPEN_TREE:
671 return ACTION_OPEN_TREE;
Ben Kwaa87e9f92016-02-17 16:06:22 -0800672 case State.ACTION_BROWSE:
673 return ACTION_BROWSE;
674 case State.ACTION_PICK_COPY_DESTINATION:
675 return ACTION_PICK_COPY_DESTINATION;
676 default:
677 return ACTION_OTHER;
678 }
679 }
680
681 /**
Ben Kwad5b2af12016-01-28 16:39:57 -0800682 * Count the given src documents and provide a tally of how many come from the same provider as
683 * the dst document (if a dst is provided), how many come from system providers, and how many
684 * come from external 3rd-party providers.
685 */
686 private static ProviderCounts countProviders(
687 List<DocumentInfo> srcs, @Nullable DocumentInfo dst) {
688 ProviderCounts counts = new ProviderCounts();
689 for (DocumentInfo doc: srcs) {
690 if (dst != null && doc.authority.equals(dst.authority)) {
691 counts.intraProvider++;
692 } else if (isSystemProvider(doc.authority)){
693 counts.systemProvider++;
694 } else {
695 counts.externalProvider++;
696 }
697 }
698 return counts;
699 }
700
701 private static class ProviderCounts {
702 int intraProvider;
703 int systemProvider;
704 int externalProvider;
705 }
Ben Kwa72379982016-01-26 11:50:03 -0800706}