@TargetApi(Build.VERSION_CODES.KITKAT) private Uri getRootUri(String docId){ Uri treeUri; final int splitIndex = docId.indexOf(':', 1); final String tag = docId.substring(0, splitIndex); //check in cache treeUri = secondaryRoots.get(tag); if(null != treeUri){ return treeUri; } //get root dynamically List<UriPermission> permissions = mContext.getContentResolver().getPersistedUriPermissions(); for (UriPermission permission : permissions) { String treeRootId = getRootUri(permission.getUri()); if(docId.startsWith(treeRootId)){ treeUri = permission.getUri(); secondaryRoots.put(tag, treeUri); return treeUri; } } return treeUri; }
@TargetApi(Build.VERSION_CODES.LOLLIPOP) public static boolean checkPersistUriPermissionsAndUpdate(Context context, DocumentFolder documentFolder) { Uri uri = documentFolder.getFolder().getUri(); List<UriPermission> permissions = context.getContentResolver().getPersistedUriPermissions(); for (UriPermission permission : permissions) { String permissionTreeId = DocumentsContract.getTreeDocumentId(permission.getUri()); String uriTreeId = DocumentsContract.getTreeDocumentId(uri); if (uriTreeId.startsWith(permissionTreeId)) { // update permissions - after a restart this is necessary... updatePersistUriPermissions(context, uri); documentFolder.updateDocument(DocumentFile.fromTreeUri(context, permission.getUri())); return true; } } return false; }
@TargetApi(Build.VERSION_CODES.LOLLIPOP) public static boolean checkPersistUriPermissionsAndUpdate(Context context, StorageDocument storageDocument) { Uri uri = storageDocument.getUri(); List<UriPermission> permissions = context.getContentResolver().getPersistedUriPermissions(); for (UriPermission permission : permissions) { String permissionTreeId = DocumentsContract.getTreeDocumentId(permission.getUri()); String uriTreeId = DocumentsContract.getTreeDocumentId(uri); if (uriTreeId.startsWith(permissionTreeId)) { // update permissions - after a restart this is necessary... updatePersistUriPermissions(context, uri); storageDocument.setWrapped(DocumentFile.fromTreeUri(context, permission.getUri())); return true; } } return false; }
/** * request_codeに対応するUriへアクセス可能かどうか * @param context * @param request_code * @return */ @TargetApi(Build.VERSION_CODES.KITKAT) public static boolean hasStorageAccess(@NonNull final Context context, final int request_code) { boolean found = false; if (BuildCheck.isLollipop()) { final Uri uri = loadUri(context, getKey(request_code)); if (uri != null) { // 恒常的に保持しているUriパーミッションの一覧を取得する final List<UriPermission> list = context.getContentResolver().getPersistedUriPermissions(); for (final UriPermission item: list) { if (item.getUri().equals(uri)) { // request_codeに対応するUriへのパーミッションを恒常的に保持していた時 found = true; break; } } } } return found; }
protected void revokePermission() { // TODO: This does not stop writing once permission was granted...needs more testing might be an android bug for (UriPermission p : getContentResolver().getPersistedUriPermissions()) { getContentResolver().releasePersistableUriPermission(p.getUri(), Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } if (getContentResolver().getPersistedUriPermissions().size() == 0) { Snackbar.make(findViewById(android.R.id.content), R.string.revokeSuccess, Snackbar.LENGTH_SHORT).show(); } else { Snackbar.make(findViewById(android.R.id.content), R.string.revokeFail, Snackbar.LENGTH_SHORT).show(); } }
@TargetApi(Build.VERSION_CODES.LOLLIPOP) public static String getDocumentTree() { String treeUri = PreferenceUtil.getInstance(RetroApplication.getInstance()).getDocumentTreeUri(); List<UriPermission> perms = RetroApplication.getInstance().getContentResolver().getPersistedUriPermissions(); for (UriPermission perm : perms) { if (perm.getUri().toString().equals(treeUri) && perm.isWritePermission()) return treeUri; } return null; }
@TargetApi(Build.VERSION_CODES.LOLLIPOP) public static boolean isSDCardAccessGranted(Context context) { if (!isTreeUriSaved(context)) return false; String sdcardUri = PreferenceUtil.getInstance(context).getSAFSDCardUri(); List<UriPermission> perms = context.getContentResolver().getPersistedUriPermissions(); for (UriPermission perm : perms) { if (perm.getUri().toString().equals(sdcardUri) && perm.isWritePermission()) return true; } return false; }
/** * request_codeに対応するUriが存在していて恒常的パーミッションがあればそれを返す, なければnullを返す * @param context * @param request_code * @return */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Nullable public static Uri getStorageUri(@NonNull final Context context, final int request_code) { if (BuildCheck.isLollipop()) { final Uri uri = loadUri(context, getKey(request_code)); if (uri != null) { boolean found = false; // 恒常的に保持しているUriパーミッションの一覧を取得する final List<UriPermission> list = context.getContentResolver().getPersistedUriPermissions(); for (final UriPermission item: list) { if (item.getUri().equals(uri)) { // request_codeに対応するUriへのパーミッションを恒常的に保持していた時 found = true; break; } } if (found) { return uri; } } } return null; }
/** * Releases all persisted URI permissions that are no longer referenced */ void releaseGarbagePermissions() { ContentResolver contentResolver = context.getContentResolver(); for (UriPermission permission : contentResolver.getPersistedUriPermissions()) { if (isGarbage(permission.getUri())) { Log.i(TAG, "releaseGarbagePermissions: Releasing permission for " + permission.getUri()); contentResolver.releasePersistableUriPermission(permission.getUri(), Intent.FLAG_GRANT_READ_URI_PERMISSION); } else { Log.v(TAG, "releaseGarbagePermissions: Keeping permission for " + permission.getUri()); } } }
/** * Returns the permissible root uri if one exists, null if not. * * @return The tree URI. */ public Uri getPermissibleRoot(Uri uri) { if (uri == null) return null; // Files can't be correlated to UriPermissions so rely on canWrite? if (FileUtil.isFileScheme(uri)) { File f = new File(uri.getPath()); while (f != null && !f.canWrite()) { if (f.canWrite()) return Uri.fromFile(f); f = f.getParentFile(); } return null; } else { for (UriPermission permission : mRootPermissions) { String permissionTreeId = DocumentsContract.getTreeDocumentId(permission.getUri()); String uriTreeId = DocumentsContract.getTreeDocumentId(uri); if (uriTreeId.startsWith(permissionTreeId)) { return permission.getUri(); } } } return null; }
private Uri findGrantedUri(final String mountPoint) { final List<UriPermission> list = mContext.getContentResolver().getPersistedUriPermissions(); final Iterator<UriPermission> iterator = list.iterator(); while (iterator.hasNext()) { final Uri uri = checkPermission(iterator.next(), mountPoint); if (uri == null) { continue; } return uri; } return null; }
@RequiresApi(api = KITKAT) private void revokePermission() { for (UriPermission p : getContentResolver().getPersistedUriPermissions()) { getContentResolver().releasePersistableUriPermission(p.getUri(), Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } if (getContentResolver().getPersistedUriPermissions().size() == 0) { LogHelper.d(TAG, "Permissions revoked successfully."); } else { LogHelper.d(TAG, "Permissions failed to be revoked."); } }
/** * We can't just simply delete the rows as that won't free up the space occupied by the * chosen image files for each row being deleted. Instead we have to query * and manually delete each chosen image file */ private void deleteBackingPhotos(Context context, List<ChosenPhoto> chosenPhotos) { for (ChosenPhoto chosenPhoto : chosenPhotos) { File file = GalleryProvider.getCacheFileForUri(context, chosenPhoto.uri); if (file != null && file.exists()) { if (!file.delete()) { Log.w(TAG, "Unable to delete " + file); } } else { Uri uriToRelease = chosenPhoto.uri; ContentResolver contentResolver = context.getContentResolver(); boolean haveUriPermission = context.checkUriPermission(uriToRelease, Binder.getCallingPid(), Binder.getCallingUid(), Intent.FLAG_GRANT_READ_URI_PERMISSION) == PackageManager.PERMISSION_GRANTED; if (haveUriPermission) { // Try to release any persisted URI permission for the imageUri List<UriPermission> persistedUriPermissions = contentResolver.getPersistedUriPermissions(); for (UriPermission persistedUriPermission : persistedUriPermissions) { if (persistedUriPermission.getUri().equals(uriToRelease)) { contentResolver.releasePersistableUriPermission( uriToRelease, Intent.FLAG_GRANT_READ_URI_PERMISSION); break; } } } else { // On API 25 and lower, we don't get URI permissions to URIs // from our own package so we manage those URI permissions manually try { contentResolver.call(uriToRelease, "releasePersistableUriPermission", uriToRelease.toString(), null); } catch (Exception e) { Log.w(TAG, "Unable to manually release uri permissions to " + chosenPhoto.uri, e); } } } } }
@Override @RequiresApi(KITKAT) @NonNull public List<UriPermission> getPersistedUriPermissions() { return mBase.getPersistedUriPermissions(); }
@Override @RequiresApi(KITKAT) @NonNull public List<UriPermission> getOutgoingPersistedUriPermissions() { return mBase.getOutgoingPersistedUriPermissions(); }
@RequiresApi(api = Build.VERSION_CODES.KITKAT) public static boolean hasPermission() { List<UriPermission> uriPermission = Common.getInstance().getContentResolver().getPersistedUriPermissions(); return uriPermission != null && uriPermission.size() > 0; }
@Test public void testReleaseGarbagePermissions() throws Exception { Context mockContext = mock(Context.class); ContentResolver mockResolver = mock(ContentResolver.class); when(mockContext.getContentResolver()).thenReturn(mockResolver); final List<UriPermission> persistedPermissions = new LinkedList<>(); when(mockResolver.getPersistedUriPermissions()).thenReturn(persistedPermissions); UriPermission usedPermission = mock(UriPermission.class); when(usedPermission.getUri()).thenReturn(newUri("content://used")); persistedPermissions.add(usedPermission); UriPermission garbagePermission = mock(UriPermission.class); when(garbagePermission.getUri()).thenReturn(newUri("content://garbage")); persistedPermissions.add(garbagePermission); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { Iterator<UriPermission> iter = persistedPermissions.iterator(); while (iter.hasNext()) { UriPermission perm = iter.next(); if (perm.getUri() == invocation.getArgumentAt(0, Uri.class)) iter.remove(); } return null; } }).when(mockResolver, "releasePersistableUriPermission", any(Uri.class), anyInt()); Configuration configuration = new Configuration(); configuration.hosts.items.add(newItemForLocation("content://used")); assertTrue(persistedPermissions.contains(usedPermission)); assertTrue(persistedPermissions.contains(garbagePermission)); new RuleDatabaseUpdateTask(mockContext, configuration, false).releaseGarbagePermissions(); assertTrue(persistedPermissions.contains(usedPermission)); assertFalse(persistedPermissions.contains(garbagePermission)); }
public List<UriPermission> getRootPermissions() { return Collections.unmodifiableList(mRootPermissions); }
private Uri checkPermission(final UriPermission uriPermission, final String string) { if (!uriPermission.isWritePermission()) { return null; } return checkUri(uriPermission.getUri(), string); }
@TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == C.REQUEST_CODE_OPEN_PATH && resultCode == Activity.RESULT_OK) { boolean success = false; Uri uri = data.getData(); ContentResolver contentResolver = getActivity().getContentResolver(); for (UriPermission uriPermission : contentResolver.getPersistedUriPermissions()) { if (!uri.equals(uriPermission.getUri())) { contentResolver.releasePersistableUriPermission(uriPermission.getUri(), (uriPermission.isReadPermission() ? Intent.FLAG_GRANT_READ_URI_PERMISSION : 0) | (uriPermission.isWritePermission() ? Intent.FLAG_GRANT_WRITE_URI_PERMISSION : 0)); } } contentResolver.takePersistableUriPermission(uri, data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)); String id = DocumentsContract.getTreeDocumentId(uri); if (id != null) { String[] splitted = id.split(":", -1); if (splitted.length == 2) { String volumeName = splitted[0]; String path = splitted[1]; File storageDirectory = null; if ("primary".equals(volumeName)) { storageDirectory = Environment.getExternalStorageDirectory(); } else { StorageManager storageManager = (StorageManager) getActivity() .getSystemService(Context.STORAGE_SERVICE); try { Object[] list = (Object[]) StorageManager.class.getMethod("getVolumeList") .invoke(storageManager); if (list != null) { for (Object volume : list) { Class<?> volumeClass = volume.getClass(); String uuid = (String) volumeClass.getMethod("getUuid").invoke(volume); if (volumeName.equals(uuid)) { storageDirectory = (File) volumeClass.getMethod("getPathFile").invoke(volume); break; } } } } catch (Exception e) { // Reflective operation, ignore exception } } if (storageDirectory != null) { if (storageDirectory.equals(Environment.getExternalStorageDirectory())) { path = "/" + path; } else { path = new File(storageDirectory, path).getAbsolutePath(); } if (!path.endsWith("/")) { path += "/"; } downloadPathPreference.getEditText().setText(path); success = true; } } } if (!success) { ToastUtils.show(getActivity(), R.string.message_unknown_error); } } }