blob: d3f4721b7d45fa13ac610591b4377d4e17c102ff [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
Aga Wronska6d50bcc2016-03-28 17:27:02 -070033import com.android.documentsui.State.ActionType;
Steve McKayd0805062016-09-15 14:30:38 -070034import com.android.documentsui.base.DocumentInfo;
35import com.android.documentsui.base.RootInfo;
Steve McKay16e0c1f2016-09-15 12:41:13 -070036import com.android.documentsui.manager.LauncherActivity;
Ben Kwafaa27202016-01-28 16:39:57 -080037import com.android.documentsui.services.FileOperationService;
38import com.android.documentsui.services.FileOperationService.OpType;
Ben Kwa1c0a3892016-01-26 11:50:03 -080039import com.android.internal.logging.MetricsLogger;
Felipe Lemee4b60122016-02-24 10:17:41 -080040import com.android.internal.logging.MetricsProto.MetricsEvent;
Ben Kwa1c0a3892016-01-26 11:50:03 -080041
Ben Kwafaa27202016-01-28 16:39:57 -080042import java.lang.annotation.Retention;
43import java.lang.annotation.RetentionPolicy;
44import java.util.List;
45
Ben Kwa1c0a3892016-01-26 11:50:03 -080046/** @hide */
47public final class Metrics {
48 private static final String TAG = "Metrics";
49
50 // These are the native provider authorities that the metrics code is capable of recognizing and
51 // explicitly counting.
52 private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
53 private static final String AUTHORITY_STORAGE = "com.android.externalstorage.documents";
54 private static final String AUTHORITY_DOWNLOADS = "com.android.providers.downloads.documents";
Ben Kwaebaaea42016-01-28 18:15:07 -080055 private static final String AUTHORITY_MTP = "com.android.mtp.documents";
Ben Kwa1c0a3892016-01-26 11:50:03 -080056
57 // These strings have to be whitelisted in tron. Do not change them.
58 private static final String COUNT_LAUNCH_ACTION = "docsui_launch_action";
59 private static final String COUNT_ROOT_VISITED = "docsui_root_visited";
60 private static final String COUNT_OPEN_MIME = "docsui_open_mime";
61 private static final String COUNT_CREATE_MIME = "docsui_create_mime";
62 private static final String COUNT_GET_CONTENT_MIME = "docsui_get_content_mime";
63 private static final String COUNT_BROWSE_ROOT = "docsui_browse_root";
Steve McKay15b92782016-03-09 15:20:00 -080064 @Deprecated private static final String COUNT_MANAGE_ROOT = "docsui_manage_root";
Aga Wronska94e53e42016-04-07 13:09:58 -070065 @Deprecated private static final String COUNT_MULTI_WINDOW = "docsui_multi_window";
Ben Kwafaa27202016-01-28 16:39:57 -080066 private static final String COUNT_FILEOP_SYSTEM = "docsui_fileop_system";
67 private static final String COUNT_FILEOP_EXTERNAL = "docsui_fileop_external";
68 private static final String COUNT_FILEOP_CANCELED = "docsui_fileop_canceled";
Daichi Hirono320a08f2016-03-25 19:04:39 +090069 private static final String COUNT_STARTUP_MS = "docsui_startup_ms";
Aga Wronska441b9be2016-03-29 16:57:10 -070070 private static final String COUNT_DRAWER_OPENED = "docsui_drawer_opened";
Aga Wronska94e53e42016-04-07 13:09:58 -070071 private static final String COUNT_USER_ACTION = "docsui_menu_action";
Ben Kwa1c0a3892016-01-26 11:50:03 -080072
73 // Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
74 // root that is not explicitly recognized by the Metrics code (see {@link
Ben Kwab41a5ed2016-02-17 16:06:22 -080075 // #getSanitizedRootIndex}). Apps are also bucketed in this histogram.
Ben Kwaebaaea42016-01-28 18:15:07 -080076 // Do not change or rearrange these values, that will break historical data. Only add to the end
77 // of the list.
Ben Kwab41a5ed2016-02-17 16:06:22 -080078 // Do not use negative numbers or zero; clearcut only handles positive integers.
79 private static final int ROOT_NONE = 1;
80 private static final int ROOT_OTHER = 2;
81 private static final int ROOT_AUDIO = 3;
82 private static final int ROOT_DEVICE_STORAGE = 4;
83 private static final int ROOT_DOWNLOADS = 5;
84 private static final int ROOT_HOME = 6;
85 private static final int ROOT_IMAGES = 7;
86 private static final int ROOT_RECENTS = 8;
87 private static final int ROOT_VIDEOS = 9;
88 private static final int ROOT_MTP = 10;
Ben Kwa1c0a3892016-01-26 11:50:03 -080089 // 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 -080090 // are logged analogously to roots.
91 private static final int ROOT_THIRD_PARTY_APP = 100;
Ben Kwa1c0a3892016-01-26 11:50:03 -080092
Ben Kwafaa27202016-01-28 16:39:57 -080093 @IntDef(flag = true, value = {
94 ROOT_NONE,
95 ROOT_OTHER,
96 ROOT_AUDIO,
97 ROOT_DEVICE_STORAGE,
98 ROOT_DOWNLOADS,
99 ROOT_HOME,
100 ROOT_IMAGES,
101 ROOT_RECENTS,
102 ROOT_VIDEOS,
Ben Kwaebaaea42016-01-28 18:15:07 -0800103 ROOT_MTP,
Ben Kwafaa27202016-01-28 16:39:57 -0800104 ROOT_THIRD_PARTY_APP
105 })
106 @Retention(RetentionPolicy.SOURCE)
107 public @interface Root {}
108
Ben Kwa1c0a3892016-01-26 11:50:03 -0800109 // Indices for bucketing mime types.
Ben Kwab41a5ed2016-02-17 16:06:22 -0800110 // Do not change or rearrange these values, that will break historical data. Only add to the end
111 // of the list.
112 // Do not use negative numbers or zero; clearcut only handles positive integers.
113 private static final int MIME_NONE = 1; // null mime
114 private static final int MIME_ANY = 2; // */*
115 private static final int MIME_APPLICATION = 3; // application/*
116 private static final int MIME_AUDIO = 4; // audio/*
117 private static final int MIME_IMAGE = 5; // image/*
118 private static final int MIME_MESSAGE = 6; // message/*
119 private static final int MIME_MULTIPART = 7; // multipart/*
120 private static final int MIME_TEXT = 8; // text/*
121 private static final int MIME_VIDEO = 9; // video/*
122 private static final int MIME_OTHER = 10; // anything not enumerated below
Ben Kwa1c0a3892016-01-26 11:50:03 -0800123
Ben Kwafaa27202016-01-28 16:39:57 -0800124 @IntDef(flag = true, value = {
Ben Kwafaa27202016-01-28 16:39:57 -0800125 MIME_NONE,
126 MIME_ANY,
127 MIME_APPLICATION,
128 MIME_AUDIO,
129 MIME_IMAGE,
130 MIME_MESSAGE,
131 MIME_MULTIPART,
132 MIME_TEXT,
Ben Kwab41a5ed2016-02-17 16:06:22 -0800133 MIME_VIDEO,
134 MIME_OTHER
Ben Kwafaa27202016-01-28 16:39:57 -0800135 })
136 @Retention(RetentionPolicy.SOURCE)
137 public @interface Mime {}
138
139 // Codes representing different kinds of file operations. These are used for bucketing
140 // operations in the COUNT_FILEOP_{SYSTEM|EXTERNAL} histograms.
Ben Kwab41a5ed2016-02-17 16:06:22 -0800141 // Do not change or rearrange these values, that will break historical data. Only add to the
142 // list.
143 // Do not use negative numbers or zero; clearcut only handles positive integers.
144 private static final int FILEOP_OTHER = 1; // any file operation not listed below
145 private static final int FILEOP_COPY_INTRA_PROVIDER = 2; // Copy within a provider
146 private static final int FILEOP_COPY_SYSTEM_PROVIDER = 3; // Copy to a system provider.
147 private static final int FILEOP_COPY_EXTERNAL_PROVIDER = 4; // Copy to a 3rd-party provider.
148 private static final int FILEOP_MOVE_INTRA_PROVIDER = 5; // Move within a provider.
149 private static final int FILEOP_MOVE_SYSTEM_PROVIDER = 6; // Move to a system provider.
150 private static final int FILEOP_MOVE_EXTERNAL_PROVIDER = 7; // Move to a 3rd-party provider.
151 private static final int FILEOP_DELETE = 8;
Aga Wronska46a868a2016-03-30 10:57:04 -0700152 private static final int FILEOP_RENAME = 9;
153 private static final int FILEOP_CREATE_DIR = 10;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800154 private static final int FILEOP_OTHER_ERROR = 100;
155 private static final int FILEOP_DELETE_ERROR = 101;
156 private static final int FILEOP_MOVE_ERROR = 102;
157 private static final int FILEOP_COPY_ERROR = 103;
Aga Wronska46a868a2016-03-30 10:57:04 -0700158 private static final int FILEOP_RENAME_ERROR = 104;
159 private static final int FILEOP_CREATE_DIR_ERROR = 105;
Ben Kwafaa27202016-01-28 16:39:57 -0800160
161 @IntDef(flag = true, value = {
162 FILEOP_OTHER,
163 FILEOP_COPY_INTRA_PROVIDER,
164 FILEOP_COPY_SYSTEM_PROVIDER,
165 FILEOP_COPY_EXTERNAL_PROVIDER,
166 FILEOP_MOVE_INTRA_PROVIDER,
167 FILEOP_MOVE_SYSTEM_PROVIDER,
168 FILEOP_MOVE_EXTERNAL_PROVIDER,
169 FILEOP_DELETE,
Aga Wronska46a868a2016-03-30 10:57:04 -0700170 FILEOP_RENAME,
171 FILEOP_CREATE_DIR,
Ben Kwafaa27202016-01-28 16:39:57 -0800172 FILEOP_OTHER_ERROR,
173 FILEOP_COPY_ERROR,
174 FILEOP_MOVE_ERROR,
Aga Wronska46a868a2016-03-30 10:57:04 -0700175 FILEOP_DELETE_ERROR,
176 FILEOP_RENAME_ERROR,
177 FILEOP_CREATE_DIR_ERROR
Ben Kwafaa27202016-01-28 16:39:57 -0800178 })
179 @Retention(RetentionPolicy.SOURCE)
180 public @interface FileOp {}
181
Ben Kwab41a5ed2016-02-17 16:06:22 -0800182 // Codes representing different kinds of file operations. These are used for bucketing
183 // operations in the COUNT_FILEOP_CANCELED histogram.
184 // Do not change or rearrange these values, that will break historical data. Only add to the
185 // list.
186 // Do not use negative numbers or zero; clearcut only handles positive integers.
187 private static final int OPERATION_UNKNOWN = 1;
188 private static final int OPERATION_COPY = 2;
189 private static final int OPERATION_MOVE = 3;
190 private static final int OPERATION_DELETE= 4;
191
192 @IntDef(flag = true, value = {
193 OPERATION_UNKNOWN,
194 OPERATION_COPY,
195 OPERATION_MOVE,
196 OPERATION_DELETE
197 })
198 @Retention(RetentionPolicy.SOURCE)
199 public @interface MetricsOpType {}
200
Aga Wronska4972d712016-03-30 13:55:19 -0700201 // Codes representing different provider types. Used for sorting file operations when logging.
202 private static final int PROVIDER_INTRA = 0;
203 private static final int PROVIDER_SYSTEM = 1;
204 private static final int PROVIDER_EXTERNAL = 2;
205
206 @IntDef(flag = false, value = {
207 PROVIDER_INTRA,
208 PROVIDER_SYSTEM,
209 PROVIDER_EXTERNAL
210 })
211 @Retention(RetentionPolicy.SOURCE)
212 public @interface Provider {}
213
214
Aga Wronska94e53e42016-04-07 13:09:58 -0700215 // Codes representing different user actions. These are used for bucketing stats in the
216 // COUNT_USER_ACTION histogram.
217 // The historgram includes action triggered from menu or invoked by keyboard shortcut.
Aga Wronska4972d712016-03-30 13:55:19 -0700218 // Do not change or rearrange these values, that will break historical data. Only add to the
219 // list.
220 // Do not use negative numbers or zero; clearcut only handles positive integers.
Aga Wronska94e53e42016-04-07 13:09:58 -0700221 public static final int USER_ACTION_OTHER = 1;
222 public static final int USER_ACTION_GRID = 2;
223 public static final int USER_ACTION_LIST = 3;
224 public static final int USER_ACTION_SORT_NAME = 4;
225 public static final int USER_ACTION_SORT_DATE = 5;
226 public static final int USER_ACTION_SORT_SIZE = 6;
227 public static final int USER_ACTION_SEARCH = 7;
228 public static final int USER_ACTION_SHOW_SIZE = 8;
229 public static final int USER_ACTION_HIDE_SIZE = 9;
230 public static final int USER_ACTION_SETTINGS = 10;
231 public static final int USER_ACTION_COPY_TO = 11;
232 public static final int USER_ACTION_MOVE_TO = 12;
233 public static final int USER_ACTION_DELETE = 13;
234 public static final int USER_ACTION_RENAME = 14;
235 public static final int USER_ACTION_CREATE_DIR = 15;
236 public static final int USER_ACTION_SELECT_ALL = 16;
237 public static final int USER_ACTION_SHARE = 17;
238 public static final int USER_ACTION_OPEN = 18;
239 public static final int USER_ACTION_SHOW_ADVANCED = 19;
240 public static final int USER_ACTION_HIDE_ADVANCED = 20;
241 public static final int USER_ACTION_NEW_WINDOW = 21;
242 public static final int USER_ACTION_PASTE_CLIPBOARD = 22;
243 public static final int USER_ACTION_COPY_CLIPBOARD = 23;
244 public static final int USER_ACTION_DRAG_N_DROP = 24;
245 public static final int USER_ACTION_DRAG_N_DROP_MULTI_WINDOW = 25;
Ben Linff4d5842016-04-18 14:35:28 -0700246 public static final int USER_ACTION_CUT_CLIPBOARD = 26;
Aga Wronska4972d712016-03-30 13:55:19 -0700247
248 @IntDef(flag = false, value = {
Aga Wronska94e53e42016-04-07 13:09:58 -0700249 USER_ACTION_OTHER,
250 USER_ACTION_GRID,
251 USER_ACTION_LIST,
252 USER_ACTION_SORT_NAME,
253 USER_ACTION_SORT_DATE,
254 USER_ACTION_SORT_SIZE,
255 USER_ACTION_SEARCH,
256 USER_ACTION_SHOW_SIZE,
257 USER_ACTION_HIDE_SIZE,
258 USER_ACTION_SETTINGS,
259 USER_ACTION_COPY_TO,
260 USER_ACTION_MOVE_TO,
261 USER_ACTION_DELETE,
262 USER_ACTION_RENAME,
263 USER_ACTION_CREATE_DIR,
264 USER_ACTION_SELECT_ALL,
265 USER_ACTION_SHARE,
266 USER_ACTION_OPEN,
267 USER_ACTION_SHOW_ADVANCED,
268 USER_ACTION_HIDE_ADVANCED,
269 USER_ACTION_NEW_WINDOW,
270 USER_ACTION_PASTE_CLIPBOARD,
271 USER_ACTION_COPY_CLIPBOARD,
272 USER_ACTION_DRAG_N_DROP,
Ben Linff4d5842016-04-18 14:35:28 -0700273 USER_ACTION_DRAG_N_DROP_MULTI_WINDOW,
274 USER_ACTION_CUT_CLIPBOARD
Aga Wronska4972d712016-03-30 13:55:19 -0700275 })
276 @Retention(RetentionPolicy.SOURCE)
Aga Wronska94e53e42016-04-07 13:09:58 -0700277 public @interface UserAction {}
Aga Wronska4972d712016-03-30 13:55:19 -0700278
279 // Codes representing different menu actions. These are used for bucketing stats in the
280 // COUNT_MENU_ACTION histogram.
Ben Kwab41a5ed2016-02-17 16:06:22 -0800281 // Do not change or rearrange these values, that will break historical data. Only add to the
282 // list.
283 // Do not use negative numbers or zero; clearcut only handles positive integers.
284 private static final int ACTION_OTHER = 1;
285 private static final int ACTION_OPEN = 2;
286 private static final int ACTION_CREATE = 3;
287 private static final int ACTION_GET_CONTENT = 4;
288 private static final int ACTION_OPEN_TREE = 5;
Steve McKay15b92782016-03-09 15:20:00 -0800289 @Deprecated private static final int ACTION_MANAGE = 6;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800290 private static final int ACTION_BROWSE = 7;
291 private static final int ACTION_PICK_COPY_DESTINATION = 8;
292
293 @IntDef(flag = true, value = {
294 ACTION_OTHER,
295 ACTION_OPEN,
296 ACTION_CREATE,
297 ACTION_GET_CONTENT,
298 ACTION_OPEN_TREE,
299 ACTION_MANAGE,
300 ACTION_BROWSE,
301 ACTION_PICK_COPY_DESTINATION
302 })
303 @Retention(RetentionPolicy.SOURCE)
304 public @interface MetricsAction {}
305
Aga Wronska441b9be2016-03-29 16:57:10 -0700306 // Codes representing different actions to open the drawer. They are used for bucketing stats in
307 // the COUNT_DRAWER_OPENED histogram.
308 // Do not change or rearrange these values, that will break historical data. Only add to the
309 // list.
310 // Do not use negative numbers or zero; clearcut only handles positive integers.
311 private static final int DRAWER_OPENED_HAMBURGER = 1;
312 private static final int DRAWER_OPENED_SWIPE = 2;
313
314 @IntDef(flag = true, value = {
315 DRAWER_OPENED_HAMBURGER,
316 DRAWER_OPENED_SWIPE
317 })
318 @Retention(RetentionPolicy.SOURCE)
319 public @interface DrawerTrigger {}
320
Ben Kwa1c0a3892016-01-26 11:50:03 -0800321 /**
322 * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up.
323 *
324 * @param context
325 * @param state
326 * @param intent
327 */
328 public static void logActivityLaunch(Context context, State state, Intent intent) {
329 // Log the launch action.
Ben Kwab41a5ed2016-02-17 16:06:22 -0800330 logHistogram(context, COUNT_LAUNCH_ACTION, toMetricsAction(state.action));
Ben Kwa1c0a3892016-01-26 11:50:03 -0800331 // Then log auxiliary data (roots/mime types) associated with some actions.
332 Uri uri = intent.getData();
333 switch (state.action) {
334 case State.ACTION_OPEN:
335 logHistogram(context, COUNT_OPEN_MIME, sanitizeMime(intent.getType()));
336 break;
337 case State.ACTION_CREATE:
338 logHistogram(context, COUNT_CREATE_MIME, sanitizeMime(intent.getType()));
339 break;
340 case State.ACTION_GET_CONTENT:
341 logHistogram(context, COUNT_GET_CONTENT_MIME, sanitizeMime(intent.getType()));
342 break;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800343 case State.ACTION_BROWSE:
344 logHistogram(context, COUNT_BROWSE_ROOT, sanitizeRoot(uri));
345 break;
346 default:
347 break;
348 }
349 }
350
351 /**
Garfield, Tan804133e2016-04-20 15:13:56 -0700352 * Logs a root visited event. Call this when the user visits on a root in the RootsFragment.
Ben Kwa1c0a3892016-01-26 11:50:03 -0800353 *
354 * @param context
355 * @param info
356 */
357 public static void logRootVisited(Context context, RootInfo info) {
358 logHistogram(context, COUNT_ROOT_VISITED, sanitizeRoot(info));
359 }
360
361 /**
Garfield, Tan804133e2016-04-20 15:13:56 -0700362 * Logs an app visited event. Call this when the user visits on an app in the RootsFragment.
Ben Kwa1c0a3892016-01-26 11:50:03 -0800363 *
364 * @param context
365 * @param info
366 */
367 public static void logAppVisited(Context context, ResolveInfo info) {
368 logHistogram(context, COUNT_ROOT_VISITED, sanitizeRoot(info));
369 }
370
371 /**
Aga Wronska441b9be2016-03-29 16:57:10 -0700372 * Logs a drawer opened event. Call this when the user opens drawer by swipe or by clicking the
373 * hamburger icon.
374 * @param context
375 * @param trigger type of action that opened the drawer
376 */
377 public static void logDrawerOpened(Context context, @DrawerController.Trigger int trigger) {
378 if (trigger == DrawerController.OPENED_HAMBURGER) {
379 logHistogram(context, COUNT_DRAWER_OPENED, DRAWER_OPENED_HAMBURGER);
380 } else if (trigger == DrawerController.OPENED_SWIPE) {
381 logHistogram(context, COUNT_DRAWER_OPENED, DRAWER_OPENED_SWIPE);
382 }
383 }
384
385 /**
Ben Kwafaa27202016-01-28 16:39:57 -0800386 * Logs file operation stats. Call this when a file operation has completed. The given
387 * DocumentInfo is only used to distinguish broad categories of actions (e.g. copying from one
388 * provider to another vs copying within a given provider). No PII is logged.
389 *
390 * @param context
391 * @param operationType
392 * @param srcs
393 * @param dst
394 */
395 public static void logFileOperation(
396 Context context,
397 @OpType int operationType,
398 List<DocumentInfo> srcs,
399 @Nullable DocumentInfo dst) {
400 ProviderCounts counts = countProviders(srcs, dst);
401
402 if (counts.intraProvider > 0) {
403 logIntraProviderFileOps(context, dst.authority, operationType);
404 }
405 if (counts.systemProvider > 0) {
406 // Log file operations on system providers.
407 logInterProviderFileOps(context, COUNT_FILEOP_SYSTEM, dst, operationType);
408 }
409 if (counts.externalProvider > 0) {
410 // Log file operations on external providers.
411 logInterProviderFileOps(context, COUNT_FILEOP_EXTERNAL, dst, operationType);
412 }
413 }
414
415 /**
Aga Wronska46a868a2016-03-30 10:57:04 -0700416 * Logs create directory operation. It is a part of file operation stats. We do not
417 * differentiate between internal and external locations, all create directory operations are
418 * logged under COUNT_FILEOP_SYSTEM. Call this when a create directory operation has completed.
419 *
420 * @param context
421 */
422 public static void logCreateDirOperation(Context context) {
423 logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_CREATE_DIR);
424 }
425
426 /**
427 * Logs rename file operation. It is a part of file operation stats. We do not differentiate
428 * between internal and external locations, all rename operations are logged under
429 * COUNT_FILEOP_SYSTEM. Call this when a rename file operation has completed.
430 *
431 * @param context
432 */
433 public static void logRenameFileOperation(Context context) {
434 logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_RENAME);
435 }
436
437 /**
Ben Kwafaa27202016-01-28 16:39:57 -0800438 * Logs some kind of file operation error. Call this when a file operation (e.g. copy, delete)
439 * fails.
440 *
441 * @param context
442 * @param operationType
443 * @param failedFiles
444 */
445 public static void logFileOperationErrors(Context context, @OpType int operationType,
446 List<DocumentInfo> failedFiles) {
447 ProviderCounts counts = countProviders(failedFiles, null);
448
449 @FileOp int opCode = FILEOP_OTHER_ERROR;
450 switch (operationType) {
451 case FileOperationService.OPERATION_COPY:
452 opCode = FILEOP_COPY_ERROR;
453 break;
454 case FileOperationService.OPERATION_DELETE:
455 opCode = FILEOP_DELETE_ERROR;
456 break;
457 case FileOperationService.OPERATION_MOVE:
458 opCode = FILEOP_MOVE_ERROR;
459 break;
460 }
461 if (counts.systemProvider > 0) {
462 logHistogram(context, COUNT_FILEOP_SYSTEM, opCode);
463 }
464 if (counts.externalProvider > 0) {
465 logHistogram(context, COUNT_FILEOP_EXTERNAL, opCode);
466 }
467 }
468
469 /**
Aga Wronska46a868a2016-03-30 10:57:04 -0700470 * Logs create directory operation error. We do not differentiate between internal and external
471 * locations, all create directory errors are logged under COUNT_FILEOP_SYSTEM. Call this when a
472 * create directory operation fails.
473 *
474 * @param context
475 */
476 public static void logCreateDirError(Context context) {
Aga Wronska94e53e42016-04-07 13:09:58 -0700477 logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_CREATE_DIR_ERROR);
Aga Wronska46a868a2016-03-30 10:57:04 -0700478 }
479
480 /**
481 * Logs rename file operation error. We do not differentiate between internal and external
482 * locations, all rename errors are logged under COUNT_FILEOP_SYSTEM. Call this
483 * when a rename file operation fails.
484 *
485 * @param context
486 */
487 public static void logRenameFileError(Context context) {
488 logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_RENAME_ERROR);
489 }
490
491 /**
Daichi Hirono320a08f2016-03-25 19:04:39 +0900492 * Logs the cancellation of a file operation. Call this when a Job is canceled.
Ben Kwafaa27202016-01-28 16:39:57 -0800493 * @param context
494 * @param operationType
495 */
496 public static void logFileOperationCancelled(Context context, @OpType int operationType) {
Ben Kwab41a5ed2016-02-17 16:06:22 -0800497 logHistogram(context, COUNT_FILEOP_CANCELED, toMetricsOpType(operationType));
Ben Kwafaa27202016-01-28 16:39:57 -0800498 }
499
Daichi Hirono320a08f2016-03-25 19:04:39 +0900500 /**
501 * Logs startup time in milliseconds.
502 * @param context
503 * @param startupMs Startup time in milliseconds.
504 */
505 public static void logStartupMs(Context context, int startupMs) {
506 logHistogram(context, COUNT_STARTUP_MS, startupMs);
507 }
508
Ben Kwafaa27202016-01-28 16:39:57 -0800509 private static void logInterProviderFileOps(
510 Context context,
511 String histogram,
512 DocumentInfo dst,
513 @OpType int operationType) {
514 if (operationType == FileOperationService.OPERATION_DELETE) {
515 logHistogram(context, histogram, FILEOP_DELETE);
516 } else {
Steve McKay0af8afd2016-02-25 13:34:03 -0800517 assert(dst != null);
Ben Kwafaa27202016-01-28 16:39:57 -0800518 @Provider int providerType =
519 isSystemProvider(dst.authority) ? PROVIDER_SYSTEM : PROVIDER_EXTERNAL;
520 logHistogram(context, histogram, getOpCode(operationType, providerType));
521 }
522 }
523
524 private static void logIntraProviderFileOps(
525 Context context, String authority, @OpType int operationType) {
526 // Find the right histogram to log to, then log the operation.
527 String histogram = isSystemProvider(authority) ? COUNT_FILEOP_SYSTEM : COUNT_FILEOP_EXTERNAL;
528 logHistogram(context, histogram, getOpCode(operationType, PROVIDER_INTRA));
529 }
530
Felipe Lemee4b60122016-02-24 10:17:41 -0800531 // Types for logInvalidScopedAccessRequest
532 public static final String SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS =
Ben Lin5e5cae12016-05-26 16:17:59 -0700533 "docsui_scoped_directory_access_invalid_args";
Felipe Lemee4b60122016-02-24 10:17:41 -0800534 public static final String SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY =
Ben Lin5e5cae12016-05-26 16:17:59 -0700535 "docsui_scoped_directory_access_invalid_dir";
Felipe Lemee4b60122016-02-24 10:17:41 -0800536 public static final String SCOPED_DIRECTORY_ACCESS_ERROR =
Ben Lin5e5cae12016-05-26 16:17:59 -0700537 "docsui_scoped_directory_access_error";
Felipe Lemee4b60122016-02-24 10:17:41 -0800538
539 @StringDef(value = {
540 SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS,
541 SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY,
542 SCOPED_DIRECTORY_ACCESS_ERROR
543 })
544 @Retention(RetentionPolicy.SOURCE)
545 public @interface InvalidScopedAccess{}
546
547 public static void logInvalidScopedAccessRequest(Context context,
548 @InvalidScopedAccess String type) {
Felipe Lemee4b60122016-02-24 10:17:41 -0800549 switch (type) {
550 case SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS:
551 case SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY:
552 case SCOPED_DIRECTORY_ACCESS_ERROR:
Ben Lin6e5a77b2016-05-06 12:30:13 -0700553 logCount(context, type);
Felipe Lemee4b60122016-02-24 10:17:41 -0800554 break;
555 default:
556 Log.wtf(TAG, "invalid InvalidScopedAccess: " + type);
557 }
558 }
559
560 // Types for logValidScopedAccessRequest
561 public static final int SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED = 0;
562 public static final int SCOPED_DIRECTORY_ACCESS_GRANTED = 1;
563 public static final int SCOPED_DIRECTORY_ACCESS_DENIED = 2;
Felipe Leme0bdb7c32016-03-09 17:40:49 -0800564 public static final int SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST = 3;
565 public static final int SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED = 4;
Felipe Lemee4b60122016-02-24 10:17:41 -0800566
567 @IntDef(flag = true, value = {
568 SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED,
569 SCOPED_DIRECTORY_ACCESS_GRANTED,
Felipe Leme0bdb7c32016-03-09 17:40:49 -0800570 SCOPED_DIRECTORY_ACCESS_DENIED,
571 SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST,
572 SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED
Felipe Lemee4b60122016-02-24 10:17:41 -0800573 })
574 @Retention(RetentionPolicy.SOURCE)
575 public @interface ScopedAccessGrant {}
576
577 public static void logValidScopedAccessRequest(Activity activity, String directory,
578 @ScopedAccessGrant int type) {
579 int index = -1;
Felipe Lemeb59a8a62016-03-17 18:56:20 -0700580 if (OpenExternalDirectoryActivity.DIRECTORY_ROOT.equals(directory)) {
581 index = -2;
582 } else {
583 for (int i = 0; i < STANDARD_DIRECTORIES.length; i++) {
584 if (STANDARD_DIRECTORIES[i].equals(directory)) {
585 index = i;
586 break;
587 }
Felipe Lemee4b60122016-02-24 10:17:41 -0800588 }
589 }
590 final String packageName = activity.getCallingPackage();
591 switch (type) {
592 case SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED:
Felipe Leme0bdb7c32016-03-09 17:40:49 -0800593 MetricsLogger.action(activity, MetricsEvent
594 .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_PACKAGE, packageName);
595 MetricsLogger.action(activity, MetricsEvent
596 .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_FOLDER, index);
Felipe Lemee4b60122016-02-24 10:17:41 -0800597 break;
598 case SCOPED_DIRECTORY_ACCESS_GRANTED:
Felipe Leme0bdb7c32016-03-09 17:40:49 -0800599 MetricsLogger.action(activity, MetricsEvent
600 .ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_PACKAGE, packageName);
601 MetricsLogger.action(activity, MetricsEvent
602 .ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_FOLDER, index);
Felipe Lemee4b60122016-02-24 10:17:41 -0800603 break;
604 case SCOPED_DIRECTORY_ACCESS_DENIED:
Felipe Leme0bdb7c32016-03-09 17:40:49 -0800605 MetricsLogger.action(activity, MetricsEvent
606 .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_PACKAGE, packageName);
607 MetricsLogger.action(activity, MetricsEvent
608 .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_FOLDER, index);
609 break;
610 case SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST:
611 MetricsLogger.action(activity, MetricsEvent
612 .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST_BY_PACKAGE, packageName);
613 MetricsLogger.action(activity, MetricsEvent
614 .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST_BY_FOLDER, index);
615 break;
616 case SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED:
617 MetricsLogger.action(activity, MetricsEvent
618 .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED_BY_PACKAGE, packageName);
619 MetricsLogger.action(activity, MetricsEvent
620 .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED_BY_FOLDER, index);
Felipe Lemee4b60122016-02-24 10:17:41 -0800621 break;
622 default:
623 Log.wtf(TAG, "invalid ScopedAccessGrant: " + type);
624 }
625 }
626
Ben Kwafaa27202016-01-28 16:39:57 -0800627 /**
Aga Wronska94e53e42016-04-07 13:09:58 -0700628 * Logs the action that was started by user.
Aga Wronska4972d712016-03-30 13:55:19 -0700629 * @param context
Aga Wronska94e53e42016-04-07 13:09:58 -0700630 * @param userAction
Aga Wronska4972d712016-03-30 13:55:19 -0700631 */
Aga Wronska94e53e42016-04-07 13:09:58 -0700632 public static void logUserAction(Context context, @UserAction int userAction) {
633 logHistogram(context, COUNT_USER_ACTION, userAction);
Aga Wronska4972d712016-03-30 13:55:19 -0700634 }
635
636 /**
Ben Kwa1c0a3892016-01-26 11:50:03 -0800637 * Internal method for making a MetricsLogger.count call. Increments the given counter by 1.
638 *
639 * @param context
640 * @param name The counter to increment.
641 */
642 private static void logCount(Context context, String name) {
643 if (DEBUG) Log.d(TAG, name + ": " + 1);
644 MetricsLogger.count(context, name, 1);
645 }
646
647 /**
648 * Internal method for making a MetricsLogger.histogram call.
649 *
650 * @param context
651 * @param name The name of the histogram.
652 * @param bucket The bucket to increment.
653 */
Aga Wronska6d50bcc2016-03-28 17:27:02 -0700654 private static void logHistogram(Context context, String name, @ActionType int bucket) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800655 if (DEBUG) Log.d(TAG, name + ": " + bucket);
656 MetricsLogger.histogram(context, name, bucket);
657 }
658
659 /**
660 * Generates an integer identifying the given root. For privacy, this function only recognizes a
661 * small set of hard-coded roots (ones provided by the system). Other roots are all grouped into
662 * a single ROOT_OTHER bucket.
663 */
Ben Kwafaa27202016-01-28 16:39:57 -0800664 private static @Root int sanitizeRoot(Uri uri) {
Tomasz Mikolajewski63e2aae2016-02-01 12:01:14 +0900665 if (uri == null || uri.getAuthority() == null || LauncherActivity.isLaunchUri(uri)) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800666 return ROOT_NONE;
667 }
668
669 switch (uri.getAuthority()) {
670 case AUTHORITY_MEDIA:
671 switch (DocumentsContract.getRootId(uri)) {
672 case "audio_root":
673 return ROOT_AUDIO;
674 case "images_root":
675 return ROOT_IMAGES;
676 case "videos_root":
677 return ROOT_VIDEOS;
678 default:
679 return ROOT_OTHER;
680 }
681 case AUTHORITY_STORAGE:
682 if ("home".equals(DocumentsContract.getRootId(uri))) {
683 return ROOT_HOME;
684 } else {
685 return ROOT_DEVICE_STORAGE;
686 }
687 case AUTHORITY_DOWNLOADS:
688 return ROOT_DOWNLOADS;
Ben Kwaebaaea42016-01-28 18:15:07 -0800689 case AUTHORITY_MTP:
690 return ROOT_MTP;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800691 default:
692 return ROOT_OTHER;
693 }
694 }
695
696 /** @see #sanitizeRoot(Uri) */
Ben Kwafaa27202016-01-28 16:39:57 -0800697 private static @Root int sanitizeRoot(RootInfo root) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800698 if (root.isRecents()) {
699 // Recents root is special and only identifiable via this method call. Other roots are
700 // identified by URI.
701 return ROOT_RECENTS;
702 } else {
703 return sanitizeRoot(root.getUri());
704 }
705 }
706
707 /** @see #sanitizeRoot(Uri) */
Ben Kwafaa27202016-01-28 16:39:57 -0800708 private static @Root int sanitizeRoot(ResolveInfo info) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800709 // Log all apps under a single bucket in the roots histogram.
710 return ROOT_THIRD_PARTY_APP;
711 }
712
713 /**
714 * Generates an int identifying a mime type. For privacy, this function only recognizes a small
715 * set of hard-coded types. For any other type, this function returns "other".
716 *
717 * @param mimeType
718 * @return
719 */
Ben Kwafaa27202016-01-28 16:39:57 -0800720 private static @Mime int sanitizeMime(String mimeType) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800721 if (mimeType == null) {
722 return MIME_NONE;
723 } else if ("*/*".equals(mimeType)) {
724 return MIME_ANY;
725 } else {
726 String type = mimeType.substring(0, mimeType.indexOf('/'));
727 switch (type) {
728 case "application":
729 return MIME_APPLICATION;
730 case "audio":
731 return MIME_AUDIO;
732 case "image":
733 return MIME_IMAGE;
734 case "message":
735 return MIME_MESSAGE;
736 case "multipart":
737 return MIME_MULTIPART;
738 case "text":
739 return MIME_TEXT;
740 case "video":
741 return MIME_VIDEO;
742 }
743 }
744 // Bucket all other types into one bucket.
745 return MIME_OTHER;
746 }
Ben Kwafaa27202016-01-28 16:39:57 -0800747
748 private static boolean isSystemProvider(String authority) {
749 switch (authority) {
750 case AUTHORITY_MEDIA:
751 case AUTHORITY_STORAGE:
752 case AUTHORITY_DOWNLOADS:
753 return true;
754 default:
755 return false;
756 }
757 }
758
759 /**
760 * @param operation
761 * @param providerType
762 * @return An opcode, suitable for use as histogram bucket, for the given operation/provider
763 * combination.
764 */
765 private static @FileOp int getOpCode(@OpType int operation, @Provider int providerType) {
766 switch (operation) {
767 case FileOperationService.OPERATION_COPY:
768 switch (providerType) {
769 case PROVIDER_INTRA:
770 return FILEOP_COPY_INTRA_PROVIDER;
771 case PROVIDER_SYSTEM:
772 return FILEOP_COPY_SYSTEM_PROVIDER;
773 case PROVIDER_EXTERNAL:
774 return FILEOP_COPY_EXTERNAL_PROVIDER;
775 }
776 case FileOperationService.OPERATION_MOVE:
777 switch (providerType) {
778 case PROVIDER_INTRA:
779 return FILEOP_MOVE_INTRA_PROVIDER;
780 case PROVIDER_SYSTEM:
781 return FILEOP_MOVE_SYSTEM_PROVIDER;
782 case PROVIDER_EXTERNAL:
783 return FILEOP_MOVE_EXTERNAL_PROVIDER;
784 }
785 case FileOperationService.OPERATION_DELETE:
786 return FILEOP_DELETE;
787 default:
788 Log.w(TAG, "Unrecognized operation type when logging a file operation");
789 return FILEOP_OTHER;
790 }
791 }
792
793 /**
Ben Kwab41a5ed2016-02-17 16:06:22 -0800794 * Maps FileOperationService OpType values, to MetricsOpType values.
795 */
796 private static @MetricsOpType int toMetricsOpType(@OpType int operation) {
797 switch (operation) {
798 case FileOperationService.OPERATION_COPY:
799 return OPERATION_COPY;
800 case FileOperationService.OPERATION_MOVE:
801 return OPERATION_MOVE;
802 case FileOperationService.OPERATION_DELETE:
803 return OPERATION_DELETE;
804 case FileOperationService.OPERATION_UNKNOWN:
805 default:
806 return OPERATION_UNKNOWN;
807 }
808 }
809
810 private static @MetricsAction int toMetricsAction(int action) {
811 switch(action) {
812 case State.ACTION_OPEN:
813 return ACTION_OPEN;
814 case State.ACTION_CREATE:
815 return ACTION_CREATE;
816 case State.ACTION_GET_CONTENT:
817 return ACTION_GET_CONTENT;
818 case State.ACTION_OPEN_TREE:
819 return ACTION_OPEN_TREE;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800820 case State.ACTION_BROWSE:
821 return ACTION_BROWSE;
822 case State.ACTION_PICK_COPY_DESTINATION:
823 return ACTION_PICK_COPY_DESTINATION;
824 default:
825 return ACTION_OTHER;
826 }
827 }
828
829 /**
Ben Kwafaa27202016-01-28 16:39:57 -0800830 * Count the given src documents and provide a tally of how many come from the same provider as
831 * the dst document (if a dst is provided), how many come from system providers, and how many
832 * come from external 3rd-party providers.
833 */
834 private static ProviderCounts countProviders(
835 List<DocumentInfo> srcs, @Nullable DocumentInfo dst) {
836 ProviderCounts counts = new ProviderCounts();
837 for (DocumentInfo doc: srcs) {
838 if (dst != null && doc.authority.equals(dst.authority)) {
839 counts.intraProvider++;
840 } else if (isSystemProvider(doc.authority)){
841 counts.systemProvider++;
842 } else {
843 counts.externalProvider++;
844 }
845 }
846 return counts;
847 }
848
849 private static class ProviderCounts {
850 int intraProvider;
851 int systemProvider;
852 int externalProvider;
853 }
Ben Kwa1c0a3892016-01-26 11:50:03 -0800854}