Chromium Code Reviews| Index: chrome/android/java/src/org/chromium/chrome/browser/BlockingFileProvider.java |
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BlockingFileProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/BlockingFileProvider.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..39dcad0dff03af2ea40a5e5cb8734bd7eebad14f |
| --- /dev/null |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/BlockingFileProvider.java |
| @@ -0,0 +1,130 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package org.chromium.chrome.browser; |
| + |
| +import android.app.Activity; |
| +import android.content.UriMatcher; |
| +import android.database.Cursor; |
| +import android.net.Uri; |
| +import android.os.ParcelFileDescriptor; |
| +import android.support.v4.content.FileProvider; |
| + |
| +import java.io.FileNotFoundException; |
| + |
| +/** |
| + * A file provider class that shares a potentially non-existent file and blocks the client from |
| + * accessing till the file is written. |
| + * |
| + * This class gives an unique identifier for the file to be shared and the user must write the file |
|
nyquist
2016/07/14 17:36:32
Could you clean up the usage of pronouns in this p
ssid
2016/07/19 18:32:57
Thanks, used these names.
|
| + * and notify that the file is ready. The client is blocked from accessing the file till the file is |
| + * ready. This lets us create the share intent immediately after the user clicks on share and the |
| + * actual file will be usually ready when it is requested by the client application. Only one file |
| + * can be shared at a given time. |
|
nyquist
2016/07/14 17:36:31
What happens if multiple files are shared? Is this
ssid
2016/07/19 18:32:57
It is the constraint of this implementation and cu
|
| + */ |
| +public class BlockingFileProvider extends FileProvider { |
|
nyquist
2016/07/14 17:36:32
Could you add a test for this class?
ssid
2016/07/19 18:32:57
Done.
|
| + private static final String AUTHORITY_SUFFIX = ".BlockingFileProvider"; |
| + |
| + /** |
| + * Returns an unique uri to identify the file to be shared. |
| + * |
| + * @param activity Activity that is used to access package manager. |
| + * @param fileName An unique file name that represents the file to be created. |
| + */ |
| + public static Uri getContentUriForFile(final Activity activity, String fileName) { |
| + Uri uri = null; |
|
nyquist
2016/07/14 17:36:32
Nit: Could you just initialize this while building
ssid
2016/07/19 18:32:57
Done.
|
| + ensureUriMatcherInitialized(activity.getPackageName()); |
| + uri = new Uri.Builder().scheme("content").authority(sAuthority).path(fileName).build(); |
| + synchronized (sLock) { |
| + sCurrentFileName = fileName; |
| + sFileUri = null; |
| + sIsFileReady = false; |
| + // In case the previous file never got ready. |
| + sLock.notify(); |
| + } |
| + return uri; |
| + } |
| + |
| + /** |
| + * Notify that the file is ready to be accessed by the resolver. |
| + * |
| + * @param fileName The unique file name that was passed to getContentUriForFile. |
| + * @param fileUri The Uri for actual file given by FileProvider. |
| + */ |
| + public static void notifyFileReady(String fileName, Uri fileUri) { |
| + synchronized (sLock) { |
| + sFileUri = fileUri; |
| + // Ready is set only if the current file is ready. |
| + sIsFileReady = sCurrentFileName.equals(fileName); |
| + sLock.notify(); |
| + } |
| + } |
| + |
| + @Override |
| + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { |
| + Uri fileUri = getFileUriWhenReady(uri); |
| + return fileUri != null ? super.openFile(fileUri, mode) : null; |
| + } |
| + |
| + @Override |
| + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, |
| + String sortOrder) { |
| + Uri fileUri = getFileUriWhenReady(uri); |
| + return fileUri != null |
| + ? super.query(fileUri, projection, selection, selectionArgs, sortOrder) |
| + : null; |
| + } |
| + |
| + @Override |
| + public int delete(Uri uri, String selection, String[] selectionArgs) { |
| + synchronized (sLock) { |
| + sFileUri = null; |
| + sIsFileReady = false; |
| + sCurrentFileName = null; |
| + } |
| + return super.delete(uri, selection, selectionArgs); |
| + } |
| + |
| + // Matcher to tell the content resolver the uri patterns that can be handled. |
| + private static UriMatcher sUriMatcher = null; |
|
nyquist
2016/07/14 17:36:31
Where is this used?
ssid
2016/07/19 18:32:57
I am not really sure. I I thought this was used by
|
| + private static String sAuthority = null; |
| + private static Object sLock = new Object(); |
| + |
| + // All these static objects must be accesseed in a synchronized block: |
| + private static boolean sIsFileReady = false; |
| + private static String sCurrentFileName = null; |
| + private static Uri sFileUri = null; |
| + |
| + /** |
| + * Initializes the matcher for content resolver. |
| + */ |
| + private static void ensureUriMatcherInitialized(String packageName) { |
| + if (sUriMatcher != null) return; |
|
nyquist
2016/07/14 17:36:32
I don't think this is threadsafe, and it can be ca
ssid
2016/07/19 18:32:57
Acknowledged.
|
| + |
| + sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); |
| + sAuthority = packageName + AUTHORITY_SUFFIX; |
| + sUriMatcher.addURI(sAuthority, "*", 0); |
| + } |
| + |
| + /** |
| + * Waits and returns file uri iff the file is ready to be accessed, or returns null if file is |
| + * replaced. |
| + */ |
| + private Uri getFileUriWhenReady(Uri uri) { |
| + String fileName = uri.getPath(); |
| + synchronized (sLock) { |
| + // Wait only if the file is not ready and the current file has not changed. |
| + while (!sIsFileReady && fileName.contains(sCurrentFileName)) { |
|
nyquist
2016/07/14 17:36:32
Should this be "endsWith" instead of "contains"?
ssid
2016/07/19 18:32:57
Done.
|
| + try { |
| + sLock.wait(); |
| + } catch (InterruptedException e) { |
| + break; |
| + } |
| + } |
| + // If the current file has changed while waiting, return null. |
| + if (fileName.contains(sCurrentFileName)) return sFileUri; |
| + } |
| + return null; |
| + } |
| +} |