From 3e166b27fcf67dedc1ca1507266484763c7e15ea Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Wed, 24 Feb 2016 10:17:41 -0800 Subject: [PATCH] Created metric events for scoped directory access API. BUG: 27334821 Change-Id: I8a1bfc328dcd26b42bb66884d14b34ad11aa232f --- core/java/android/os/Environment.java | 2 +- .../src/com/android/documentsui/Metrics.java | 83 +++++++++++++++++++++- .../documentsui/OpenExternalDirectoryActivity.java | 44 +++++++++--- proto/src/metrics_constants.proto | 25 +++++++ 4 files changed, 144 insertions(+), 10 deletions(-) diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 1085b1e9dc6b..2410503c217b 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -493,7 +493,7 @@ public class Environment { * * @hide */ - private static final String[] STANDARD_DIRECTORIES = { + public static final String[] STANDARD_DIRECTORIES = { DIRECTORY_MUSIC, DIRECTORY_PODCASTS, DIRECTORY_RINGTONES, diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java index bff65d5b5caf..dcaea15459a6 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java +++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java @@ -16,10 +16,12 @@ package com.android.documentsui; +import static android.os.Environment.STANDARD_DIRECTORIES; import static com.android.documentsui.Shared.DEBUG; - import android.annotation.IntDef; import android.annotation.Nullable; +import android.annotation.StringDef; +import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; @@ -32,6 +34,7 @@ import com.android.documentsui.model.RootInfo; import com.android.documentsui.services.FileOperationService; import com.android.documentsui.services.FileOperationService.OpType; import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.MetricsProto.MetricsEvent; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -376,6 +379,84 @@ public final class Metrics { logHistogram(context, histogram, getOpCode(operationType, PROVIDER_INTRA)); } + // Types for logInvalidScopedAccessRequest + public static final String SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS = + "scoped_directory_access_invalid_args"; + public static final String SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY = + "scoped_directory_access_invalid_dir"; + public static final String SCOPED_DIRECTORY_ACCESS_ERROR = + "scoped_directory_access_error"; + + @StringDef(value = { + SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS, + SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY, + SCOPED_DIRECTORY_ACCESS_ERROR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface InvalidScopedAccess{} + + public static void logInvalidScopedAccessRequest(Context context, + @InvalidScopedAccess String type) { + MetricsLogger.count(context, type, 1); + switch (type) { + case SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS: + case SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY: + case SCOPED_DIRECTORY_ACCESS_ERROR: + MetricsLogger.count(context, type, 1); + break; + default: + Log.wtf(TAG, "invalid InvalidScopedAccess: " + type); + } + } + + // Types for logValidScopedAccessRequest + public static final int SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED = 0; + public static final int SCOPED_DIRECTORY_ACCESS_GRANTED = 1; + public static final int SCOPED_DIRECTORY_ACCESS_DENIED = 2; + + @IntDef(flag = true, value = { + SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED, + SCOPED_DIRECTORY_ACCESS_GRANTED, + SCOPED_DIRECTORY_ACCESS_DENIED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ScopedAccessGrant {} + + public static void logValidScopedAccessRequest(Activity activity, String directory, + @ScopedAccessGrant int type) { + int index = -1; + for (int i = 0; i < STANDARD_DIRECTORIES.length; i++) { + if (STANDARD_DIRECTORIES[i].equals(directory)) { + index = i; + break; + } + } + final String packageName = activity.getCallingPackage(); + switch (type) { + case SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED: + MetricsLogger.action(activity, + MetricsEvent.ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_PACKAGE, + packageName); + MetricsLogger.action(activity, + MetricsEvent.ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_FOLDER, index); + break; + case SCOPED_DIRECTORY_ACCESS_GRANTED: + MetricsLogger.action(activity, + MetricsEvent.ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_PACKAGE, packageName); + MetricsLogger.action(activity, + MetricsEvent.ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_FOLDER, index); + break; + case SCOPED_DIRECTORY_ACCESS_DENIED: + MetricsLogger.action(activity, + MetricsEvent.ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_PACKAGE, packageName); + MetricsLogger.action(activity, + MetricsEvent.ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_FOLDER, index); + break; + default: + Log.wtf(TAG, "invalid ScopedAccessGrant: " + type); + } + } + /** * Internal method for making a MetricsLogger.count call. Increments the given counter by 1. * diff --git a/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java b/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java index 27d6797fdce6..dc529ceb18ec 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java @@ -17,9 +17,18 @@ package com.android.documentsui; import static android.os.Environment.isStandardDirectory; +import static android.os.Environment.STANDARD_DIRECTORIES; import static android.os.storage.StorageVolume.EXTRA_DIRECTORY_NAME; import static android.os.storage.StorageVolume.EXTRA_STORAGE_VOLUME; import static com.android.documentsui.Shared.DEBUG; +import static com.android.documentsui.Metrics.logInvalidScopedAccessRequest; +import static com.android.documentsui.Metrics.logValidScopedAccessRequest; +import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED; +import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_DENIED; +import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_ERROR; +import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_GRANTED; +import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS; +import static com.android.documentsui.Metrics.SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY; import android.app.Activity; import android.app.ActivityManager; @@ -73,6 +82,7 @@ public class OpenExternalDirectoryActivity extends Activity { final Intent intent = getIntent(); if (intent == null) { if (DEBUG) Log.d(TAG, "missing intent"); + logInvalidScopedAccessRequest(this, SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS); setResult(RESULT_CANCELED); finish(); return; @@ -82,12 +92,14 @@ public class OpenExternalDirectoryActivity extends Activity { if (DEBUG) Log.d(TAG, "extra " + EXTRA_STORAGE_VOLUME + " is not a StorageVolume: " + storageVolume); + logInvalidScopedAccessRequest(this, SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS); setResult(RESULT_CANCELED); finish(); return; } final String directoryName = intent.getStringExtra(EXTRA_DIRECTORY_NAME); if (directoryName == null) { + logInvalidScopedAccessRequest(this, SCOPED_DIRECTORY_ACCESS_INVALID_ARGUMENTS); if (DEBUG) Log.d(TAG, "missing extra " + EXTRA_DIRECTORY_NAME + " on " + intent); setResult(RESULT_CANCELED); finish(); @@ -125,6 +137,7 @@ public class OpenExternalDirectoryActivity extends Activity { } catch (IOException e) { Log.e(TAG, "Could not get canonical file for volume " + storageVolume.dump() + " and directory " + directoryName); + logInvalidScopedAccessRequest(activity, SCOPED_DIRECTORY_ACCESS_ERROR); return false; } final StorageManager sm = @@ -138,6 +151,7 @@ public class OpenExternalDirectoryActivity extends Activity { if (DEBUG) Log.d(TAG, "Directory '" + directory + "' is not standard (full path: '" + file.getAbsolutePath() + "')"); + logInvalidScopedAccessRequest(activity, SCOPED_DIRECTORY_ACCESS_INVALID_DIRECTORY); return false; } @@ -159,6 +173,8 @@ public class OpenExternalDirectoryActivity extends Activity { // Checks if the user has granted the permission already. final Intent intent = getIntentForExistingPermission(activity, file); if (intent != null) { + logValidScopedAccessRequest(activity, directory, + SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED); activity.setResult(RESULT_OK, intent); activity.finish(); return true; @@ -166,12 +182,14 @@ public class OpenExternalDirectoryActivity extends Activity { if (volumeLabel == null) { Log.e(TAG, "Could not get volume for " + file); + logInvalidScopedAccessRequest(activity, SCOPED_DIRECTORY_ACCESS_ERROR); return false; } // Gets the package label. final String appLabel = getAppLabel(activity); if (appLabel == null) { + // Error already logged. return false; } @@ -198,6 +216,7 @@ public class OpenExternalDirectoryActivity extends Activity { try { return pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0)).toString(); } catch (NameNotFoundException e) { + logInvalidScopedAccessRequest(activity, SCOPED_DIRECTORY_ACCESS_ERROR); Log.w(TAG, "Could not get label for package " + packageName); return null; } @@ -217,18 +236,21 @@ public class OpenExternalDirectoryActivity extends Activity { return volume.isVisibleForWrite(userId) && root.equals(path); } - private static Uri getGrantedUriPermission(ContentProviderClient provider, File file) { + private static Uri getGrantedUriPermission(Context context, ContentProviderClient provider, + File file) { // Calls ExternalStorageProvider to get the doc id for the file final Bundle bundle; try { bundle = provider.call("getDocIdForFileCreateNewDir", file.getPath(), null); } catch (RemoteException e) { Log.e(TAG, "Did not get doc id from External Storage provider for " + file, e); + logInvalidScopedAccessRequest(context, SCOPED_DIRECTORY_ACCESS_ERROR); return null; } final String docId = bundle == null ? null : bundle.getString("DOC_ID"); if (docId == null) { Log.e(TAG, "Did not get doc id from External Storage provider for " + file); + logInvalidScopedAccessRequest(context, SCOPED_DIRECTORY_ACCESS_ERROR); return null; } Log.d(TAG, "doc id for " + file + ": " + docId); @@ -242,9 +264,9 @@ public class OpenExternalDirectoryActivity extends Activity { return uri; } - private static Intent createGrantedUriPermissionsIntent(ContentProviderClient provider, - File file) { - final Uri uri = getGrantedUriPermission(provider, file); + private static Intent createGrantedUriPermissionsIntent(Context context, + ContentProviderClient provider, File file) { + final Uri uri = getGrantedUriPermission(context, provider, file); return createGrantedUriPermissionsIntent(uri); } @@ -261,7 +283,8 @@ public class OpenExternalDirectoryActivity extends Activity { private static Intent getIntentForExistingPermission(OpenExternalDirectoryActivity activity, File file) { final String packageName = activity.getCallingPackage(); - final Uri grantedUri = getGrantedUriPermission(activity.getExternalStorageClient(), file); + final Uri grantedUri = + getGrantedUriPermission(activity, activity.getExternalStorageClient(), file); if (DEBUG) Log.d(TAG, "checking if " + packageName + " already has permission for " + grantedUri); final ActivityManager am = @@ -298,7 +321,7 @@ public class OpenExternalDirectoryActivity extends Activity { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - final String folder = mFile.getName(); + final String directory = mFile.getName(); final Activity activity = getActivity(); final OnClickListener listener = new OnClickListener() { @@ -306,12 +329,16 @@ public class OpenExternalDirectoryActivity extends Activity { public void onClick(DialogInterface dialog, int which) { Intent intent = null; if (which == DialogInterface.BUTTON_POSITIVE) { - intent = createGrantedUriPermissionsIntent( + intent = createGrantedUriPermissionsIntent(mActivity, mActivity.getExternalStorageClient(), mFile); } if (which == DialogInterface.BUTTON_NEGATIVE || intent == null) { + logValidScopedAccessRequest(activity, directory, + SCOPED_DIRECTORY_ACCESS_DENIED); activity.setResult(RESULT_CANCELED); } else { + logValidScopedAccessRequest(activity, directory, + SCOPED_DIRECTORY_ACCESS_GRANTED); activity.setResult(RESULT_OK, intent); } activity.finish(); @@ -320,7 +347,7 @@ public class OpenExternalDirectoryActivity extends Activity { final CharSequence message = TextUtils .expandTemplate( - getText(R.string.open_external_dialog_request), mAppLabel, folder, + getText(R.string.open_external_dialog_request), mAppLabel, directory, mVolumeLabel); return new AlertDialog.Builder(activity, R.style.AlertDialogTheme) .setMessage(message) @@ -333,6 +360,7 @@ public class OpenExternalDirectoryActivity extends Activity { public void onCancel(DialogInterface dialog) { super.onCancel(dialog); final Activity activity = getActivity(); + logValidScopedAccessRequest(activity, mFile.getName(), SCOPED_DIRECTORY_ACCESS_DENIED); activity.setResult(RESULT_CANCELED); activity.finish(); } diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 471feef15e6c..ec09b13a0563 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -478,5 +478,30 @@ message MetricsEvent { // Logged when we execute an app transition. This indicates the device uptime in seconds when // the transition was executed. APP_TRANSITION_DEVICE_UPTIME_SECONDS = 325; + + // User granted access to the request folder; action takes an integer + // representing the folder's index on Environment.STANDARD_DIRECTORIES + ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_FOLDER = 326; + + // User denied access to the request folder; action takes an integer + // representing the folder's index on Environment.STANDARD_DIRECTORIES + ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_FOLDER = 327; + + // User granted access to the request folder; action pass package name + // of calling package. + ACTION_SCOPED_DIRECTORY_ACCESS_GRANTED_BY_PACKAGE = 328; + + // User denied access to the request folder; action pass package name + // of calling package. + ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_BY_PACKAGE = 329; + + // App requested access to a directory it has already been granted + // access before; action takes an integer representing the folder's + // index on Environment.STANDARD_DIRECTORIES + ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_FOLDER = 330; + + // App requested access to a directory it has already been granted + // access before; action pass package name of calling package. + ACTION_SCOPED_DIRECTORY_ACCESS_ALREADY_GRANTED_BY_PACKAGE = 331; } } -- 2.11.0