1 /* 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 17 package com.android.documentsui; 18 19 import static android.content.ContentResolver.wrap; 20 21 import static com.android.documentsui.DocumentsApplication.acquireUnstableProviderOrThrow; 22 23 import android.content.ContentProviderClient; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.pm.ResolveInfo; 27 import android.net.Uri; 28 import android.os.RemoteException; 29 import android.provider.DocumentsContract; 30 import android.provider.DocumentsContract.Path; 31 import android.provider.DocumentsProvider; 32 import android.util.Log; 33 import android.util.StatsEvent; 34 import android.util.StatsLog; 35 36 import androidx.annotation.Nullable; 37 38 import com.android.documentsui.base.DocumentInfo; 39 import com.android.documentsui.base.Providers; 40 import com.android.documentsui.base.RootInfo; 41 import com.android.documentsui.base.State; 42 import com.android.documentsui.base.UserId; 43 import com.android.documentsui.files.LauncherActivity; 44 import com.android.documentsui.picker.PickResult; 45 import com.android.documentsui.roots.ProvidersAccess; 46 import com.android.documentsui.services.FileOperationService; 47 import com.android.documentsui.services.FileOperationService.OpType; 48 import com.android.documentsui.util.VersionUtils; 49 50 import java.io.FileNotFoundException; 51 import java.util.List; 52 53 /** 54 * Methods for logging metrics. 55 */ 56 public final class Metrics { 57 private static final String TAG = "Metrics"; 58 59 /** 60 * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up. 61 * 62 * @param state 63 * @param intent 64 */ logActivityLaunch(State state, Intent intent)65 public static void logActivityLaunch(State state, Intent intent) { 66 Uri uri = intent.getData(); 67 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_LAUNCH_REPORTED, 68 toMetricsAction(state.action), false, 69 sanitizeMime(intent.getType()), sanitizeRoot(uri)); 70 } 71 72 /** 73 * Logs when DocumentsUI are launched with {@link DocumentsContract#EXTRA_INITIAL_URI}. 74 * 75 * @param state used to resolve action 76 * @param rootUri the resolved rootUri, or {@code null} if the provider doesn't 77 * support {@link DocumentsProvider#findDocumentPath(String, String)} 78 */ logLaunchAtLocation(State state, @Nullable Uri rootUri)79 public static void logLaunchAtLocation(State state, @Nullable Uri rootUri) { 80 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_LAUNCH_REPORTED, 81 toMetricsAction(state.action), true, 82 MetricConsts.MIME_UNKNOWN, sanitizeRoot(rootUri)); 83 } 84 85 /** 86 * Logs a root visited event in file managers. Call this when the user 87 * taps on a root in {@link com.android.documentsui.sidebar.RootsFragment}. 88 * @param scope 89 * @param info 90 */ logRootVisited(@etricConsts.ContextScope int scope, RootInfo info)91 public static void logRootVisited(@MetricConsts.ContextScope int scope, RootInfo info) { 92 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_ROOT_VISITED, scope, sanitizeRoot(info)); 93 } 94 logLaunchOtherApp(boolean acrossProfile)95 public static void logLaunchOtherApp(boolean acrossProfile) { 96 DevicePolicyEventLogger.write(DevicePolicyMetricConsts.EVENT_ID_DOCSUI_LAUNCH_OTHER_APP, 97 acrossProfile); 98 } 99 logCrossProfileEmptyState(CrossProfileException e)100 public static void logCrossProfileEmptyState(CrossProfileException e) { 101 int eventId; 102 if (e instanceof CrossProfileQuietModeException) { 103 eventId = DevicePolicyMetricConsts.EVENT_ID_DOCSUI_EMPTY_STATE_QUIET_MODE; 104 } else if (e instanceof CrossProfileNoPermissionException) { 105 eventId = DevicePolicyMetricConsts.EVENT_ID_DOCSUI_EMPTY_STATE_NO_PERMISSION; 106 } else { 107 Log.d(TAG, "logCrossProfileEmptyState: Unexpected exception " + e); 108 return; 109 } 110 DevicePolicyEventLogger.write(eventId, /* booleanValue= */ true); 111 } 112 113 /** 114 * Logs an app visited event in file pickers. Call this when the user visits 115 * on an app in the RootsFragment. 116 * 117 * @param info 118 */ logAppVisited(ResolveInfo info)119 public static void logAppVisited(ResolveInfo info) { 120 DocumentsStatsLog.write( 121 DocumentsStatsLog.DOCS_UI_ROOT_VISITED, 122 MetricConsts.PICKER_SCOPE, sanitizeRoot(info)); 123 } 124 125 /** 126 * Logs file operation stats. Call this when a file operation has completed. The given 127 * DocumentInfo is only used to distinguish broad categories of actions (e.g. copying from one 128 * provider to another vs copying within a given provider). No PII is logged. 129 * 130 * @param operationType 131 * @param srcs 132 * @param dst 133 */ logFileOperation( @pType int operationType, List<DocumentInfo> srcs, @Nullable DocumentInfo dst)134 public static void logFileOperation( 135 @OpType int operationType, 136 List<DocumentInfo> srcs, 137 @Nullable DocumentInfo dst) { 138 ProviderCounts counts = new ProviderCounts(); 139 countProviders(counts, srcs, dst); 140 if (counts.intraProvider > 0) { 141 logIntraProviderFileOps(dst.authority, operationType); 142 } 143 if (counts.systemProvider > 0) { 144 // Log file operations on system providers. 145 logInterProviderFileOps(MetricConsts.PROVIDER_SYSTEM, dst, operationType); 146 } 147 if (counts.externalProvider > 0) { 148 // Log file operations on external providers. 149 logInterProviderFileOps(MetricConsts.PROVIDER_EXTERNAL, dst, operationType); 150 } 151 } 152 logFileOperated( @pType int operationType, @MetricConsts.FileOpMode int approach)153 public static void logFileOperated( 154 @OpType int operationType, @MetricConsts.FileOpMode int approach) { 155 switch (operationType) { 156 case FileOperationService.OPERATION_COPY: 157 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_FILE_OP_COPY_MOVE_MODE_REPORTED, 158 MetricConsts.FILEOP_COPY, approach); 159 break; 160 case FileOperationService.OPERATION_MOVE: 161 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_FILE_OP_COPY_MOVE_MODE_REPORTED, 162 MetricConsts.FILEOP_MOVE, approach); 163 break; 164 } 165 } 166 167 /** 168 * Logs create directory operation. It is a part of file operation stats. We do not 169 * differentiate between internal and external locations, all create directory operations are 170 * logged under COUNT_FILEOP_SYSTEM. Call this when a create directory operation has completed. 171 */ logCreateDirOperation()172 public static void logCreateDirOperation() { 173 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP, 174 MetricConsts.PROVIDER_SYSTEM, MetricConsts.FILEOP_CREATE_DIR); 175 } 176 177 /** 178 * Logs rename file operation. It is a part of file operation stats. We do not differentiate 179 * between internal and external locations, all rename operations are logged under 180 * COUNT_FILEOP_SYSTEM. Call this when a rename file operation has completed. 181 */ logRenameFileOperation()182 public static void logRenameFileOperation() { 183 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP, 184 MetricConsts.PROVIDER_SYSTEM, MetricConsts.FILEOP_RENAME); 185 } 186 187 /** 188 * Logs some kind of file operation error. Call this when a file operation (e.g. copy, delete) 189 * fails. 190 * 191 * @param operationType 192 * @param failedFiles 193 */ logFileOperationErrors(@pType int operationType, List<DocumentInfo> failedFiles, List<Uri> failedUris)194 public static void logFileOperationErrors(@OpType int operationType, 195 List<DocumentInfo> failedFiles, List<Uri> failedUris) { 196 ProviderCounts counts = new ProviderCounts(); 197 countProviders(counts, failedFiles, null); 198 // TODO: Report URI errors separate from file operation errors. 199 countProviders(counts, failedUris); 200 @MetricConsts.FileOp int opCode = MetricConsts.FILEOP_OTHER_ERROR; 201 switch (operationType) { 202 case FileOperationService.OPERATION_COPY: 203 opCode = MetricConsts.FILEOP_COPY_ERROR; 204 break; 205 case FileOperationService.OPERATION_COMPRESS: 206 opCode = MetricConsts.FILEOP_COMPRESS_ERROR; 207 break; 208 case FileOperationService.OPERATION_EXTRACT: 209 opCode = MetricConsts.FILEOP_EXTRACT_ERROR; 210 break; 211 case FileOperationService.OPERATION_DELETE: 212 opCode = MetricConsts.FILEOP_DELETE_ERROR; 213 break; 214 case FileOperationService.OPERATION_MOVE: 215 opCode = MetricConsts.FILEOP_MOVE_ERROR; 216 break; 217 } 218 if (counts.systemProvider > 0) { 219 DocumentsStatsLog.write( 220 DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP, 221 MetricConsts.PROVIDER_SYSTEM, opCode); 222 } 223 if (counts.externalProvider > 0) { 224 DocumentsStatsLog.write( 225 DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP, 226 MetricConsts.PROVIDER_EXTERNAL, opCode); 227 } 228 } 229 logFileOperationFailure( Context context, @MetricConsts.SubFileOp int subFileOp, Uri docUri)230 public static void logFileOperationFailure( 231 Context context, @MetricConsts.SubFileOp int subFileOp, Uri docUri) { 232 final String authority = docUri.getAuthority(); 233 switch (authority) { 234 case Providers.AUTHORITY_MEDIA: 235 DocumentsStatsLog.write( 236 DocumentsStatsLog.DOCS_UI_FILE_OP_FAILURE, 237 MetricConsts.AUTH_MEDIA, subFileOp); 238 break; 239 case Providers.AUTHORITY_STORAGE: 240 logStorageFileOperationFailure(context, subFileOp, docUri); 241 break; 242 case Providers.AUTHORITY_DOWNLOADS: 243 DocumentsStatsLog.write( 244 DocumentsStatsLog.DOCS_UI_FILE_OP_FAILURE, 245 MetricConsts.AUTH_DOWNLOADS, subFileOp); 246 break; 247 case Providers.AUTHORITY_MTP: 248 DocumentsStatsLog.write( 249 DocumentsStatsLog.DOCS_UI_FILE_OP_FAILURE, 250 MetricConsts.AUTH_MTP, subFileOp); 251 break; 252 default: 253 DocumentsStatsLog.write( 254 DocumentsStatsLog.DOCS_UI_FILE_OP_FAILURE, 255 MetricConsts.AUTH_OTHER, subFileOp); 256 break; 257 } 258 } 259 260 /** 261 * Logs create directory operation error. We do not differentiate between internal and external 262 * locations, all create directory errors are logged under COUNT_FILEOP_SYSTEM. Call this when a 263 * create directory operation fails. 264 */ logCreateDirError()265 public static void logCreateDirError() { 266 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP, 267 MetricConsts.PROVIDER_SYSTEM, MetricConsts.FILEOP_CREATE_DIR_ERROR); 268 } 269 270 /** 271 * Logs rename file operation error. We do not differentiate between internal and external 272 * locations, all rename errors are logged under COUNT_FILEOP_SYSTEM. Call this 273 * when a rename file operation fails. 274 */ logRenameFileError()275 public static void logRenameFileError() { 276 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP, 277 MetricConsts.PROVIDER_SYSTEM, MetricConsts.FILEOP_RENAME_ERROR); 278 } 279 280 /** 281 * Logs the cancellation of a file operation. Call this when a Job is canceled. 282 * 283 * @param operationType 284 */ logFileOperationCancelled(@pType int operationType)285 public static void logFileOperationCancelled(@OpType int operationType) { 286 DocumentsStatsLog.write( 287 DocumentsStatsLog.DOCS_UI_FILE_OP_CANCELED, toMetricsOpType(operationType)); 288 } 289 290 /** 291 * Logs startup time in milliseconds. 292 * 293 * @param startupMs Startup time in milliseconds. 294 */ logStartupMs(int startupMs)295 public static void logStartupMs(int startupMs) { 296 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_STARTUP_MS, startupMs); 297 } 298 logInterProviderFileOps( @etricConsts.Provider int providerType, DocumentInfo dst, @OpType int operationType)299 private static void logInterProviderFileOps( 300 @MetricConsts.Provider int providerType, 301 DocumentInfo dst, 302 @OpType int operationType) { 303 if (operationType == FileOperationService.OPERATION_DELETE) { 304 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP, 305 providerType, MetricConsts.FILEOP_DELETE); 306 } else { 307 assert(dst != null); 308 @MetricConsts.Provider int opProviderType = isSystemProvider(dst.authority) 309 ? MetricConsts.PROVIDER_SYSTEM : MetricConsts.PROVIDER_EXTERNAL; 310 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP, 311 providerType, getOpCode(operationType, opProviderType)); 312 } 313 } 314 logIntraProviderFileOps(String authority, @OpType int operationType)315 private static void logIntraProviderFileOps(String authority, @OpType int operationType) { 316 @MetricConsts.Provider int providerType = isSystemProvider(authority) 317 ? MetricConsts.PROVIDER_SYSTEM : MetricConsts.PROVIDER_EXTERNAL; 318 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_PROVIDER_FILE_OP, 319 providerType, getOpCode(operationType, MetricConsts.PROVIDER_INTRA)); 320 } 321 322 /** 323 * Logs the action that was started by user. 324 * 325 * @param userAction 326 */ logUserAction(@etricConsts.UserAction int userAction)327 public static void logUserAction(@MetricConsts.UserAction int userAction) { 328 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_USER_ACTION_REPORTED, userAction); 329 } 330 logPickerLaunchedFrom(String packgeName)331 public static void logPickerLaunchedFrom(String packgeName) { 332 DocumentsStatsLog.write( 333 DocumentsStatsLog.DOCS_UI_PICKER_LAUNCHED_FROM_REPORTED, packgeName); 334 } 335 logSearchType(int searchType)336 public static void logSearchType(int searchType) { 337 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_SEARCH_TYPE_REPORTED, searchType); 338 } 339 logSearchMode(boolean isKeywordSearch, boolean isChipsSearch)340 public static void logSearchMode(boolean isKeywordSearch, boolean isChipsSearch) { 341 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_SEARCH_MODE_REPORTED, 342 getSearchMode(isKeywordSearch, isChipsSearch)); 343 } 344 345 /** 346 * Logs drag initiated from which app, documentsUI or another app. 347 */ logDragInitiated(boolean isDragInitatedFromDocsUI)348 public static void logDragInitiated(boolean isDragInitatedFromDocsUI) { 349 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_DRAG_AND_DROP_REPORTED, 350 isDragInitatedFromDocsUI); 351 } 352 logPickResult(PickResult result)353 public static void logPickResult(PickResult result) { 354 DocumentsStatsLog.write( 355 DocumentsStatsLog.DOCS_UI_PICK_RESULT_REPORTED, 356 result.getActionCount(), 357 result.getDuration(), 358 result.getFileCount(), 359 result.isSearching(), 360 result.getRoot(), 361 result.getMimeType(), 362 result.getRepeatedPickTimes()); 363 364 DevicePolicyEventLogger.write(DevicePolicyMetricConsts.EVENT_ID_DOCSUI_PICK_RESULT, 365 result.hasCrossProfileUri()); 366 } 367 logStorageFileOperationFailure( Context context, @MetricConsts.SubFileOp int subFileOp, Uri docUri)368 private static void logStorageFileOperationFailure( 369 Context context, @MetricConsts.SubFileOp int subFileOp, Uri docUri) { 370 assert(Providers.AUTHORITY_STORAGE.equals(docUri.getAuthority())); 371 boolean isInternal; 372 try (ContentProviderClient client = acquireUnstableProviderOrThrow( 373 context.getContentResolver(), Providers.AUTHORITY_STORAGE)) { 374 final Path path = DocumentsContract.findDocumentPath(wrap(client), docUri); 375 final ProvidersAccess providers = DocumentsApplication.getProvidersCache(context); 376 final RootInfo root = providers.getRootOneshot(UserId.DEFAULT_USER, 377 Providers.AUTHORITY_STORAGE, path.getRootId()); 378 isInternal = !root.supportsEject(); 379 } catch (FileNotFoundException | RemoteException | RuntimeException e) { 380 Log.e(TAG, "Failed to obtain its root info. Log the metrics as internal.", e); 381 // It's not very likely to have an external storage so log it as internal. 382 isInternal = true; 383 } 384 @MetricConsts.MetricsAuth final int authority = isInternal 385 ? MetricConsts.AUTH_STORAGE_INTERNAL : MetricConsts.AUTH_STORAGE_EXTERNAL; 386 DocumentsStatsLog.write(DocumentsStatsLog.DOCS_UI_FILE_OP_FAILURE, authority, subFileOp); 387 } 388 389 /** 390 * Generates an integer identifying the given root. For privacy, this function only recognizes a 391 * small set of hard-coded roots (ones provided by the system). Other roots are all grouped into 392 * a single ROOT_OTHER bucket. 393 */ sanitizeRoot(Uri uri)394 private static @MetricConsts.Root int sanitizeRoot(Uri uri) { 395 if (uri == null || uri.getAuthority() == null || LauncherActivity.isLaunchUri(uri)) { 396 return MetricConsts.ROOT_NONE; 397 } 398 switch (uri.getAuthority()) { 399 case Providers.AUTHORITY_MEDIA: 400 String rootId = getRootIdSafely(uri); 401 if (rootId == null) { 402 return MetricConsts.ROOT_NONE; 403 } 404 switch (rootId) { 405 case Providers.ROOT_ID_AUDIO: 406 return MetricConsts.ROOT_AUDIO; 407 case Providers.ROOT_ID_IMAGES: 408 return MetricConsts.ROOT_IMAGES; 409 case Providers.ROOT_ID_VIDEOS: 410 return MetricConsts.ROOT_VIDEOS; 411 case Providers.ROOT_ID_DOCUMENTS: 412 return MetricConsts.ROOT_DOCUMENTS; 413 default: 414 return MetricConsts.ROOT_OTHER_DOCS_PROVIDER; 415 } 416 case Providers.AUTHORITY_STORAGE: 417 rootId = getRootIdSafely(uri); 418 if (rootId == null) { 419 return MetricConsts.ROOT_NONE; 420 } 421 if (Providers.ROOT_ID_HOME.equals(rootId)) { 422 return MetricConsts.ROOT_HOME; 423 } else { 424 return MetricConsts.ROOT_DEVICE_STORAGE; 425 } 426 case Providers.AUTHORITY_DOWNLOADS: 427 return MetricConsts.ROOT_DOWNLOADS; 428 case Providers.AUTHORITY_MTP: 429 return MetricConsts.ROOT_MTP; 430 default: 431 return MetricConsts.ROOT_OTHER_DOCS_PROVIDER; 432 } 433 } 434 435 /** @see #sanitizeRoot(Uri) */ sanitizeRoot(RootInfo root)436 public static @MetricConsts.Root int sanitizeRoot(RootInfo root) { 437 if (root.isRecents()) { 438 // Recents root is special and only identifiable via this method call. Other roots are 439 // identified by URI. 440 return MetricConsts.ROOT_RECENTS; 441 } else { 442 return sanitizeRoot(root.getUri()); 443 } 444 } 445 446 /** @see #sanitizeRoot(Uri) */ sanitizeRoot(ResolveInfo info)447 public static @MetricConsts.Root int sanitizeRoot(ResolveInfo info) { 448 // Log all apps under a single bucket in the roots histogram. 449 return MetricConsts.ROOT_THIRD_PARTY_APP; 450 } 451 452 /** 453 * Generates an int identifying a mime type. For privacy, this function only recognizes a small 454 * set of hard-coded types. For any other type, this function returns "other". 455 * 456 * @param mimeType 457 * @return 458 */ sanitizeMime(String mimeType)459 public static @MetricConsts.Mime int sanitizeMime(String mimeType) { 460 if (mimeType == null) { 461 return MetricConsts.MIME_NONE; 462 } else if ("*/*".equals(mimeType)) { 463 return MetricConsts.MIME_ANY; 464 } else { 465 String type = mimeType.substring(0, mimeType.indexOf('/')); 466 switch (type) { 467 case "application": 468 return MetricConsts.MIME_APPLICATION; 469 case "audio": 470 return MetricConsts.MIME_AUDIO; 471 case "image": 472 return MetricConsts.MIME_IMAGE; 473 case "message": 474 return MetricConsts.MIME_MESSAGE; 475 case "multipart": 476 return MetricConsts.MIME_MULTIPART; 477 case "text": 478 return MetricConsts.MIME_TEXT; 479 case "video": 480 return MetricConsts.MIME_VIDEO; 481 } 482 } 483 // Bucket all other types into one bucket. 484 return MetricConsts.MIME_OTHER; 485 } 486 isSystemProvider(String authority)487 private static boolean isSystemProvider(String authority) { 488 switch (authority) { 489 case Providers.AUTHORITY_MEDIA: 490 case Providers.AUTHORITY_STORAGE: 491 case Providers.AUTHORITY_DOWNLOADS: 492 return true; 493 default: 494 return false; 495 } 496 } 497 498 /** 499 * @param operation 500 * @param providerType 501 * @return An opcode, suitable for use as histogram bucket, for the given operation/provider 502 * combination. 503 */ getOpCode( @pType int operation, @MetricConsts.Provider int providerType)504 private static @MetricConsts.FileOp int getOpCode( 505 @OpType int operation, @MetricConsts.Provider int providerType) { 506 switch (operation) { 507 case FileOperationService.OPERATION_COPY: 508 switch (providerType) { 509 case MetricConsts.PROVIDER_INTRA: 510 return MetricConsts.FILEOP_COPY_INTRA_PROVIDER; 511 case MetricConsts.PROVIDER_SYSTEM: 512 return MetricConsts.FILEOP_COPY_SYSTEM_PROVIDER; 513 case MetricConsts.PROVIDER_EXTERNAL: 514 return MetricConsts.FILEOP_COPY_EXTERNAL_PROVIDER; 515 } 516 case FileOperationService.OPERATION_COMPRESS: 517 switch (providerType) { 518 case MetricConsts.PROVIDER_INTRA: 519 return MetricConsts.FILEOP_COMPRESS_INTRA_PROVIDER; 520 case MetricConsts.PROVIDER_SYSTEM: 521 return MetricConsts.FILEOP_COMPRESS_SYSTEM_PROVIDER; 522 case MetricConsts.PROVIDER_EXTERNAL: 523 return MetricConsts.FILEOP_COMPRESS_EXTERNAL_PROVIDER; 524 } 525 case FileOperationService.OPERATION_EXTRACT: 526 switch (providerType) { 527 case MetricConsts.PROVIDER_INTRA: 528 return MetricConsts.FILEOP_EXTRACT_INTRA_PROVIDER; 529 case MetricConsts.PROVIDER_SYSTEM: 530 return MetricConsts.FILEOP_EXTRACT_SYSTEM_PROVIDER; 531 case MetricConsts.PROVIDER_EXTERNAL: 532 return MetricConsts.FILEOP_EXTRACT_EXTERNAL_PROVIDER; 533 } 534 case FileOperationService.OPERATION_MOVE: 535 switch (providerType) { 536 case MetricConsts.PROVIDER_INTRA: 537 return MetricConsts.FILEOP_MOVE_INTRA_PROVIDER; 538 case MetricConsts.PROVIDER_SYSTEM: 539 return MetricConsts.FILEOP_MOVE_SYSTEM_PROVIDER; 540 case MetricConsts.PROVIDER_EXTERNAL: 541 return MetricConsts.FILEOP_MOVE_EXTERNAL_PROVIDER; 542 } 543 case FileOperationService.OPERATION_DELETE: 544 return MetricConsts.FILEOP_DELETE; 545 default: 546 Log.w(TAG, "Unrecognized operation type when logging a file operation"); 547 return MetricConsts.FILEOP_OTHER; 548 } 549 } 550 551 /** 552 * Maps FileOperationService OpType values, to MetricsOpType values. 553 */ toMetricsOpType(@pType int operation)554 private static @MetricConsts.FileOp int toMetricsOpType(@OpType int operation) { 555 switch (operation) { 556 case FileOperationService.OPERATION_COPY: 557 return MetricConsts.FILEOP_COPY; 558 case FileOperationService.OPERATION_MOVE: 559 return MetricConsts.FILEOP_MOVE; 560 case FileOperationService.OPERATION_DELETE: 561 return MetricConsts.FILEOP_DELETE; 562 case FileOperationService.OPERATION_UNKNOWN: 563 default: 564 return MetricConsts.FILEOP_UNKNOWN; 565 } 566 } 567 toMetricsAction(int action)568 private static @MetricConsts.MetricsAction int toMetricsAction(int action) { 569 switch(action) { 570 case State.ACTION_OPEN: 571 return MetricConsts.ACTION_OPEN; 572 case State.ACTION_CREATE: 573 return MetricConsts.ACTION_CREATE; 574 case State.ACTION_GET_CONTENT: 575 return MetricConsts.ACTION_GET_CONTENT; 576 case State.ACTION_OPEN_TREE: 577 return MetricConsts.ACTION_OPEN_TREE; 578 case State.ACTION_BROWSE: 579 return MetricConsts.ACTION_BROWSE; 580 case State.ACTION_PICK_COPY_DESTINATION: 581 return MetricConsts.ACTION_PICK_COPY_DESTINATION; 582 default: 583 return MetricConsts.ACTION_OTHER; 584 } 585 } 586 getSearchMode(boolean isKeyword, boolean isChip)587 private static int getSearchMode(boolean isKeyword, boolean isChip) { 588 if (isKeyword && isChip) { 589 return MetricConsts.SEARCH_KEYWORD_N_CHIPS; 590 } else if (isKeyword) { 591 return MetricConsts.SEARCH_KEYWORD; 592 } else if (isChip) { 593 return MetricConsts.SEARCH_CHIPS; 594 } else { 595 return MetricConsts.SEARCH_UNKNOWN; 596 } 597 } 598 599 /** 600 * Count the given src documents and provide a tally of how many come from the same provider as 601 * the dst document (if a dst is provided), how many come from system providers, and how many 602 * come from external 3rd-party providers. 603 */ countProviders( ProviderCounts counts, List<DocumentInfo> srcs, @Nullable DocumentInfo dst)604 private static void countProviders( 605 ProviderCounts counts, List<DocumentInfo> srcs, @Nullable DocumentInfo dst) { 606 for (DocumentInfo doc: srcs) { 607 countForAuthority(counts, doc.authority, dst); 608 } 609 } 610 611 /** 612 * Count the given uris and provide a tally of how many come from the same provider as 613 * the dst document (if a dst is provided), how many come from system providers, and how many 614 * come from external 3rd-party providers. 615 */ countProviders(ProviderCounts counts, List<Uri> uris)616 private static void countProviders(ProviderCounts counts, List<Uri> uris) { 617 for (Uri uri: uris) { 618 countForAuthority(counts, uri.getAuthority(), null); 619 } 620 } 621 countForAuthority( ProviderCounts counts, String authority, @Nullable DocumentInfo dst)622 private static void countForAuthority( 623 ProviderCounts counts, String authority, @Nullable DocumentInfo dst) { 624 if (dst != null && authority.equals(dst.authority)) { 625 counts.intraProvider++; 626 } else if (isSystemProvider(authority)){ 627 counts.systemProvider++; 628 } else { 629 counts.externalProvider++; 630 } 631 } 632 633 private static class ProviderCounts { 634 int intraProvider; 635 int systemProvider; 636 int externalProvider; 637 } 638 getRootIdSafely(Uri uri)639 private static String getRootIdSafely(Uri uri) { 640 try { 641 return DocumentsContract.getRootId(uri); 642 } catch (IllegalArgumentException iae) { 643 Log.w(TAG, "Invalid root Uri " + uri.toSafeString()); 644 } 645 return null; 646 } 647 648 /** 649 * The implementation is copied from StatsLogInternal for the DEVICE_POLICY_EVENT. This is a 650 * no-op pre-R. 651 */ 652 private static class DevicePolicyEventLogger { write(@evicePolicyMetricConsts.EventId int eventId, boolean booleanValue)653 public static void write(@DevicePolicyMetricConsts.EventId int eventId, 654 boolean booleanValue) { 655 if (!VersionUtils.isAtLeastR()) { 656 return; 657 } 658 final StatsEvent.Builder builder = StatsEvent.newBuilder(); 659 builder.setAtomId(DevicePolicyMetricConsts.ATOM_DEVICE_POLICY_EVENT); 660 builder.writeInt(eventId); // eventId 661 builder.writeString(null); // adminPackageName 662 builder.writeInt(0); // intValue 663 builder.writeBoolean(booleanValue); // booleanValue 664 builder.writeLong(0); // timePeriodMs 665 builder.writeByteArray(new byte[0]); // bytes 666 667 builder.usePooledBuffer(); 668 StatsLog.write(builder.build()); 669 } 670 } 671 } 672