blob: 066ac5ed9a861a479efc811d55eb9ef9f8528fe6 [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;
Garfield Tan5e1fd5e2017-01-13 15:28:30 -080020import static com.android.documentsui.DocumentsApplication.acquireUnstableProviderOrThrow;
Steve McKayd9caa6a2016-09-15 16:36:45 -070021import static com.android.documentsui.base.Shared.DEBUG;
Steve McKay15b92782016-03-09 15:20:00 -080022
Ben Kwafaa27202016-01-28 16:39:57 -080023import android.annotation.IntDef;
24import android.annotation.Nullable;
Felipe Lemee4b60122016-02-24 10:17:41 -080025import android.annotation.StringDef;
26import android.app.Activity;
Garfield Tan5e1fd5e2017-01-13 15:28:30 -080027import android.content.ContentProviderClient;
Ben Kwa1c0a3892016-01-26 11:50:03 -080028import android.content.Context;
29import android.content.Intent;
30import android.content.pm.ResolveInfo;
31import android.net.Uri;
Garfield Tan5e1fd5e2017-01-13 15:28:30 -080032import android.os.RemoteException;
Ben Kwa1c0a3892016-01-26 11:50:03 -080033import android.provider.DocumentsContract;
Garfield Tan5e1fd5e2017-01-13 15:28:30 -080034import android.provider.DocumentsContract.Path;
Garfield Tane3be6a42017-01-12 14:16:08 -080035import android.provider.DocumentsProvider;
Ben Kwa1c0a3892016-01-26 11:50:03 -080036import android.util.Log;
37
Steve McKayd0805062016-09-15 14:30:38 -070038import com.android.documentsui.base.DocumentInfo;
Steve McKay8659cbc2016-10-31 13:13:36 -070039import com.android.documentsui.base.Providers;
Steve McKayd0805062016-09-15 14:30:38 -070040import com.android.documentsui.base.RootInfo;
Steve McKayd9caa6a2016-09-15 16:36:45 -070041import com.android.documentsui.base.State;
42import com.android.documentsui.base.State.ActionType;
Steve McKayb6006b22016-09-29 09:23:45 -070043import com.android.documentsui.files.LauncherActivity;
Garfield Tan5e1fd5e2017-01-13 15:28:30 -080044import com.android.documentsui.roots.RootsAccess;
Ben Kwafaa27202016-01-28 16:39:57 -080045import com.android.documentsui.services.FileOperationService;
46import com.android.documentsui.services.FileOperationService.OpType;
Ben Kwa1c0a3892016-01-26 11:50:03 -080047import com.android.internal.logging.MetricsLogger;
Felipe Lemee4b60122016-02-24 10:17:41 -080048import com.android.internal.logging.MetricsProto.MetricsEvent;
Ben Kwa1c0a3892016-01-26 11:50:03 -080049
Ben Kwafaa27202016-01-28 16:39:57 -080050import java.lang.annotation.Retention;
51import java.lang.annotation.RetentionPolicy;
52import java.util.List;
53
Ben Kwa1c0a3892016-01-26 11:50:03 -080054/** @hide */
55public final class Metrics {
56 private static final String TAG = "Metrics";
57
Ben Kwa1c0a3892016-01-26 11:50:03 -080058 // These strings have to be whitelisted in tron. Do not change them.
59 private static final String COUNT_LAUNCH_ACTION = "docsui_launch_action";
Garfield Tan2a8719c2017-01-19 16:17:04 -080060 private static final String COUNT_ROOT_VISITED_IN_MANAGER
61 = "docsui_root_visited_in_manager";
62 private static final String COUNT_ROOT_VISITED_IN_PICKER
63 = "docsui_root_visited_in_picker";
Ben Kwa1c0a3892016-01-26 11:50:03 -080064 private static final String COUNT_OPEN_MIME = "docsui_open_mime";
65 private static final String COUNT_CREATE_MIME = "docsui_create_mime";
66 private static final String COUNT_GET_CONTENT_MIME = "docsui_get_content_mime";
67 private static final String COUNT_BROWSE_ROOT = "docsui_browse_root";
Steve McKay15b92782016-03-09 15:20:00 -080068 @Deprecated private static final String COUNT_MANAGE_ROOT = "docsui_manage_root";
Aga Wronska94e53e42016-04-07 13:09:58 -070069 @Deprecated private static final String COUNT_MULTI_WINDOW = "docsui_multi_window";
Ben Kwafaa27202016-01-28 16:39:57 -080070 private static final String COUNT_FILEOP_SYSTEM = "docsui_fileop_system";
71 private static final String COUNT_FILEOP_EXTERNAL = "docsui_fileop_external";
72 private static final String COUNT_FILEOP_CANCELED = "docsui_fileop_canceled";
Daichi Hirono320a08f2016-03-25 19:04:39 +090073 private static final String COUNT_STARTUP_MS = "docsui_startup_ms";
Aga Wronska441b9be2016-03-29 16:57:10 -070074 private static final String COUNT_DRAWER_OPENED = "docsui_drawer_opened";
Aga Wronska94e53e42016-04-07 13:09:58 -070075 private static final String COUNT_USER_ACTION = "docsui_menu_action";
Garfield Tanf8969d62017-02-02 16:55:55 -080076 private static final String COUNT_BROWSE_AT_LOCATION = "docsui_browse_at_location";
Garfield Tane3be6a42017-01-12 14:16:08 -080077 private static final String COUNT_CREATE_AT_LOCATION = "docsui_create_at_location";
78 private static final String COUNT_OPEN_AT_LOCATION = "docsui_open_at_location";
79 private static final String COUNT_GET_CONTENT_AT_LOCATION = "docsui_get_content_at_location";
Garfield Tan5e1fd5e2017-01-13 15:28:30 -080080 private static final String COUNT_MEDIA_FILEOP_FAILURE = "docsui_media_fileop_failure";
81 private static final String COUNT_DOWNLOADS_FILEOP_FAILURE = "docsui_downloads_fileop_failure";
82 private static final String COUNT_INTERNAL_STORAGE_FILEOP_FAILURE
83 = "docsui_internal_storage_fileop_failure";
84 private static final String COUNT_EXTERNAL_STORAGE_FILEOP_FAILURE
85 = "docsui_external_storage_fileop_failure";
86 private static final String COUNT_MTP_FILEOP_FAILURE = "docsui_mtp_fileop_failure";
87 private static final String COUNT_OTHER_FILEOP_FAILURE = "docsui_other_fileop_failure";
Ben Kwa1c0a3892016-01-26 11:50:03 -080088
89 // Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
90 // root that is not explicitly recognized by the Metrics code (see {@link
Ben Kwab41a5ed2016-02-17 16:06:22 -080091 // #getSanitizedRootIndex}). Apps are also bucketed in this histogram.
Ben Kwaebaaea42016-01-28 18:15:07 -080092 // Do not change or rearrange these values, that will break historical data. Only add to the end
93 // of the list.
Ben Kwab41a5ed2016-02-17 16:06:22 -080094 // Do not use negative numbers or zero; clearcut only handles positive integers.
95 private static final int ROOT_NONE = 1;
96 private static final int ROOT_OTHER = 2;
97 private static final int ROOT_AUDIO = 3;
98 private static final int ROOT_DEVICE_STORAGE = 4;
99 private static final int ROOT_DOWNLOADS = 5;
100 private static final int ROOT_HOME = 6;
101 private static final int ROOT_IMAGES = 7;
102 private static final int ROOT_RECENTS = 8;
103 private static final int ROOT_VIDEOS = 9;
104 private static final int ROOT_MTP = 10;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800105 // 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 -0800106 // are logged analogously to roots.
107 private static final int ROOT_THIRD_PARTY_APP = 100;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800108
Ben Kwafaa27202016-01-28 16:39:57 -0800109 @IntDef(flag = true, value = {
110 ROOT_NONE,
111 ROOT_OTHER,
112 ROOT_AUDIO,
113 ROOT_DEVICE_STORAGE,
114 ROOT_DOWNLOADS,
115 ROOT_HOME,
116 ROOT_IMAGES,
117 ROOT_RECENTS,
118 ROOT_VIDEOS,
Ben Kwaebaaea42016-01-28 18:15:07 -0800119 ROOT_MTP,
Ben Kwafaa27202016-01-28 16:39:57 -0800120 ROOT_THIRD_PARTY_APP
121 })
122 @Retention(RetentionPolicy.SOURCE)
123 public @interface Root {}
124
Ben Kwa1c0a3892016-01-26 11:50:03 -0800125 // Indices for bucketing mime types.
Ben Kwab41a5ed2016-02-17 16:06:22 -0800126 // Do not change or rearrange these values, that will break historical data. Only add to the end
127 // of the list.
128 // Do not use negative numbers or zero; clearcut only handles positive integers.
129 private static final int MIME_NONE = 1; // null mime
130 private static final int MIME_ANY = 2; // */*
131 private static final int MIME_APPLICATION = 3; // application/*
132 private static final int MIME_AUDIO = 4; // audio/*
133 private static final int MIME_IMAGE = 5; // image/*
134 private static final int MIME_MESSAGE = 6; // message/*
135 private static final int MIME_MULTIPART = 7; // multipart/*
136 private static final int MIME_TEXT = 8; // text/*
137 private static final int MIME_VIDEO = 9; // video/*
138 private static final int MIME_OTHER = 10; // anything not enumerated below
Ben Kwa1c0a3892016-01-26 11:50:03 -0800139
Ben Kwafaa27202016-01-28 16:39:57 -0800140 @IntDef(flag = true, value = {
Ben Kwafaa27202016-01-28 16:39:57 -0800141 MIME_NONE,
142 MIME_ANY,
143 MIME_APPLICATION,
144 MIME_AUDIO,
145 MIME_IMAGE,
146 MIME_MESSAGE,
147 MIME_MULTIPART,
148 MIME_TEXT,
Ben Kwab41a5ed2016-02-17 16:06:22 -0800149 MIME_VIDEO,
150 MIME_OTHER
Ben Kwafaa27202016-01-28 16:39:57 -0800151 })
152 @Retention(RetentionPolicy.SOURCE)
153 public @interface Mime {}
154
Garfield Tan2a8719c2017-01-19 16:17:04 -0800155 public static final int FILES_SCOPE = 1;
156 public static final int PICKER_SCOPE = 2;
157
158 @IntDef({ FILES_SCOPE, PICKER_SCOPE })
159 @Retention(RetentionPolicy.SOURCE)
160 public @interface ContextScope {}
161
Ben Kwafaa27202016-01-28 16:39:57 -0800162 // Codes representing different kinds of file operations. These are used for bucketing
163 // operations in the COUNT_FILEOP_{SYSTEM|EXTERNAL} histograms.
Ben Kwab41a5ed2016-02-17 16:06:22 -0800164 // Do not change or rearrange these values, that will break historical data. Only add to the
165 // list.
166 // Do not use negative numbers or zero; clearcut only handles positive integers.
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900167 //
168 // Next available ID: 112
Ben Kwab41a5ed2016-02-17 16:06:22 -0800169 private static final int FILEOP_OTHER = 1; // any file operation not listed below
170 private static final int FILEOP_COPY_INTRA_PROVIDER = 2; // Copy within a provider
171 private static final int FILEOP_COPY_SYSTEM_PROVIDER = 3; // Copy to a system provider.
172 private static final int FILEOP_COPY_EXTERNAL_PROVIDER = 4; // Copy to a 3rd-party provider.
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900173 private static final int FILEOP_COMPRESS_INTRA_PROVIDER = 106; // Compres within a provider
174 private static final int FILEOP_COMPRESS_SYSTEM_PROVIDER = 107; // Compress to a system provider.
175 private static final int FILEOP_COMPRESS_EXTERNAL_PROVIDER = 108; // Compress to a 3rd-party provider.
176 private static final int FILEOP_EXTRACT_INTRA_PROVIDER = 109; // Extract within a provider
177 private static final int FILEOP_EXTRACT_SYSTEM_PROVIDER = 110; // Extract to a system provider.
178 private static final int FILEOP_EXTRACT_EXTERNAL_PROVIDER = 111; // Extract to a 3rd-party provider.
Ben Kwab41a5ed2016-02-17 16:06:22 -0800179 private static final int FILEOP_MOVE_INTRA_PROVIDER = 5; // Move within a provider.
180 private static final int FILEOP_MOVE_SYSTEM_PROVIDER = 6; // Move to a system provider.
181 private static final int FILEOP_MOVE_EXTERNAL_PROVIDER = 7; // Move to a 3rd-party provider.
182 private static final int FILEOP_DELETE = 8;
Aga Wronska46a868a2016-03-30 10:57:04 -0700183 private static final int FILEOP_RENAME = 9;
184 private static final int FILEOP_CREATE_DIR = 10;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800185 private static final int FILEOP_OTHER_ERROR = 100;
186 private static final int FILEOP_DELETE_ERROR = 101;
187 private static final int FILEOP_MOVE_ERROR = 102;
188 private static final int FILEOP_COPY_ERROR = 103;
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900189 private static final int FILEOP_COMPRESS_ERROR = 112;
190 private static final int FILEOP_EXTRACT_ERROR = 113;
Aga Wronska46a868a2016-03-30 10:57:04 -0700191 private static final int FILEOP_RENAME_ERROR = 104;
192 private static final int FILEOP_CREATE_DIR_ERROR = 105;
Ben Kwafaa27202016-01-28 16:39:57 -0800193
194 @IntDef(flag = true, value = {
195 FILEOP_OTHER,
196 FILEOP_COPY_INTRA_PROVIDER,
197 FILEOP_COPY_SYSTEM_PROVIDER,
198 FILEOP_COPY_EXTERNAL_PROVIDER,
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900199 FILEOP_COMPRESS_INTRA_PROVIDER,
200 FILEOP_COMPRESS_SYSTEM_PROVIDER,
201 FILEOP_COMPRESS_EXTERNAL_PROVIDER,
202 FILEOP_EXTRACT_INTRA_PROVIDER,
203 FILEOP_EXTRACT_SYSTEM_PROVIDER,
204 FILEOP_EXTRACT_EXTERNAL_PROVIDER,
Ben Kwafaa27202016-01-28 16:39:57 -0800205 FILEOP_MOVE_INTRA_PROVIDER,
206 FILEOP_MOVE_SYSTEM_PROVIDER,
207 FILEOP_MOVE_EXTERNAL_PROVIDER,
208 FILEOP_DELETE,
Aga Wronska46a868a2016-03-30 10:57:04 -0700209 FILEOP_RENAME,
210 FILEOP_CREATE_DIR,
Ben Kwafaa27202016-01-28 16:39:57 -0800211 FILEOP_OTHER_ERROR,
212 FILEOP_COPY_ERROR,
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900213 FILEOP_COMPRESS_ERROR,
214 FILEOP_EXTRACT_ERROR,
Ben Kwafaa27202016-01-28 16:39:57 -0800215 FILEOP_MOVE_ERROR,
Aga Wronska46a868a2016-03-30 10:57:04 -0700216 FILEOP_DELETE_ERROR,
217 FILEOP_RENAME_ERROR,
218 FILEOP_CREATE_DIR_ERROR
Ben Kwafaa27202016-01-28 16:39:57 -0800219 })
220 @Retention(RetentionPolicy.SOURCE)
221 public @interface FileOp {}
222
Ben Kwab41a5ed2016-02-17 16:06:22 -0800223 // Codes representing different kinds of file operations. These are used for bucketing
224 // operations in the COUNT_FILEOP_CANCELED histogram.
225 // Do not change or rearrange these values, that will break historical data. Only add to the
226 // list.
227 // Do not use negative numbers or zero; clearcut only handles positive integers.
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900228 //
229 // Next available ID: 7
Ben Kwab41a5ed2016-02-17 16:06:22 -0800230 private static final int OPERATION_UNKNOWN = 1;
231 private static final int OPERATION_COPY = 2;
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900232 private static final int OPERATION_COMPRESS = 5;
233 private static final int OPERATION_EXTRACT = 6;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800234 private static final int OPERATION_MOVE = 3;
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900235 private static final int OPERATION_DELETE = 4;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800236
237 @IntDef(flag = true, value = {
238 OPERATION_UNKNOWN,
239 OPERATION_COPY,
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900240 OPERATION_COMPRESS,
241 OPERATION_EXTRACT,
Ben Kwab41a5ed2016-02-17 16:06:22 -0800242 OPERATION_MOVE,
243 OPERATION_DELETE
244 })
245 @Retention(RetentionPolicy.SOURCE)
246 public @interface MetricsOpType {}
247
Aga Wronska4972d712016-03-30 13:55:19 -0700248 // Codes representing different provider types. Used for sorting file operations when logging.
249 private static final int PROVIDER_INTRA = 0;
250 private static final int PROVIDER_SYSTEM = 1;
251 private static final int PROVIDER_EXTERNAL = 2;
252
253 @IntDef(flag = false, value = {
254 PROVIDER_INTRA,
255 PROVIDER_SYSTEM,
256 PROVIDER_EXTERNAL
257 })
258 @Retention(RetentionPolicy.SOURCE)
259 public @interface Provider {}
260
Garfield Tan5e1fd5e2017-01-13 15:28:30 -0800261 // Codes representing different types of sub-fileops. These are used for bucketing fileop
262 // failures in COUNT_*_FILEOP_FAILURE.
263 public static final int SUBFILEOP_QUERY_DOCUMENT = 1;
264 public static final int SUBFILEOP_QUERY_CHILDREN = 2;
265 public static final int SUBFILEOP_OPEN_FILE = 3;
266 public static final int SUBFILEOP_READ_FILE = 4;
267 public static final int SUBFILEOP_CREATE_DOCUMENT = 5;
268 public static final int SUBFILEOP_WRITE_FILE = 6;
269 public static final int SUBFILEOP_DELETE_DOCUMENT = 7;
270 public static final int SUBFILEOP_OBTAIN_STREAM_TYPE = 8;
271 public static final int SUBFILEOP_QUICK_MOVE = 9;
272 public static final int SUBFILEOP_QUICK_COPY = 10;
273
274 @IntDef(flag = false, value = {
275 SUBFILEOP_QUERY_DOCUMENT,
276 SUBFILEOP_QUERY_CHILDREN,
277 SUBFILEOP_OPEN_FILE,
278 SUBFILEOP_READ_FILE,
279 SUBFILEOP_CREATE_DOCUMENT,
280 SUBFILEOP_WRITE_FILE,
281 SUBFILEOP_DELETE_DOCUMENT,
282 SUBFILEOP_OBTAIN_STREAM_TYPE,
283 SUBFILEOP_QUICK_MOVE,
284 SUBFILEOP_QUICK_COPY
285 })
286 @Retention(RetentionPolicy.SOURCE)
287 public @interface SubFileOp {}
Aga Wronska4972d712016-03-30 13:55:19 -0700288
Aga Wronska94e53e42016-04-07 13:09:58 -0700289 // Codes representing different user actions. These are used for bucketing stats in the
290 // COUNT_USER_ACTION histogram.
291 // The historgram includes action triggered from menu or invoked by keyboard shortcut.
Aga Wronska4972d712016-03-30 13:55:19 -0700292 // Do not change or rearrange these values, that will break historical data. Only add to the
293 // list.
294 // Do not use negative numbers or zero; clearcut only handles positive integers.
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900295 //
296 // Next available ID: 29
Aga Wronska94e53e42016-04-07 13:09:58 -0700297 public static final int USER_ACTION_OTHER = 1;
298 public static final int USER_ACTION_GRID = 2;
299 public static final int USER_ACTION_LIST = 3;
300 public static final int USER_ACTION_SORT_NAME = 4;
301 public static final int USER_ACTION_SORT_DATE = 5;
302 public static final int USER_ACTION_SORT_SIZE = 6;
303 public static final int USER_ACTION_SEARCH = 7;
304 public static final int USER_ACTION_SHOW_SIZE = 8;
305 public static final int USER_ACTION_HIDE_SIZE = 9;
306 public static final int USER_ACTION_SETTINGS = 10;
307 public static final int USER_ACTION_COPY_TO = 11;
Tomasz Mikolajewski2e8acf02017-02-09 17:24:23 +0900308 public static final int USER_ACTION_COMPRESS = 27;
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900309 public static final int USER_ACTION_EXTRACT_TO = 28;
Aga Wronska94e53e42016-04-07 13:09:58 -0700310 public static final int USER_ACTION_MOVE_TO = 12;
311 public static final int USER_ACTION_DELETE = 13;
312 public static final int USER_ACTION_RENAME = 14;
313 public static final int USER_ACTION_CREATE_DIR = 15;
314 public static final int USER_ACTION_SELECT_ALL = 16;
315 public static final int USER_ACTION_SHARE = 17;
316 public static final int USER_ACTION_OPEN = 18;
317 public static final int USER_ACTION_SHOW_ADVANCED = 19;
318 public static final int USER_ACTION_HIDE_ADVANCED = 20;
319 public static final int USER_ACTION_NEW_WINDOW = 21;
320 public static final int USER_ACTION_PASTE_CLIPBOARD = 22;
321 public static final int USER_ACTION_COPY_CLIPBOARD = 23;
322 public static final int USER_ACTION_DRAG_N_DROP = 24;
323 public static final int USER_ACTION_DRAG_N_DROP_MULTI_WINDOW = 25;
Ben Linff4d5842016-04-18 14:35:28 -0700324 public static final int USER_ACTION_CUT_CLIPBOARD = 26;
Jon Mann253a9922017-03-21 18:53:27 -0700325 public static final int USER_ACTION_VIEW_IN_APPLICATION = 27;
Aga Wronska4972d712016-03-30 13:55:19 -0700326
327 @IntDef(flag = false, value = {
Aga Wronska94e53e42016-04-07 13:09:58 -0700328 USER_ACTION_OTHER,
329 USER_ACTION_GRID,
330 USER_ACTION_LIST,
331 USER_ACTION_SORT_NAME,
332 USER_ACTION_SORT_DATE,
333 USER_ACTION_SORT_SIZE,
334 USER_ACTION_SEARCH,
335 USER_ACTION_SHOW_SIZE,
336 USER_ACTION_HIDE_SIZE,
337 USER_ACTION_SETTINGS,
338 USER_ACTION_COPY_TO,
Tomasz Mikolajewski2e8acf02017-02-09 17:24:23 +0900339 USER_ACTION_COMPRESS,
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900340 USER_ACTION_EXTRACT_TO,
Aga Wronska94e53e42016-04-07 13:09:58 -0700341 USER_ACTION_MOVE_TO,
342 USER_ACTION_DELETE,
343 USER_ACTION_RENAME,
344 USER_ACTION_CREATE_DIR,
345 USER_ACTION_SELECT_ALL,
346 USER_ACTION_SHARE,
347 USER_ACTION_OPEN,
348 USER_ACTION_SHOW_ADVANCED,
349 USER_ACTION_HIDE_ADVANCED,
350 USER_ACTION_NEW_WINDOW,
351 USER_ACTION_PASTE_CLIPBOARD,
352 USER_ACTION_COPY_CLIPBOARD,
353 USER_ACTION_DRAG_N_DROP,
Ben Linff4d5842016-04-18 14:35:28 -0700354 USER_ACTION_DRAG_N_DROP_MULTI_WINDOW,
Jon Mann253a9922017-03-21 18:53:27 -0700355 USER_ACTION_CUT_CLIPBOARD,
356 USER_ACTION_VIEW_IN_APPLICATION
Aga Wronska4972d712016-03-30 13:55:19 -0700357 })
358 @Retention(RetentionPolicy.SOURCE)
Aga Wronska94e53e42016-04-07 13:09:58 -0700359 public @interface UserAction {}
Aga Wronska4972d712016-03-30 13:55:19 -0700360
361 // Codes representing different menu actions. These are used for bucketing stats in the
362 // COUNT_MENU_ACTION histogram.
Ben Kwab41a5ed2016-02-17 16:06:22 -0800363 // Do not change or rearrange these values, that will break historical data. Only add to the
364 // list.
365 // Do not use negative numbers or zero; clearcut only handles positive integers.
366 private static final int ACTION_OTHER = 1;
367 private static final int ACTION_OPEN = 2;
368 private static final int ACTION_CREATE = 3;
369 private static final int ACTION_GET_CONTENT = 4;
370 private static final int ACTION_OPEN_TREE = 5;
Steve McKay15b92782016-03-09 15:20:00 -0800371 @Deprecated private static final int ACTION_MANAGE = 6;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800372 private static final int ACTION_BROWSE = 7;
373 private static final int ACTION_PICK_COPY_DESTINATION = 8;
374
375 @IntDef(flag = true, value = {
376 ACTION_OTHER,
377 ACTION_OPEN,
378 ACTION_CREATE,
379 ACTION_GET_CONTENT,
380 ACTION_OPEN_TREE,
381 ACTION_MANAGE,
382 ACTION_BROWSE,
383 ACTION_PICK_COPY_DESTINATION
384 })
385 @Retention(RetentionPolicy.SOURCE)
386 public @interface MetricsAction {}
387
Aga Wronska441b9be2016-03-29 16:57:10 -0700388 // Codes representing different actions to open the drawer. They are used for bucketing stats in
389 // the COUNT_DRAWER_OPENED histogram.
390 // Do not change or rearrange these values, that will break historical data. Only add to the
391 // list.
392 // Do not use negative numbers or zero; clearcut only handles positive integers.
393 private static final int DRAWER_OPENED_HAMBURGER = 1;
394 private static final int DRAWER_OPENED_SWIPE = 2;
395
396 @IntDef(flag = true, value = {
397 DRAWER_OPENED_HAMBURGER,
398 DRAWER_OPENED_SWIPE
399 })
400 @Retention(RetentionPolicy.SOURCE)
401 public @interface DrawerTrigger {}
402
Ben Kwa1c0a3892016-01-26 11:50:03 -0800403 /**
404 * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up.
405 *
406 * @param context
407 * @param state
408 * @param intent
409 */
410 public static void logActivityLaunch(Context context, State state, Intent intent) {
411 // Log the launch action.
Ben Kwab41a5ed2016-02-17 16:06:22 -0800412 logHistogram(context, COUNT_LAUNCH_ACTION, toMetricsAction(state.action));
Ben Kwa1c0a3892016-01-26 11:50:03 -0800413 // Then log auxiliary data (roots/mime types) associated with some actions.
414 Uri uri = intent.getData();
415 switch (state.action) {
416 case State.ACTION_OPEN:
417 logHistogram(context, COUNT_OPEN_MIME, sanitizeMime(intent.getType()));
418 break;
419 case State.ACTION_CREATE:
420 logHistogram(context, COUNT_CREATE_MIME, sanitizeMime(intent.getType()));
421 break;
422 case State.ACTION_GET_CONTENT:
423 logHistogram(context, COUNT_GET_CONTENT_MIME, sanitizeMime(intent.getType()));
424 break;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800425 case State.ACTION_BROWSE:
426 logHistogram(context, COUNT_BROWSE_ROOT, sanitizeRoot(uri));
427 break;
428 default:
429 break;
430 }
431 }
432
433 /**
Garfield Tane3be6a42017-01-12 14:16:08 -0800434 * Logs when DocumentsUI are launched with {@link DocumentsContract#EXTRA_INITIAL_URI}.
435 *
436 * @param context
437 * @param state used to resolve action
438 * @param rootUri the resolved rootUri, or {@code null} if the provider doesn't
439 * support {@link DocumentsProvider#findDocumentPath(String, String)}
440 */
441 public static void logLaunchAtLocation(Context context, State state, @Nullable Uri rootUri) {
442 switch (state.action) {
Garfield Tanf8969d62017-02-02 16:55:55 -0800443 case State.ACTION_BROWSE:
444 logHistogram(context, COUNT_BROWSE_AT_LOCATION, sanitizeRoot(rootUri));
445 break;
Garfield Tane3be6a42017-01-12 14:16:08 -0800446 case State.ACTION_CREATE:
447 logHistogram(context, COUNT_CREATE_AT_LOCATION, sanitizeRoot(rootUri));
448 break;
449 case State.ACTION_GET_CONTENT:
450 logHistogram(context, COUNT_GET_CONTENT_AT_LOCATION, sanitizeRoot(rootUri));
451 break;
452 case State.ACTION_OPEN:
453 logHistogram(context, COUNT_OPEN_AT_LOCATION, sanitizeRoot(rootUri));
454 break;
455 }
456 }
457
458 /**
Garfield Tan2a8719c2017-01-19 16:17:04 -0800459 * Logs a root visited event in file managers. Call this when the user
460 * taps on a root in {@link com.android.documentsui.sidebar.RootsFragment}.
Ben Kwa1c0a3892016-01-26 11:50:03 -0800461 *
462 * @param context
Garfield Tan2a8719c2017-01-19 16:17:04 -0800463 * @param scope
Ben Kwa1c0a3892016-01-26 11:50:03 -0800464 * @param info
465 */
Garfield Tan2a8719c2017-01-19 16:17:04 -0800466 public static void logRootVisited(
467 Context context, @ContextScope int scope, RootInfo info) {
468 switch (scope) {
469 case FILES_SCOPE:
470 logHistogram(context, COUNT_ROOT_VISITED_IN_MANAGER,
471 sanitizeRoot(info));
472 break;
473 case PICKER_SCOPE:
474 logHistogram(context, COUNT_ROOT_VISITED_IN_PICKER,
475 sanitizeRoot(info));
476 break;
477 }
Ben Kwa1c0a3892016-01-26 11:50:03 -0800478 }
479
480 /**
Garfield Tan2a8719c2017-01-19 16:17:04 -0800481 * Logs an app visited event in file pickers. Call this when the user visits
482 * on an app in the RootsFragment.
Ben Kwa1c0a3892016-01-26 11:50:03 -0800483 *
484 * @param context
485 * @param info
486 */
487 public static void logAppVisited(Context context, ResolveInfo info) {
Garfield Tan2a8719c2017-01-19 16:17:04 -0800488 logHistogram(context, COUNT_ROOT_VISITED_IN_PICKER, sanitizeRoot(info));
Ben Kwa1c0a3892016-01-26 11:50:03 -0800489 }
490
491 /**
Aga Wronska441b9be2016-03-29 16:57:10 -0700492 * Logs a drawer opened event. Call this when the user opens drawer by swipe or by clicking the
493 * hamburger icon.
494 * @param context
495 * @param trigger type of action that opened the drawer
496 */
497 public static void logDrawerOpened(Context context, @DrawerController.Trigger int trigger) {
498 if (trigger == DrawerController.OPENED_HAMBURGER) {
499 logHistogram(context, COUNT_DRAWER_OPENED, DRAWER_OPENED_HAMBURGER);
500 } else if (trigger == DrawerController.OPENED_SWIPE) {
501 logHistogram(context, COUNT_DRAWER_OPENED, DRAWER_OPENED_SWIPE);
502 }
503 }
504
505 /**
Ben Kwafaa27202016-01-28 16:39:57 -0800506 * Logs file operation stats. Call this when a file operation has completed. The given
507 * DocumentInfo is only used to distinguish broad categories of actions (e.g. copying from one
508 * provider to another vs copying within a given provider). No PII is logged.
509 *
510 * @param context
511 * @param operationType
512 * @param srcs
513 * @param dst
514 */
515 public static void logFileOperation(
516 Context context,
517 @OpType int operationType,
518 List<DocumentInfo> srcs,
519 @Nullable DocumentInfo dst) {
Steve McKay99f1dc32016-12-29 16:02:01 -0800520
521 ProviderCounts counts = new ProviderCounts();
522 countProviders(counts, srcs, dst);
Ben Kwafaa27202016-01-28 16:39:57 -0800523
524 if (counts.intraProvider > 0) {
525 logIntraProviderFileOps(context, dst.authority, operationType);
526 }
527 if (counts.systemProvider > 0) {
528 // Log file operations on system providers.
529 logInterProviderFileOps(context, COUNT_FILEOP_SYSTEM, dst, operationType);
530 }
531 if (counts.externalProvider > 0) {
532 // Log file operations on external providers.
533 logInterProviderFileOps(context, COUNT_FILEOP_EXTERNAL, dst, operationType);
534 }
535 }
536
537 /**
Aga Wronska46a868a2016-03-30 10:57:04 -0700538 * Logs create directory operation. It is a part of file operation stats. We do not
539 * differentiate between internal and external locations, all create directory operations are
540 * logged under COUNT_FILEOP_SYSTEM. Call this when a create directory operation has completed.
541 *
542 * @param context
543 */
544 public static void logCreateDirOperation(Context context) {
545 logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_CREATE_DIR);
546 }
547
548 /**
549 * Logs rename file operation. It is a part of file operation stats. We do not differentiate
550 * between internal and external locations, all rename operations are logged under
551 * COUNT_FILEOP_SYSTEM. Call this when a rename file operation has completed.
552 *
553 * @param context
554 */
555 public static void logRenameFileOperation(Context context) {
556 logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_RENAME);
557 }
558
559 /**
Ben Kwafaa27202016-01-28 16:39:57 -0800560 * Logs some kind of file operation error. Call this when a file operation (e.g. copy, delete)
561 * fails.
562 *
563 * @param context
564 * @param operationType
565 * @param failedFiles
566 */
567 public static void logFileOperationErrors(Context context, @OpType int operationType,
Steve McKay99f1dc32016-12-29 16:02:01 -0800568 List<DocumentInfo> failedFiles, List<Uri> failedUris) {
569
570 ProviderCounts counts = new ProviderCounts();
571 countProviders(counts, failedFiles, null);
572
573 // TODO: Report URI errors separate from file operation errors.
574 countProviders(counts, failedUris);
Ben Kwafaa27202016-01-28 16:39:57 -0800575
576 @FileOp int opCode = FILEOP_OTHER_ERROR;
577 switch (operationType) {
578 case FileOperationService.OPERATION_COPY:
579 opCode = FILEOP_COPY_ERROR;
580 break;
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900581 case FileOperationService.OPERATION_COMPRESS:
582 opCode = FILEOP_COMPRESS_ERROR;
583 break;
584 case FileOperationService.OPERATION_EXTRACT:
585 opCode = FILEOP_EXTRACT_ERROR;
586 break;
Ben Kwafaa27202016-01-28 16:39:57 -0800587 case FileOperationService.OPERATION_DELETE:
588 opCode = FILEOP_DELETE_ERROR;
589 break;
590 case FileOperationService.OPERATION_MOVE:
591 opCode = FILEOP_MOVE_ERROR;
592 break;
593 }
594 if (counts.systemProvider > 0) {
595 logHistogram(context, COUNT_FILEOP_SYSTEM, opCode);
596 }
597 if (counts.externalProvider > 0) {
598 logHistogram(context, COUNT_FILEOP_EXTERNAL, opCode);
599 }
600 }
601
Garfield Tan5e1fd5e2017-01-13 15:28:30 -0800602 public static void logFileOperationFailure(
603 Context context, @SubFileOp int subFileOp, Uri docUri) {
604 final String authority = docUri.getAuthority();
605 switch (authority) {
606 case Providers.AUTHORITY_MEDIA:
607 logHistogram(context, COUNT_MEDIA_FILEOP_FAILURE, subFileOp);
608 break;
609 case Providers.AUTHORITY_STORAGE:
610 logStorageFileOperationFailure(context, subFileOp, docUri);
611 break;
612 case Providers.AUTHORITY_DOWNLOADS:
613 logHistogram(context, COUNT_DOWNLOADS_FILEOP_FAILURE, subFileOp);
614 break;
615 case Providers.AUTHORITY_MTP:
616 logHistogram(context, COUNT_MTP_FILEOP_FAILURE, subFileOp);
617 break;
618 default:
619 logHistogram(context, COUNT_OTHER_FILEOP_FAILURE, subFileOp);
620 break;
621 }
622 }
623
Ben Kwafaa27202016-01-28 16:39:57 -0800624 /**
Aga Wronska46a868a2016-03-30 10:57:04 -0700625 * Logs create directory operation error. We do not differentiate between internal and external
626 * locations, all create directory errors are logged under COUNT_FILEOP_SYSTEM. Call this when a
627 * create directory operation fails.
628 *
629 * @param context
630 */
631 public static void logCreateDirError(Context context) {
Aga Wronska94e53e42016-04-07 13:09:58 -0700632 logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_CREATE_DIR_ERROR);
Aga Wronska46a868a2016-03-30 10:57:04 -0700633 }
634
635 /**
636 * Logs rename file operation error. We do not differentiate between internal and external
637 * locations, all rename errors are logged under COUNT_FILEOP_SYSTEM. Call this
638 * when a rename file operation fails.
639 *
640 * @param context
641 */
642 public static void logRenameFileError(Context context) {
643 logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_RENAME_ERROR);
644 }
645
646 /**
Daichi Hirono320a08f2016-03-25 19:04:39 +0900647 * Logs the cancellation of a file operation. Call this when a Job is canceled.
Ben Kwafaa27202016-01-28 16:39:57 -0800648 * @param context
649 * @param operationType
650 */
651 public static void logFileOperationCancelled(Context context, @OpType int operationType) {
Ben Kwab41a5ed2016-02-17 16:06:22 -0800652 logHistogram(context, COUNT_FILEOP_CANCELED, toMetricsOpType(operationType));
Ben Kwafaa27202016-01-28 16:39:57 -0800653 }
654
Daichi Hirono320a08f2016-03-25 19:04:39 +0900655 /**
656 * Logs startup time in milliseconds.
657 * @param context
658 * @param startupMs Startup time in milliseconds.
659 */
660 public static void logStartupMs(Context context, int startupMs) {
661 logHistogram(context, COUNT_STARTUP_MS, startupMs);
662 }
663
Ben Kwafaa27202016-01-28 16:39:57 -0800664 private static void logInterProviderFileOps(
665 Context context,
666 String histogram,
667 DocumentInfo dst,
668 @OpType int operationType) {
669 if (operationType == FileOperationService.OPERATION_DELETE) {
670 logHistogram(context, histogram, FILEOP_DELETE);
671 } else {
Steve McKay0af8afd2016-02-25 13:34:03 -0800672 assert(dst != null);
Ben Kwafaa27202016-01-28 16:39:57 -0800673 @Provider int providerType =
674 isSystemProvider(dst.authority) ? PROVIDER_SYSTEM : PROVIDER_EXTERNAL;
675 logHistogram(context, histogram, getOpCode(operationType, providerType));
676 }
677 }
678
679 private static void logIntraProviderFileOps(
680 Context context, String authority, @OpType int operationType) {
681 // Find the right histogram to log to, then log the operation.
682 String histogram = isSystemProvider(authority) ? COUNT_FILEOP_SYSTEM : COUNT_FILEOP_EXTERNAL;
683 logHistogram(context, histogram, getOpCode(operationType, PROVIDER_INTRA));
684 }
685
Felipe Lemee4b60122016-02-24 10:17:41 -0800686 // Types for logInvalidScopedAccessRequest
687 public static final String SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS =
Ben Lin5e5cae12016-05-26 16:17:59 -0700688 "docsui_scoped_directory_access_invalid_args";
Felipe Lemee4b60122016-02-24 10:17:41 -0800689 public static final String SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY =
Ben Lin5e5cae12016-05-26 16:17:59 -0700690 "docsui_scoped_directory_access_invalid_dir";
Felipe Lemee4b60122016-02-24 10:17:41 -0800691 public static final String SCOPED_DIRECTORY_ACCESS_ERROR =
Ben Lin5e5cae12016-05-26 16:17:59 -0700692 "docsui_scoped_directory_access_error";
Felipe Lemee4b60122016-02-24 10:17:41 -0800693
694 @StringDef(value = {
695 SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS,
696 SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY,
697 SCOPED_DIRECTORY_ACCESS_ERROR
698 })
699 @Retention(RetentionPolicy.SOURCE)
700 public @interface InvalidScopedAccess{}
701
702 public static void logInvalidScopedAccessRequest(Context context,
703 @InvalidScopedAccess String type) {
Felipe Lemee4b60122016-02-24 10:17:41 -0800704 switch (type) {
705 case SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS:
706 case SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY:
707 case SCOPED_DIRECTORY_ACCESS_ERROR:
Ben Lin6e5a77b2016-05-06 12:30:13 -0700708 logCount(context, type);
Felipe Lemee4b60122016-02-24 10:17:41 -0800709 break;
710 default:
711 Log.wtf(TAG, "invalid InvalidScopedAccess: " + type);
712 }
713 }
714
715 // Types for logValidScopedAccessRequest
716 public static final int SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED = 0;
717 public static final int SCOPED_DIRECTORY_ACCESS_GRANTED = 1;
718 public static final int SCOPED_DIRECTORY_ACCESS_DENIED = 2;
Felipe Leme0bdb7c32016-03-09 17:40:49 -0800719 public static final int SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST = 3;
720 public static final int SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED = 4;
Felipe Lemee4b60122016-02-24 10:17:41 -0800721
722 @IntDef(flag = true, value = {
723 SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED,
724 SCOPED_DIRECTORY_ACCESS_GRANTED,
Felipe Leme0bdb7c32016-03-09 17:40:49 -0800725 SCOPED_DIRECTORY_ACCESS_DENIED,
726 SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST,
727 SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED
Felipe Lemee4b60122016-02-24 10:17:41 -0800728 })
729 @Retention(RetentionPolicy.SOURCE)
730 public @interface ScopedAccessGrant {}
731
732 public static void logValidScopedAccessRequest(Activity activity, String directory,
733 @ScopedAccessGrant int type) {
734 int index = -1;
Felipe Lemeb59a8a62016-03-17 18:56:20 -0700735 if (OpenExternalDirectoryActivity.DIRECTORY_ROOT.equals(directory)) {
736 index = -2;
737 } else {
738 for (int i = 0; i < STANDARD_DIRECTORIES.length; i++) {
739 if (STANDARD_DIRECTORIES[i].equals(directory)) {
740 index = i;
741 break;
742 }
Felipe Lemee4b60122016-02-24 10:17:41 -0800743 }
744 }
745 final String packageName = activity.getCallingPackage();
746 switch (type) {
747 case SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED:
Felipe Leme0bdb7c32016-03-09 17:40:49 -0800748 MetricsLogger.action(activity, MetricsEvent
749 .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_PACKAGE, packageName);
750 MetricsLogger.action(activity, MetricsEvent
751 .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_FOLDER, index);
Felipe Lemee4b60122016-02-24 10:17:41 -0800752 break;
753 case SCOPED_DIRECTORY_ACCESS_GRANTED:
Felipe Leme0bdb7c32016-03-09 17:40:49 -0800754 MetricsLogger.action(activity, MetricsEvent
755 .ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_PACKAGE, packageName);
756 MetricsLogger.action(activity, MetricsEvent
757 .ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_FOLDER, index);
Felipe Lemee4b60122016-02-24 10:17:41 -0800758 break;
759 case SCOPED_DIRECTORY_ACCESS_DENIED:
Felipe Leme0bdb7c32016-03-09 17:40:49 -0800760 MetricsLogger.action(activity, MetricsEvent
761 .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_PACKAGE, packageName);
762 MetricsLogger.action(activity, MetricsEvent
763 .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_FOLDER, index);
764 break;
765 case SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST:
766 MetricsLogger.action(activity, MetricsEvent
767 .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST_BY_PACKAGE, packageName);
768 MetricsLogger.action(activity, MetricsEvent
769 .ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST_BY_FOLDER, index);
770 break;
771 case SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED:
772 MetricsLogger.action(activity, MetricsEvent
773 .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED_BY_PACKAGE, packageName);
774 MetricsLogger.action(activity, MetricsEvent
775 .ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_DENIED_BY_FOLDER, index);
Felipe Lemee4b60122016-02-24 10:17:41 -0800776 break;
777 default:
778 Log.wtf(TAG, "invalid ScopedAccessGrant: " + type);
779 }
780 }
781
Ben Kwafaa27202016-01-28 16:39:57 -0800782 /**
Aga Wronska94e53e42016-04-07 13:09:58 -0700783 * Logs the action that was started by user.
Aga Wronska4972d712016-03-30 13:55:19 -0700784 * @param context
Aga Wronska94e53e42016-04-07 13:09:58 -0700785 * @param userAction
Aga Wronska4972d712016-03-30 13:55:19 -0700786 */
Aga Wronska94e53e42016-04-07 13:09:58 -0700787 public static void logUserAction(Context context, @UserAction int userAction) {
788 logHistogram(context, COUNT_USER_ACTION, userAction);
Aga Wronska4972d712016-03-30 13:55:19 -0700789 }
790
Garfield Tan5e1fd5e2017-01-13 15:28:30 -0800791 private static void logStorageFileOperationFailure(
792 Context context, @SubFileOp int subFileOp, Uri docUri) {
793 assert(Providers.AUTHORITY_STORAGE.equals(docUri.getAuthority()));
794
795 boolean isInternal;
796 try (ContentProviderClient client = acquireUnstableProviderOrThrow(
797 context.getContentResolver(), Providers.AUTHORITY_STORAGE)) {
798 final Path path = DocumentsContract.findDocumentPath(client, docUri);
799
800 final RootsAccess roots = DocumentsApplication.getRootsCache(context);
801 final RootInfo root = roots.getRootOneshot(
802 Providers.AUTHORITY_STORAGE, path.getRootId());
803 isInternal = !root.supportsEject();
804 } catch (RemoteException | RuntimeException e) {
805 Log.e(TAG, "Failed to obtain its root info. Log the metrics as internal.", e);
806 // It's not very likely to have an external storage so log it as internal.
807 isInternal = true;
808 }
809
810 final String histogram = isInternal
811 ? COUNT_INTERNAL_STORAGE_FILEOP_FAILURE
812 : COUNT_EXTERNAL_STORAGE_FILEOP_FAILURE;
813 logHistogram(context, histogram, subFileOp);
814 }
815
Aga Wronska4972d712016-03-30 13:55:19 -0700816 /**
Ben Kwa1c0a3892016-01-26 11:50:03 -0800817 * Internal method for making a MetricsLogger.count call. Increments the given counter by 1.
818 *
819 * @param context
820 * @param name The counter to increment.
821 */
822 private static void logCount(Context context, String name) {
823 if (DEBUG) Log.d(TAG, name + ": " + 1);
824 MetricsLogger.count(context, name, 1);
825 }
826
827 /**
828 * Internal method for making a MetricsLogger.histogram call.
829 *
830 * @param context
831 * @param name The name of the histogram.
832 * @param bucket The bucket to increment.
833 */
Aga Wronska6d50bcc2016-03-28 17:27:02 -0700834 private static void logHistogram(Context context, String name, @ActionType int bucket) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800835 if (DEBUG) Log.d(TAG, name + ": " + bucket);
836 MetricsLogger.histogram(context, name, bucket);
837 }
838
839 /**
840 * Generates an integer identifying the given root. For privacy, this function only recognizes a
841 * small set of hard-coded roots (ones provided by the system). Other roots are all grouped into
842 * a single ROOT_OTHER bucket.
843 */
Ben Kwafaa27202016-01-28 16:39:57 -0800844 private static @Root int sanitizeRoot(Uri uri) {
Tomasz Mikolajewski63e2aae2016-02-01 12:01:14 +0900845 if (uri == null || uri.getAuthority() == null || LauncherActivity.isLaunchUri(uri)) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800846 return ROOT_NONE;
847 }
848
849 switch (uri.getAuthority()) {
Steve McKay8659cbc2016-10-31 13:13:36 -0700850 case Providers.AUTHORITY_MEDIA:
Ben Kwa1c0a3892016-01-26 11:50:03 -0800851 switch (DocumentsContract.getRootId(uri)) {
Steve McKay8659cbc2016-10-31 13:13:36 -0700852 case Providers.ROOT_ID_AUDIO:
Ben Kwa1c0a3892016-01-26 11:50:03 -0800853 return ROOT_AUDIO;
Steve McKay8659cbc2016-10-31 13:13:36 -0700854 case Providers.ROOT_ID_IMAGES:
Ben Kwa1c0a3892016-01-26 11:50:03 -0800855 return ROOT_IMAGES;
Steve McKay8659cbc2016-10-31 13:13:36 -0700856 case Providers.ROOT_ID_VIDEOS:
Ben Kwa1c0a3892016-01-26 11:50:03 -0800857 return ROOT_VIDEOS;
858 default:
859 return ROOT_OTHER;
860 }
Steve McKay8659cbc2016-10-31 13:13:36 -0700861 case Providers.AUTHORITY_STORAGE:
862 if (Providers.ROOT_ID_HOME.equals(DocumentsContract.getRootId(uri))) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800863 return ROOT_HOME;
864 } else {
865 return ROOT_DEVICE_STORAGE;
866 }
Steve McKay8659cbc2016-10-31 13:13:36 -0700867 case Providers.AUTHORITY_DOWNLOADS:
Ben Kwa1c0a3892016-01-26 11:50:03 -0800868 return ROOT_DOWNLOADS;
Steve McKay8659cbc2016-10-31 13:13:36 -0700869 case Providers.AUTHORITY_MTP:
Ben Kwaebaaea42016-01-28 18:15:07 -0800870 return ROOT_MTP;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800871 default:
872 return ROOT_OTHER;
873 }
874 }
875
876 /** @see #sanitizeRoot(Uri) */
Ben Kwafaa27202016-01-28 16:39:57 -0800877 private static @Root int sanitizeRoot(RootInfo root) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800878 if (root.isRecents()) {
879 // Recents root is special and only identifiable via this method call. Other roots are
880 // identified by URI.
881 return ROOT_RECENTS;
882 } else {
883 return sanitizeRoot(root.getUri());
884 }
885 }
886
887 /** @see #sanitizeRoot(Uri) */
Ben Kwafaa27202016-01-28 16:39:57 -0800888 private static @Root int sanitizeRoot(ResolveInfo info) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800889 // Log all apps under a single bucket in the roots histogram.
890 return ROOT_THIRD_PARTY_APP;
891 }
892
893 /**
894 * Generates an int identifying a mime type. For privacy, this function only recognizes a small
895 * set of hard-coded types. For any other type, this function returns "other".
896 *
897 * @param mimeType
898 * @return
899 */
Ben Kwafaa27202016-01-28 16:39:57 -0800900 private static @Mime int sanitizeMime(String mimeType) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800901 if (mimeType == null) {
902 return MIME_NONE;
903 } else if ("*/*".equals(mimeType)) {
904 return MIME_ANY;
905 } else {
906 String type = mimeType.substring(0, mimeType.indexOf('/'));
907 switch (type) {
908 case "application":
909 return MIME_APPLICATION;
910 case "audio":
911 return MIME_AUDIO;
912 case "image":
913 return MIME_IMAGE;
914 case "message":
915 return MIME_MESSAGE;
916 case "multipart":
917 return MIME_MULTIPART;
918 case "text":
919 return MIME_TEXT;
920 case "video":
921 return MIME_VIDEO;
922 }
923 }
924 // Bucket all other types into one bucket.
925 return MIME_OTHER;
926 }
Ben Kwafaa27202016-01-28 16:39:57 -0800927
928 private static boolean isSystemProvider(String authority) {
929 switch (authority) {
Steve McKay8659cbc2016-10-31 13:13:36 -0700930 case Providers.AUTHORITY_MEDIA:
931 case Providers.AUTHORITY_STORAGE:
932 case Providers.AUTHORITY_DOWNLOADS:
Ben Kwafaa27202016-01-28 16:39:57 -0800933 return true;
934 default:
935 return false;
936 }
937 }
938
939 /**
940 * @param operation
941 * @param providerType
942 * @return An opcode, suitable for use as histogram bucket, for the given operation/provider
943 * combination.
944 */
945 private static @FileOp int getOpCode(@OpType int operation, @Provider int providerType) {
946 switch (operation) {
947 case FileOperationService.OPERATION_COPY:
948 switch (providerType) {
949 case PROVIDER_INTRA:
950 return FILEOP_COPY_INTRA_PROVIDER;
951 case PROVIDER_SYSTEM:
952 return FILEOP_COPY_SYSTEM_PROVIDER;
953 case PROVIDER_EXTERNAL:
954 return FILEOP_COPY_EXTERNAL_PROVIDER;
955 }
Tomasz Mikolajewskibc7eb5f2017-01-27 16:29:30 +0900956 case FileOperationService.OPERATION_COMPRESS:
957 switch (providerType) {
958 case PROVIDER_INTRA:
959 return FILEOP_COMPRESS_INTRA_PROVIDER;
960 case PROVIDER_SYSTEM:
961 return FILEOP_COMPRESS_SYSTEM_PROVIDER;
962 case PROVIDER_EXTERNAL:
963 return FILEOP_COMPRESS_EXTERNAL_PROVIDER;
964 }
965 case FileOperationService.OPERATION_EXTRACT:
966 switch (providerType) {
967 case PROVIDER_INTRA:
968 return FILEOP_EXTRACT_INTRA_PROVIDER;
969 case PROVIDER_SYSTEM:
970 return FILEOP_EXTRACT_SYSTEM_PROVIDER;
971 case PROVIDER_EXTERNAL:
972 return FILEOP_EXTRACT_EXTERNAL_PROVIDER;
973 }
Ben Kwafaa27202016-01-28 16:39:57 -0800974 case FileOperationService.OPERATION_MOVE:
975 switch (providerType) {
976 case PROVIDER_INTRA:
977 return FILEOP_MOVE_INTRA_PROVIDER;
978 case PROVIDER_SYSTEM:
979 return FILEOP_MOVE_SYSTEM_PROVIDER;
980 case PROVIDER_EXTERNAL:
981 return FILEOP_MOVE_EXTERNAL_PROVIDER;
982 }
983 case FileOperationService.OPERATION_DELETE:
984 return FILEOP_DELETE;
985 default:
986 Log.w(TAG, "Unrecognized operation type when logging a file operation");
987 return FILEOP_OTHER;
988 }
989 }
990
991 /**
Ben Kwab41a5ed2016-02-17 16:06:22 -0800992 * Maps FileOperationService OpType values, to MetricsOpType values.
993 */
994 private static @MetricsOpType int toMetricsOpType(@OpType int operation) {
995 switch (operation) {
996 case FileOperationService.OPERATION_COPY:
997 return OPERATION_COPY;
998 case FileOperationService.OPERATION_MOVE:
999 return OPERATION_MOVE;
1000 case FileOperationService.OPERATION_DELETE:
1001 return OPERATION_DELETE;
1002 case FileOperationService.OPERATION_UNKNOWN:
1003 default:
1004 return OPERATION_UNKNOWN;
1005 }
1006 }
1007
1008 private static @MetricsAction int toMetricsAction(int action) {
1009 switch(action) {
1010 case State.ACTION_OPEN:
1011 return ACTION_OPEN;
1012 case State.ACTION_CREATE:
1013 return ACTION_CREATE;
1014 case State.ACTION_GET_CONTENT:
1015 return ACTION_GET_CONTENT;
1016 case State.ACTION_OPEN_TREE:
1017 return ACTION_OPEN_TREE;
Ben Kwab41a5ed2016-02-17 16:06:22 -08001018 case State.ACTION_BROWSE:
1019 return ACTION_BROWSE;
1020 case State.ACTION_PICK_COPY_DESTINATION:
1021 return ACTION_PICK_COPY_DESTINATION;
1022 default:
1023 return ACTION_OTHER;
1024 }
1025 }
1026
1027 /**
Ben Kwafaa27202016-01-28 16:39:57 -08001028 * Count the given src documents and provide a tally of how many come from the same provider as
1029 * the dst document (if a dst is provided), how many come from system providers, and how many
1030 * come from external 3rd-party providers.
1031 */
Steve McKay99f1dc32016-12-29 16:02:01 -08001032 private static void countProviders(
1033 ProviderCounts counts, List<DocumentInfo> srcs, @Nullable DocumentInfo dst) {
Ben Kwafaa27202016-01-28 16:39:57 -08001034 for (DocumentInfo doc: srcs) {
Steve McKay99f1dc32016-12-29 16:02:01 -08001035 countForAuthority(counts, doc.authority, dst);
Ben Kwafaa27202016-01-28 16:39:57 -08001036 }
Steve McKay99f1dc32016-12-29 16:02:01 -08001037 }
1038
1039 /**
1040 * Count the given uris and provide a tally of how many come from the same provider as
1041 * the dst document (if a dst is provided), how many come from system providers, and how many
1042 * come from external 3rd-party providers.
1043 */
1044 private static void countProviders(ProviderCounts counts, List<Uri> uris) {
1045 for (Uri uri: uris) {
1046 countForAuthority(counts, uri.getAuthority(), null);
1047 }
1048 }
1049
1050 private static void countForAuthority(
1051 ProviderCounts counts, String authority, @Nullable DocumentInfo dst) {
1052 if (dst != null && authority.equals(dst.authority)) {
1053 counts.intraProvider++;
1054 } else if (isSystemProvider(authority)){
1055 counts.systemProvider++;
1056 } else {
1057 counts.externalProvider++;
1058 }
Ben Kwafaa27202016-01-28 16:39:57 -08001059 }
1060
1061 private static class ProviderCounts {
1062 int intraProvider;
1063 int systemProvider;
1064 int externalProvider;
1065 }
Ben Kwa1c0a3892016-01-26 11:50:03 -08001066}