blob: 125a9fa918a7ada76391b8006fccd9b9577e807b [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
Jeff Sharkey2c0b4852019-02-15 15:53:47 -070019import static android.content.ContentResolver.wrap;
20
Garfield Tan02fd92f2017-01-13 15:28:30 -080021import static com.android.documentsui.DocumentsApplication.acquireUnstableProviderOrThrow;
Steve McKay15b92782016-03-09 15:20:00 -080022
Garfield Tan02fd92f2017-01-13 15:28:30 -080023import android.content.ContentProviderClient;
Ben Kwa1c0a3892016-01-26 11:50:03 -080024import android.content.Context;
25import android.content.Intent;
26import android.content.pm.ResolveInfo;
27import android.net.Uri;
Garfield Tan02fd92f2017-01-13 15:28:30 -080028import android.os.RemoteException;
Ben Kwa1c0a3892016-01-26 11:50:03 -080029import android.provider.DocumentsContract;
Garfield Tan02fd92f2017-01-13 15:28:30 -080030import android.provider.DocumentsContract.Path;
Garfield Tan935c5132017-01-12 14:16:08 -080031import android.provider.DocumentsProvider;
Ben Kwa1c0a3892016-01-26 11:50:03 -080032import android.util.Log;
33
shawnlina06573a2019-04-09 15:48:43 +080034import androidx.annotation.Nullable;
35
Steve McKayd0805062016-09-15 14:30:38 -070036import com.android.documentsui.base.DocumentInfo;
Steve McKay8659cbc2016-10-31 13:13:36 -070037import com.android.documentsui.base.Providers;
Steve McKayd0805062016-09-15 14:30:38 -070038import com.android.documentsui.base.RootInfo;
Steve McKayd9caa6a2016-09-15 16:36:45 -070039import com.android.documentsui.base.State;
Steve McKayb6006b22016-09-29 09:23:45 -070040import com.android.documentsui.files.LauncherActivity;
shawnline0ba46d2019-01-25 15:21:26 +080041import com.android.documentsui.picker.PickResult;
Jon Mann9bd40992017-03-24 12:34:34 -070042import com.android.documentsui.roots.ProvidersAccess;
Ben Kwafaa27202016-01-28 16:39:57 -080043import com.android.documentsui.services.FileOperationService;
44import com.android.documentsui.services.FileOperationService.OpType;
Ben Kwa1c0a3892016-01-26 11:50:03 -080045
Jeff Sharkey844989e2018-12-07 15:16:08 -070046import java.io.FileNotFoundException;
Ben Kwafaa27202016-01-28 16:39:57 -080047import java.util.List;
48
Leif Hendrik Wilden42053492018-01-03 11:16:09 -080049/**
50 * Methods for logging metrics.
51 */
Ben Kwa1c0a3892016-01-26 11:50:03 -080052public final class Metrics {
53 private static final String TAG = "Metrics";
54
Ben Kwa1c0a3892016-01-26 11:50:03 -080055 /**
56 * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up.
57 *
Ben Kwa1c0a3892016-01-26 11:50:03 -080058 * @param state
59 * @param intent
60 */
shawnlin9cee68f2019-01-25 11:20:18 +080061 public static void logActivityLaunch(State state, Intent intent) {
Ben Kwa1c0a3892016-01-26 11:50:03 -080062 Uri uri = intent.getData();
shawnlina06573a2019-04-09 15:48:43 +080063 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_LAUNCH_REPORTED,
64 toMetricsAction(state.action), false,
shawnlin9cee68f2019-01-25 11:20:18 +080065 sanitizeMime(intent.getType()), sanitizeRoot(uri));
Ben Kwa1c0a3892016-01-26 11:50:03 -080066 }
67
68 /**
Garfield Tan935c5132017-01-12 14:16:08 -080069 * Logs when DocumentsUI are launched with {@link DocumentsContract#EXTRA_INITIAL_URI}.
70 *
Garfield Tan935c5132017-01-12 14:16:08 -080071 * @param state used to resolve action
72 * @param rootUri the resolved rootUri, or {@code null} if the provider doesn't
73 * support {@link DocumentsProvider#findDocumentPath(String, String)}
74 */
shawnlin9cee68f2019-01-25 11:20:18 +080075 public static void logLaunchAtLocation(State state, @Nullable Uri rootUri) {
shawnlina06573a2019-04-09 15:48:43 +080076 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_LAUNCH_REPORTED,
77 toMetricsAction(state.action), true,
shawnlin9cee68f2019-01-25 11:20:18 +080078 MetricConsts.MIME_UNKNOWN, sanitizeRoot(rootUri));
Garfield Tan935c5132017-01-12 14:16:08 -080079 }
80
81 /**
Garfield Tan045ce6b2017-01-19 16:17:04 -080082 * Logs a root visited event in file managers. Call this when the user
83 * taps on a root in {@link com.android.documentsui.sidebar.RootsFragment}.
Garfield Tan045ce6b2017-01-19 16:17:04 -080084 * @param scope
Ben Kwa1c0a3892016-01-26 11:50:03 -080085 * @param info
86 */
shawnlin9cee68f2019-01-25 11:20:18 +080087 public static void logRootVisited(@MetricConsts.ContextScope int scope, RootInfo info) {
shawnlina06573a2019-04-09 15:48:43 +080088 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_ROOT_VISITED, scope, sanitizeRoot(info));
Ben Kwa1c0a3892016-01-26 11:50:03 -080089 }
90
91 /**
Garfield Tan045ce6b2017-01-19 16:17:04 -080092 * Logs an app visited event in file pickers. Call this when the user visits
93 * on an app in the RootsFragment.
Ben Kwa1c0a3892016-01-26 11:50:03 -080094 *
Ben Kwa1c0a3892016-01-26 11:50:03 -080095 * @param info
96 */
shawnlin9cee68f2019-01-25 11:20:18 +080097 public static void logAppVisited(ResolveInfo info) {
shawnlina06573a2019-04-09 15:48:43 +080098 DocumentsStatsLog.write(
99 DocumentsStatsLog.DOCS_UI_ROOT_VISITED,
100 MetricConsts.PICKER_SCOPE, sanitizeRoot(info));
Ben Kwa1c0a3892016-01-26 11:50:03 -0800101 }
102
103 /**
Ben Kwafaa27202016-01-28 16:39:57 -0800104 * Logs file operation stats. Call this when a file operation has completed. The given
105 * DocumentInfo is only used to distinguish broad categories of actions (e.g. copying from one
106 * provider to another vs copying within a given provider). No PII is logged.
107 *
Ben Kwafaa27202016-01-28 16:39:57 -0800108 * @param operationType
109 * @param srcs
110 * @param dst
111 */
112 public static void logFileOperation(
Ben Kwafaa27202016-01-28 16:39:57 -0800113 @OpType int operationType,
114 List<DocumentInfo> srcs,
115 @Nullable DocumentInfo dst) {
Steve McKay99f1dc32016-12-29 16:02:01 -0800116 ProviderCounts counts = new ProviderCounts();
117 countProviders(counts, srcs, dst);
Ben Kwafaa27202016-01-28 16:39:57 -0800118 if (counts.intraProvider > 0) {
shawnlin9cee68f2019-01-25 11:20:18 +0800119 logIntraProviderFileOps(dst.authority, operationType);
Ben Kwafaa27202016-01-28 16:39:57 -0800120 }
121 if (counts.systemProvider > 0) {
122 // Log file operations on system providers.
shawnlin9cee68f2019-01-25 11:20:18 +0800123 logInterProviderFileOps(MetricConsts.PROVIDER_SYSTEM, dst, operationType);
Ben Kwafaa27202016-01-28 16:39:57 -0800124 }
125 if (counts.externalProvider > 0) {
126 // Log file operations on external providers.
shawnlin9cee68f2019-01-25 11:20:18 +0800127 logInterProviderFileOps(MetricConsts.PROVIDER_EXTERNAL, dst, operationType);
Ben Kwafaa27202016-01-28 16:39:57 -0800128 }
129 }
130
Garfield Tan331de982017-04-19 16:22:00 -0700131 public static void logFileOperated(
shawnlin9cee68f2019-01-25 11:20:18 +0800132 @OpType int operationType, @MetricConsts.FileOpMode int approach) {
Garfield Tan331de982017-04-19 16:22:00 -0700133 switch (operationType) {
134 case FileOperationService.OPERATION_COPY:
shawnlina06573a2019-04-09 15:48:43 +0800135 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_FILE_OP_COPY_MOVE_MODE_REPORTED,
shawnlin9cee68f2019-01-25 11:20:18 +0800136 MetricConsts.FILEOP_COPY, approach);
Garfield Tan331de982017-04-19 16:22:00 -0700137 break;
138 case FileOperationService.OPERATION_MOVE:
shawnlina06573a2019-04-09 15:48:43 +0800139 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_FILE_OP_COPY_MOVE_MODE_REPORTED,
shawnlin9cee68f2019-01-25 11:20:18 +0800140 MetricConsts.FILEOP_MOVE, approach);
Garfield Tan331de982017-04-19 16:22:00 -0700141 break;
142 }
143 }
144
Ben Kwafaa27202016-01-28 16:39:57 -0800145 /**
Aga Wronska46a868a2016-03-30 10:57:04 -0700146 * Logs create directory operation. It is a part of file operation stats. We do not
147 * differentiate between internal and external locations, all create directory operations are
148 * logged under COUNT_FILEOP_SYSTEM. Call this when a create directory operation has completed.
Aga Wronska46a868a2016-03-30 10:57:04 -0700149 */
shawnlin9cee68f2019-01-25 11:20:18 +0800150 public static void logCreateDirOperation() {
shawnlina06573a2019-04-09 15:48:43 +0800151 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP,
shawnlin9cee68f2019-01-25 11:20:18 +0800152 MetricConsts.PROVIDER_SYSTEM, MetricConsts.FILEOP_CREATE_DIR);
Aga Wronska46a868a2016-03-30 10:57:04 -0700153 }
154
155 /**
156 * Logs rename file operation. It is a part of file operation stats. We do not differentiate
157 * between internal and external locations, all rename operations are logged under
158 * COUNT_FILEOP_SYSTEM. Call this when a rename file operation has completed.
Aga Wronska46a868a2016-03-30 10:57:04 -0700159 */
shawnlin9cee68f2019-01-25 11:20:18 +0800160 public static void logRenameFileOperation() {
shawnlina06573a2019-04-09 15:48:43 +0800161 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP,
shawnlin9cee68f2019-01-25 11:20:18 +0800162 MetricConsts.PROVIDER_SYSTEM, MetricConsts.FILEOP_RENAME);
Aga Wronska46a868a2016-03-30 10:57:04 -0700163 }
164
165 /**
Ben Kwafaa27202016-01-28 16:39:57 -0800166 * Logs some kind of file operation error. Call this when a file operation (e.g. copy, delete)
167 * fails.
168 *
Ben Kwafaa27202016-01-28 16:39:57 -0800169 * @param operationType
170 * @param failedFiles
171 */
shawnlin9cee68f2019-01-25 11:20:18 +0800172 public static void logFileOperationErrors(@OpType int operationType,
Steve McKay99f1dc32016-12-29 16:02:01 -0800173 List<DocumentInfo> failedFiles, List<Uri> failedUris) {
Steve McKay99f1dc32016-12-29 16:02:01 -0800174 ProviderCounts counts = new ProviderCounts();
175 countProviders(counts, failedFiles, null);
Steve McKay99f1dc32016-12-29 16:02:01 -0800176 // TODO: Report URI errors separate from file operation errors.
177 countProviders(counts, failedUris);
shawnlin9cee68f2019-01-25 11:20:18 +0800178 @MetricConsts.FileOp int opCode = MetricConsts.FILEOP_OTHER_ERROR;
Ben Kwafaa27202016-01-28 16:39:57 -0800179 switch (operationType) {
180 case FileOperationService.OPERATION_COPY:
shawnlin9cee68f2019-01-25 11:20:18 +0800181 opCode = MetricConsts.FILEOP_COPY_ERROR;
Ben Kwafaa27202016-01-28 16:39:57 -0800182 break;
Tomasz Mikolajewski4f225492017-01-27 16:29:30 +0900183 case FileOperationService.OPERATION_COMPRESS:
shawnlin9cee68f2019-01-25 11:20:18 +0800184 opCode = MetricConsts.FILEOP_COMPRESS_ERROR;
Tomasz Mikolajewski4f225492017-01-27 16:29:30 +0900185 break;
186 case FileOperationService.OPERATION_EXTRACT:
shawnlin9cee68f2019-01-25 11:20:18 +0800187 opCode = MetricConsts.FILEOP_EXTRACT_ERROR;
Tomasz Mikolajewski4f225492017-01-27 16:29:30 +0900188 break;
Ben Kwafaa27202016-01-28 16:39:57 -0800189 case FileOperationService.OPERATION_DELETE:
shawnlin9cee68f2019-01-25 11:20:18 +0800190 opCode = MetricConsts.FILEOP_DELETE_ERROR;
Ben Kwafaa27202016-01-28 16:39:57 -0800191 break;
192 case FileOperationService.OPERATION_MOVE:
shawnlin9cee68f2019-01-25 11:20:18 +0800193 opCode = MetricConsts.FILEOP_MOVE_ERROR;
Ben Kwafaa27202016-01-28 16:39:57 -0800194 break;
195 }
196 if (counts.systemProvider > 0) {
shawnlina06573a2019-04-09 15:48:43 +0800197 DocumentsStatsLog.write(
198 DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP,
199 MetricConsts.PROVIDER_SYSTEM, opCode);
Ben Kwafaa27202016-01-28 16:39:57 -0800200 }
201 if (counts.externalProvider > 0) {
shawnlina06573a2019-04-09 15:48:43 +0800202 DocumentsStatsLog.write(
203 DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP,
204 MetricConsts.PROVIDER_EXTERNAL, opCode);
Ben Kwafaa27202016-01-28 16:39:57 -0800205 }
206 }
207
Garfield Tan02fd92f2017-01-13 15:28:30 -0800208 public static void logFileOperationFailure(
shawnlin9cee68f2019-01-25 11:20:18 +0800209 Context context, @MetricConsts.SubFileOp int subFileOp, Uri docUri) {
Garfield Tan02fd92f2017-01-13 15:28:30 -0800210 final String authority = docUri.getAuthority();
211 switch (authority) {
212 case Providers.AUTHORITY_MEDIA:
shawnlina06573a2019-04-09 15:48:43 +0800213 DocumentsStatsLog.write(
214 DocumentsStatsLog.DOCS_UI_FILE_OP_FAILURE,
215 MetricConsts.AUTH_MEDIA, subFileOp);
Garfield Tan02fd92f2017-01-13 15:28:30 -0800216 break;
217 case Providers.AUTHORITY_STORAGE:
218 logStorageFileOperationFailure(context, subFileOp, docUri);
219 break;
220 case Providers.AUTHORITY_DOWNLOADS:
shawnlina06573a2019-04-09 15:48:43 +0800221 DocumentsStatsLog.write(
222 DocumentsStatsLog.DOCS_UI_FILE_OP_FAILURE,
223 MetricConsts.AUTH_DOWNLOADS, subFileOp);
Garfield Tan02fd92f2017-01-13 15:28:30 -0800224 break;
225 case Providers.AUTHORITY_MTP:
shawnlina06573a2019-04-09 15:48:43 +0800226 DocumentsStatsLog.write(
227 DocumentsStatsLog.DOCS_UI_FILE_OP_FAILURE,
228 MetricConsts.AUTH_MTP, subFileOp);
Garfield Tan02fd92f2017-01-13 15:28:30 -0800229 break;
230 default:
shawnlina06573a2019-04-09 15:48:43 +0800231 DocumentsStatsLog.write(
232 DocumentsStatsLog.DOCS_UI_FILE_OP_FAILURE,
233 MetricConsts.AUTH_OTHER, subFileOp);
Garfield Tan02fd92f2017-01-13 15:28:30 -0800234 break;
235 }
236 }
237
Ben Kwafaa27202016-01-28 16:39:57 -0800238 /**
Aga Wronska46a868a2016-03-30 10:57:04 -0700239 * Logs create directory operation error. We do not differentiate between internal and external
240 * locations, all create directory errors are logged under COUNT_FILEOP_SYSTEM. Call this when a
241 * create directory operation fails.
Aga Wronska46a868a2016-03-30 10:57:04 -0700242 */
shawnlin9cee68f2019-01-25 11:20:18 +0800243 public static void logCreateDirError() {
shawnlina06573a2019-04-09 15:48:43 +0800244 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP,
shawnlin9cee68f2019-01-25 11:20:18 +0800245 MetricConsts.PROVIDER_SYSTEM, MetricConsts.FILEOP_CREATE_DIR_ERROR);
Aga Wronska46a868a2016-03-30 10:57:04 -0700246 }
247
248 /**
249 * Logs rename file operation error. We do not differentiate between internal and external
250 * locations, all rename errors are logged under COUNT_FILEOP_SYSTEM. Call this
251 * when a rename file operation fails.
Aga Wronska46a868a2016-03-30 10:57:04 -0700252 */
shawnlin9cee68f2019-01-25 11:20:18 +0800253 public static void logRenameFileError() {
shawnlina06573a2019-04-09 15:48:43 +0800254 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP,
shawnlin9cee68f2019-01-25 11:20:18 +0800255 MetricConsts.PROVIDER_SYSTEM, MetricConsts.FILEOP_RENAME_ERROR);
Aga Wronska46a868a2016-03-30 10:57:04 -0700256 }
257
258 /**
Daichi Hirono320a08f2016-03-25 19:04:39 +0900259 * Logs the cancellation of a file operation. Call this when a Job is canceled.
shawnlin9cee68f2019-01-25 11:20:18 +0800260 *
Ben Kwafaa27202016-01-28 16:39:57 -0800261 * @param operationType
262 */
shawnlin9cee68f2019-01-25 11:20:18 +0800263 public static void logFileOperationCancelled(@OpType int operationType) {
shawnlina06573a2019-04-09 15:48:43 +0800264 DocumentsStatsLog.write(
265 DocumentsStatsLog.DOCS_UI_FILE_OP_CANCELED, toMetricsOpType(operationType));
Ben Kwafaa27202016-01-28 16:39:57 -0800266 }
267
Daichi Hirono320a08f2016-03-25 19:04:39 +0900268 /**
269 * Logs startup time in milliseconds.
shawnlin9cee68f2019-01-25 11:20:18 +0800270 *
Daichi Hirono320a08f2016-03-25 19:04:39 +0900271 * @param startupMs Startup time in milliseconds.
272 */
shawnlin9cee68f2019-01-25 11:20:18 +0800273 public static void logStartupMs(int startupMs) {
shawnlina06573a2019-04-09 15:48:43 +0800274 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_STARTUP_MS, startupMs);
Daichi Hirono320a08f2016-03-25 19:04:39 +0900275 }
276
Ben Kwafaa27202016-01-28 16:39:57 -0800277 private static void logInterProviderFileOps(
shawnlin9cee68f2019-01-25 11:20:18 +0800278 @MetricConsts.Provider int providerType,
Ben Kwafaa27202016-01-28 16:39:57 -0800279 DocumentInfo dst,
280 @OpType int operationType) {
281 if (operationType == FileOperationService.OPERATION_DELETE) {
shawnlina06573a2019-04-09 15:48:43 +0800282 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP,
283 providerType, MetricConsts.FILEOP_DELETE);
Ben Kwafaa27202016-01-28 16:39:57 -0800284 } else {
Steve McKay0af8afd2016-02-25 13:34:03 -0800285 assert(dst != null);
shawnlin9cee68f2019-01-25 11:20:18 +0800286 @MetricConsts.Provider int opProviderType = isSystemProvider(dst.authority)
287 ? MetricConsts.PROVIDER_SYSTEM : MetricConsts.PROVIDER_EXTERNAL;
shawnlina06573a2019-04-09 15:48:43 +0800288 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP,
shawnlin9cee68f2019-01-25 11:20:18 +0800289 providerType, getOpCode(operationType, opProviderType));
Ben Kwafaa27202016-01-28 16:39:57 -0800290 }
291 }
292
shawnlin9cee68f2019-01-25 11:20:18 +0800293 private static void logIntraProviderFileOps(String authority, @OpType int operationType) {
294 @MetricConsts.Provider int providerType = isSystemProvider(authority)
295 ? MetricConsts.PROVIDER_SYSTEM : MetricConsts.PROVIDER_EXTERNAL;
shawnlina06573a2019-04-09 15:48:43 +0800296 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP,
shawnlin9cee68f2019-01-25 11:20:18 +0800297 providerType, getOpCode(operationType, MetricConsts.PROVIDER_INTRA));
Ben Kwafaa27202016-01-28 16:39:57 -0800298 }
299
300 /**
Aga Wronska94e53e42016-04-07 13:09:58 -0700301 * Logs the action that was started by user.
shawnlin9cee68f2019-01-25 11:20:18 +0800302 *
Aga Wronska94e53e42016-04-07 13:09:58 -0700303 * @param userAction
Aga Wronska4972d712016-03-30 13:55:19 -0700304 */
shawnlin9cee68f2019-01-25 11:20:18 +0800305 public static void logUserAction(@MetricConsts.UserAction int userAction) {
shawnlina06573a2019-04-09 15:48:43 +0800306 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_USER_ACTION_REPORTED, userAction);
Aga Wronska4972d712016-03-30 13:55:19 -0700307 }
308
shawnline0ba46d2019-01-25 15:21:26 +0800309 public static void logPickerLaunchedFrom(String packgeName) {
shawnlina06573a2019-04-09 15:48:43 +0800310 DocumentsStatsLog.write(
311 DocumentsStatsLog.DOCS_UI_PICKER_LAUNCHED_FROM_REPORTED, packgeName);
shawnline0ba46d2019-01-25 15:21:26 +0800312 }
313
314 public static void logSearchType(int searchType) {
shawnlina06573a2019-04-09 15:48:43 +0800315 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_SEARCH_TYPE_REPORTED, searchType);
shawnline0ba46d2019-01-25 15:21:26 +0800316 }
317
318 public static void logSearchMode(boolean isKeywordSearch, boolean isChipsSearch) {
shawnlina06573a2019-04-09 15:48:43 +0800319 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_SEARCH_MODE_REPORTED,
320 getSearchMode(isKeywordSearch, isChipsSearch));
shawnline0ba46d2019-01-25 15:21:26 +0800321 }
322
323 public static void logPickResult(PickResult result) {
shawnlina06573a2019-04-09 15:48:43 +0800324 DocumentsStatsLog.write(
325 DocumentsStatsLog.DOCS_UI_PICK_RESULT_REPORTED,
shawnline0ba46d2019-01-25 15:21:26 +0800326 result.getActionCount(),
327 result.getDuration(),
328 result.getFileCount(),
329 result.isSearching(),
330 result.getRoot(),
331 result.getMimeType(),
332 result.getRepeatedPickTimes());
333 }
334
Garfield Tan02fd92f2017-01-13 15:28:30 -0800335 private static void logStorageFileOperationFailure(
shawnlin9cee68f2019-01-25 11:20:18 +0800336 Context context, @MetricConsts.SubFileOp int subFileOp, Uri docUri) {
Garfield Tan02fd92f2017-01-13 15:28:30 -0800337 assert(Providers.AUTHORITY_STORAGE.equals(docUri.getAuthority()));
Garfield Tan02fd92f2017-01-13 15:28:30 -0800338 boolean isInternal;
339 try (ContentProviderClient client = acquireUnstableProviderOrThrow(
340 context.getContentResolver(), Providers.AUTHORITY_STORAGE)) {
Jeff Sharkey2c0b4852019-02-15 15:53:47 -0700341 final Path path = DocumentsContract.findDocumentPath(wrap(client), docUri);
Jon Mann9bd40992017-03-24 12:34:34 -0700342 final ProvidersAccess providers = DocumentsApplication.getProvidersCache(context);
343 final RootInfo root = providers.getRootOneshot(
Garfield Tan02fd92f2017-01-13 15:28:30 -0800344 Providers.AUTHORITY_STORAGE, path.getRootId());
345 isInternal = !root.supportsEject();
Jeff Sharkey844989e2018-12-07 15:16:08 -0700346 } catch (FileNotFoundException | RemoteException | RuntimeException e) {
Garfield Tan02fd92f2017-01-13 15:28:30 -0800347 Log.e(TAG, "Failed to obtain its root info. Log the metrics as internal.", e);
348 // It's not very likely to have an external storage so log it as internal.
349 isInternal = true;
350 }
shawnlin9cee68f2019-01-25 11:20:18 +0800351 @MetricConsts.MetricsAuth final int authority = isInternal
352 ? MetricConsts.AUTH_STORAGE_INTERNAL : MetricConsts.AUTH_STORAGE_EXTERNAL;
shawnlina06573a2019-04-09 15:48:43 +0800353 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_FILE_OP_FAILURE, authority, subFileOp);
Ben Kwa1c0a3892016-01-26 11:50:03 -0800354 }
355
356 /**
357 * Generates an integer identifying the given root. For privacy, this function only recognizes a
358 * small set of hard-coded roots (ones provided by the system). Other roots are all grouped into
359 * a single ROOT_OTHER bucket.
360 */
shawnlin9cee68f2019-01-25 11:20:18 +0800361 private static @MetricConsts.Root int sanitizeRoot(Uri uri) {
Tomasz Mikolajewski63e2aae2016-02-01 12:01:14 +0900362 if (uri == null || uri.getAuthority() == null || LauncherActivity.isLaunchUri(uri)) {
shawnlin9cee68f2019-01-25 11:20:18 +0800363 return MetricConsts.ROOT_NONE;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800364 }
Ben Kwa1c0a3892016-01-26 11:50:03 -0800365 switch (uri.getAuthority()) {
Steve McKay8659cbc2016-10-31 13:13:36 -0700366 case Providers.AUTHORITY_MEDIA:
felkachangec40c5c2018-05-14 18:01:11 +0800367 String rootId = getRootIdSafely(uri);
368 if (rootId == null) {
shawnlin9cee68f2019-01-25 11:20:18 +0800369 return MetricConsts.ROOT_NONE;
felkachangec40c5c2018-05-14 18:01:11 +0800370 }
371 switch (rootId) {
Steve McKay8659cbc2016-10-31 13:13:36 -0700372 case Providers.ROOT_ID_AUDIO:
shawnlin9cee68f2019-01-25 11:20:18 +0800373 return MetricConsts.ROOT_AUDIO;
Steve McKay8659cbc2016-10-31 13:13:36 -0700374 case Providers.ROOT_ID_IMAGES:
shawnlin9cee68f2019-01-25 11:20:18 +0800375 return MetricConsts.ROOT_IMAGES;
Steve McKay8659cbc2016-10-31 13:13:36 -0700376 case Providers.ROOT_ID_VIDEOS:
shawnlin9cee68f2019-01-25 11:20:18 +0800377 return MetricConsts.ROOT_VIDEOS;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800378 default:
shawnlin9cee68f2019-01-25 11:20:18 +0800379 return MetricConsts.ROOT_OTHER_DOCS_PROVIDER;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800380 }
Steve McKay8659cbc2016-10-31 13:13:36 -0700381 case Providers.AUTHORITY_STORAGE:
felkachangec40c5c2018-05-14 18:01:11 +0800382 rootId = getRootIdSafely(uri);
383 if (rootId == null) {
shawnlin9cee68f2019-01-25 11:20:18 +0800384 return MetricConsts.ROOT_NONE;
felkachangec40c5c2018-05-14 18:01:11 +0800385 }
386 if (Providers.ROOT_ID_HOME.equals(rootId)) {
shawnlin9cee68f2019-01-25 11:20:18 +0800387 return MetricConsts.ROOT_HOME;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800388 } else {
shawnlin9cee68f2019-01-25 11:20:18 +0800389 return MetricConsts.ROOT_DEVICE_STORAGE;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800390 }
Steve McKay8659cbc2016-10-31 13:13:36 -0700391 case Providers.AUTHORITY_DOWNLOADS:
shawnlin9cee68f2019-01-25 11:20:18 +0800392 return MetricConsts.ROOT_DOWNLOADS;
Steve McKay8659cbc2016-10-31 13:13:36 -0700393 case Providers.AUTHORITY_MTP:
shawnlin9cee68f2019-01-25 11:20:18 +0800394 return MetricConsts.ROOT_MTP;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800395 default:
shawnlin9cee68f2019-01-25 11:20:18 +0800396 return MetricConsts.ROOT_OTHER_DOCS_PROVIDER;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800397 }
398 }
399
400 /** @see #sanitizeRoot(Uri) */
shawnlin9cee68f2019-01-25 11:20:18 +0800401 public static @MetricConsts.Root int sanitizeRoot(RootInfo root) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800402 if (root.isRecents()) {
403 // Recents root is special and only identifiable via this method call. Other roots are
404 // identified by URI.
shawnlin9cee68f2019-01-25 11:20:18 +0800405 return MetricConsts.ROOT_RECENTS;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800406 } else {
407 return sanitizeRoot(root.getUri());
408 }
409 }
410
411 /** @see #sanitizeRoot(Uri) */
shawnlin9cee68f2019-01-25 11:20:18 +0800412 public static @MetricConsts.Root int sanitizeRoot(ResolveInfo info) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800413 // Log all apps under a single bucket in the roots histogram.
shawnlin9cee68f2019-01-25 11:20:18 +0800414 return MetricConsts.ROOT_THIRD_PARTY_APP;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800415 }
416
417 /**
418 * Generates an int identifying a mime type. For privacy, this function only recognizes a small
419 * set of hard-coded types. For any other type, this function returns "other".
420 *
421 * @param mimeType
422 * @return
423 */
shawnlin9cee68f2019-01-25 11:20:18 +0800424 public static @MetricConsts.Mime int sanitizeMime(String mimeType) {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800425 if (mimeType == null) {
shawnlin9cee68f2019-01-25 11:20:18 +0800426 return MetricConsts.MIME_NONE;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800427 } else if ("*/*".equals(mimeType)) {
shawnlin9cee68f2019-01-25 11:20:18 +0800428 return MetricConsts.MIME_ANY;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800429 } else {
430 String type = mimeType.substring(0, mimeType.indexOf('/'));
431 switch (type) {
432 case "application":
shawnlin9cee68f2019-01-25 11:20:18 +0800433 return MetricConsts.MIME_APPLICATION;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800434 case "audio":
shawnlin9cee68f2019-01-25 11:20:18 +0800435 return MetricConsts.MIME_AUDIO;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800436 case "image":
shawnlin9cee68f2019-01-25 11:20:18 +0800437 return MetricConsts.MIME_IMAGE;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800438 case "message":
shawnlin9cee68f2019-01-25 11:20:18 +0800439 return MetricConsts.MIME_MESSAGE;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800440 case "multipart":
shawnlin9cee68f2019-01-25 11:20:18 +0800441 return MetricConsts.MIME_MULTIPART;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800442 case "text":
shawnlin9cee68f2019-01-25 11:20:18 +0800443 return MetricConsts.MIME_TEXT;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800444 case "video":
shawnlin9cee68f2019-01-25 11:20:18 +0800445 return MetricConsts.MIME_VIDEO;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800446 }
447 }
448 // Bucket all other types into one bucket.
shawnlin9cee68f2019-01-25 11:20:18 +0800449 return MetricConsts.MIME_OTHER;
Ben Kwa1c0a3892016-01-26 11:50:03 -0800450 }
Ben Kwafaa27202016-01-28 16:39:57 -0800451
452 private static boolean isSystemProvider(String authority) {
453 switch (authority) {
Steve McKay8659cbc2016-10-31 13:13:36 -0700454 case Providers.AUTHORITY_MEDIA:
455 case Providers.AUTHORITY_STORAGE:
456 case Providers.AUTHORITY_DOWNLOADS:
Ben Kwafaa27202016-01-28 16:39:57 -0800457 return true;
458 default:
459 return false;
460 }
461 }
462
463 /**
464 * @param operation
465 * @param providerType
466 * @return An opcode, suitable for use as histogram bucket, for the given operation/provider
467 * combination.
468 */
shawnlin9cee68f2019-01-25 11:20:18 +0800469 private static @MetricConsts.FileOp int getOpCode(
470 @OpType int operation, @MetricConsts.Provider int providerType) {
Ben Kwafaa27202016-01-28 16:39:57 -0800471 switch (operation) {
472 case FileOperationService.OPERATION_COPY:
473 switch (providerType) {
shawnlin9cee68f2019-01-25 11:20:18 +0800474 case MetricConsts.PROVIDER_INTRA:
475 return MetricConsts.FILEOP_COPY_INTRA_PROVIDER;
476 case MetricConsts.PROVIDER_SYSTEM:
477 return MetricConsts.FILEOP_COPY_SYSTEM_PROVIDER;
478 case MetricConsts.PROVIDER_EXTERNAL:
479 return MetricConsts.FILEOP_COPY_EXTERNAL_PROVIDER;
Ben Kwafaa27202016-01-28 16:39:57 -0800480 }
Tomasz Mikolajewski4f225492017-01-27 16:29:30 +0900481 case FileOperationService.OPERATION_COMPRESS:
482 switch (providerType) {
shawnlin9cee68f2019-01-25 11:20:18 +0800483 case MetricConsts.PROVIDER_INTRA:
484 return MetricConsts.FILEOP_COMPRESS_INTRA_PROVIDER;
485 case MetricConsts.PROVIDER_SYSTEM:
486 return MetricConsts.FILEOP_COMPRESS_SYSTEM_PROVIDER;
487 case MetricConsts.PROVIDER_EXTERNAL:
488 return MetricConsts.FILEOP_COMPRESS_EXTERNAL_PROVIDER;
Tomasz Mikolajewski4f225492017-01-27 16:29:30 +0900489 }
shawnlin9cee68f2019-01-25 11:20:18 +0800490 case FileOperationService.OPERATION_EXTRACT:
Tomasz Mikolajewski4f225492017-01-27 16:29:30 +0900491 switch (providerType) {
shawnlin9cee68f2019-01-25 11:20:18 +0800492 case MetricConsts.PROVIDER_INTRA:
493 return MetricConsts.FILEOP_EXTRACT_INTRA_PROVIDER;
494 case MetricConsts.PROVIDER_SYSTEM:
495 return MetricConsts.FILEOP_EXTRACT_SYSTEM_PROVIDER;
496 case MetricConsts.PROVIDER_EXTERNAL:
497 return MetricConsts.FILEOP_EXTRACT_EXTERNAL_PROVIDER;
Tomasz Mikolajewski4f225492017-01-27 16:29:30 +0900498 }
Ben Kwafaa27202016-01-28 16:39:57 -0800499 case FileOperationService.OPERATION_MOVE:
500 switch (providerType) {
shawnlin9cee68f2019-01-25 11:20:18 +0800501 case MetricConsts.PROVIDER_INTRA:
502 return MetricConsts.FILEOP_MOVE_INTRA_PROVIDER;
503 case MetricConsts.PROVIDER_SYSTEM:
504 return MetricConsts.FILEOP_MOVE_SYSTEM_PROVIDER;
505 case MetricConsts.PROVIDER_EXTERNAL:
506 return MetricConsts.FILEOP_MOVE_EXTERNAL_PROVIDER;
Ben Kwafaa27202016-01-28 16:39:57 -0800507 }
508 case FileOperationService.OPERATION_DELETE:
shawnlin9cee68f2019-01-25 11:20:18 +0800509 return MetricConsts.FILEOP_DELETE;
Ben Kwafaa27202016-01-28 16:39:57 -0800510 default:
511 Log.w(TAG, "Unrecognized operation type when logging a file operation");
shawnlin9cee68f2019-01-25 11:20:18 +0800512 return MetricConsts.FILEOP_OTHER;
Ben Kwafaa27202016-01-28 16:39:57 -0800513 }
514 }
515
516 /**
Ben Kwab41a5ed2016-02-17 16:06:22 -0800517 * Maps FileOperationService OpType values, to MetricsOpType values.
518 */
shawnlin9cee68f2019-01-25 11:20:18 +0800519 private static @MetricConsts.FileOp int toMetricsOpType(@OpType int operation) {
Ben Kwab41a5ed2016-02-17 16:06:22 -0800520 switch (operation) {
521 case FileOperationService.OPERATION_COPY:
shawnlin9cee68f2019-01-25 11:20:18 +0800522 return MetricConsts.FILEOP_COPY;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800523 case FileOperationService.OPERATION_MOVE:
shawnlin9cee68f2019-01-25 11:20:18 +0800524 return MetricConsts.FILEOP_MOVE;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800525 case FileOperationService.OPERATION_DELETE:
shawnlin9cee68f2019-01-25 11:20:18 +0800526 return MetricConsts.FILEOP_DELETE;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800527 case FileOperationService.OPERATION_UNKNOWN:
528 default:
shawnlin9cee68f2019-01-25 11:20:18 +0800529 return MetricConsts.FILEOP_UNKNOWN;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800530 }
531 }
532
shawnlin9cee68f2019-01-25 11:20:18 +0800533 private static @MetricConsts.MetricsAction int toMetricsAction(int action) {
Ben Kwab41a5ed2016-02-17 16:06:22 -0800534 switch(action) {
535 case State.ACTION_OPEN:
shawnlin9cee68f2019-01-25 11:20:18 +0800536 return MetricConsts.ACTION_OPEN;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800537 case State.ACTION_CREATE:
shawnlin9cee68f2019-01-25 11:20:18 +0800538 return MetricConsts.ACTION_CREATE;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800539 case State.ACTION_GET_CONTENT:
shawnlin9cee68f2019-01-25 11:20:18 +0800540 return MetricConsts.ACTION_GET_CONTENT;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800541 case State.ACTION_OPEN_TREE:
shawnlin9cee68f2019-01-25 11:20:18 +0800542 return MetricConsts.ACTION_OPEN_TREE;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800543 case State.ACTION_BROWSE:
shawnlin9cee68f2019-01-25 11:20:18 +0800544 return MetricConsts.ACTION_BROWSE;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800545 case State.ACTION_PICK_COPY_DESTINATION:
shawnlin9cee68f2019-01-25 11:20:18 +0800546 return MetricConsts.ACTION_PICK_COPY_DESTINATION;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800547 default:
shawnlin9cee68f2019-01-25 11:20:18 +0800548 return MetricConsts.ACTION_OTHER;
Ben Kwab41a5ed2016-02-17 16:06:22 -0800549 }
550 }
551
shawnline0ba46d2019-01-25 15:21:26 +0800552 private static int getSearchMode(boolean isKeyword, boolean isChip) {
553 if (isKeyword && isChip) {
554 return MetricConsts.SEARCH_KEYWORD_N_CHIPS;
555 } else if (isKeyword) {
556 return MetricConsts.SEARCH_KEYWORD;
557 } else if (isChip) {
558 return MetricConsts.SEARCH_CHIPS;
559 } else {
560 return MetricConsts.SEARCH_UNKNOWN;
561 }
562 }
563
Ben Kwab41a5ed2016-02-17 16:06:22 -0800564 /**
Ben Kwafaa27202016-01-28 16:39:57 -0800565 * Count the given src documents and provide a tally of how many come from the same provider as
566 * the dst document (if a dst is provided), how many come from system providers, and how many
567 * come from external 3rd-party providers.
568 */
Steve McKay99f1dc32016-12-29 16:02:01 -0800569 private static void countProviders(
570 ProviderCounts counts, List<DocumentInfo> srcs, @Nullable DocumentInfo dst) {
Ben Kwafaa27202016-01-28 16:39:57 -0800571 for (DocumentInfo doc: srcs) {
Steve McKay99f1dc32016-12-29 16:02:01 -0800572 countForAuthority(counts, doc.authority, dst);
Ben Kwafaa27202016-01-28 16:39:57 -0800573 }
Steve McKay99f1dc32016-12-29 16:02:01 -0800574 }
575
576 /**
577 * Count the given uris and provide a tally of how many come from the same provider as
578 * the dst document (if a dst is provided), how many come from system providers, and how many
579 * come from external 3rd-party providers.
580 */
581 private static void countProviders(ProviderCounts counts, List<Uri> uris) {
582 for (Uri uri: uris) {
583 countForAuthority(counts, uri.getAuthority(), null);
584 }
585 }
586
587 private static void countForAuthority(
588 ProviderCounts counts, String authority, @Nullable DocumentInfo dst) {
589 if (dst != null && authority.equals(dst.authority)) {
590 counts.intraProvider++;
591 } else if (isSystemProvider(authority)){
592 counts.systemProvider++;
593 } else {
594 counts.externalProvider++;
595 }
Ben Kwafaa27202016-01-28 16:39:57 -0800596 }
597
598 private static class ProviderCounts {
599 int intraProvider;
600 int systemProvider;
601 int externalProvider;
602 }
felkachangec40c5c2018-05-14 18:01:11 +0800603
604 private static String getRootIdSafely(Uri uri) {
605 try {
606 return DocumentsContract.getRootId(uri);
607 } catch (IllegalArgumentException iae) {
608 Log.w(TAG, "Invalid root Uri " + uri.toSafeString());
609 }
610 return null;
611 }
Ben Kwa1c0a3892016-01-26 11:50:03 -0800612}