import android.content.Context;
import android.content.Intent;
import android.content.UriMatcher;
+import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Point;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract.Document;
import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+
import libcore.io.IoUtils;
import java.io.FileNotFoundException;
@Override
public final Bundle callFromPackage(
String callingPackage, String method, String arg, Bundle extras) {
+ final Context context = getContext();
+
if (!method.startsWith("android:")) {
// Let non-platform methods pass through
return super.callFromPackage(callingPackage, method, arg, extras);
}
- // Require that caller can manage given document
final String documentId = extras.getString(Document.COLUMN_DOCUMENT_ID);
final Uri documentUri = DocumentsContract.buildDocumentUri(mAuthority, documentId);
- getContext().enforceCallingOrSelfUriPermission(
- documentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, method);
+
+ // Require that caller can manage given document
+ final boolean callerHasManage =
+ context.checkCallingOrSelfPermission(android.Manifest.permission.MANAGE_DOCUMENTS)
+ == PackageManager.PERMISSION_GRANTED;
+ if (!callerHasManage) {
+ getContext().enforceCallingOrSelfUriPermission(
+ documentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, method);
+ }
final Bundle out = new Bundle();
try {
final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
- // TODO: issue Uri grant towards calling package
- // TODO: enforce that package belongs to caller
final String newDocumentId = createDocument(documentId, mimeType, displayName);
out.putString(Document.COLUMN_DOCUMENT_ID, newDocumentId);
+ // Extend permission grant towards caller if needed
+ if (!callerHasManage) {
+ final Uri newDocumentUri = DocumentsContract.buildDocumentUri(
+ mAuthority, newDocumentId);
+ context.grantUriPermission(callingPackage, newDocumentUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
+ }
+
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
- final String docId = extras.getString(Document.COLUMN_DOCUMENT_ID);
- deleteDocument(docId);
+ deleteDocument(documentId);
+
+ // Document no longer exists, clean up any grants
+ context.revokeUriPermission(documentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
} else {
throw new UnsupportedOperationException("Method not supported " + method);
import libcore.io.Streams;
import java.io.InputStream;
+import java.io.OutputStream;
public class TestActivity extends Activity {
private static final String TAG = "TestActivity";
+ private static final int CODE_READ = 42;
+ private static final int CODE_WRITE = 43;
+
private TextView mResult;
@Override
view.setOrientation(LinearLayout.VERTICAL);
final CheckBox multiple = new CheckBox(context);
- multiple.setText("\nALLOW_MULTIPLE\n");
+ multiple.setText("ALLOW_MULTIPLE");
view.addView(multiple);
final CheckBox localOnly = new CheckBox(context);
- localOnly.setText("\nLOCAL_ONLY\n");
+ localOnly.setText("LOCAL_ONLY");
view.addView(localOnly);
Button button;
if (localOnly.isChecked()) {
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
}
- startActivityForResult(intent, 42);
+ startActivityForResult(intent, CODE_READ);
}
});
view.addView(button);
if (localOnly.isChecked()) {
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
}
- startActivityForResult(intent, 42);
+ startActivityForResult(intent, CODE_READ);
}
});
view.addView(button);
if (localOnly.isChecked()) {
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
}
- startActivityForResult(intent, 42);
+ startActivityForResult(intent, CODE_READ);
}
});
view.addView(button);
if (localOnly.isChecked()) {
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
}
- startActivityForResult(intent, 42);
+ startActivityForResult(intent, CODE_READ);
}
});
view.addView(button);
if (localOnly.isChecked()) {
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
}
- startActivityForResult(intent, 42);
+ startActivityForResult(intent, CODE_WRITE);
}
});
view.addView(button);
if (localOnly.isChecked()) {
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
}
- startActivityForResult(Intent.createChooser(intent, "Kittens!"), 42);
+ startActivityForResult(Intent.createChooser(intent, "Kittens!"), CODE_READ);
}
});
view.addView(button);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- mResult.setText("resultCode=" + resultCode + ", data=" + String.valueOf(data));
-
- final Uri uri = data != null ? data.getData() : null;
- if (uri != null) {
- InputStream is = null;
- try {
- is = getContentResolver().openInputStream(uri);
- final int length = Streams.readFullyNoClose(is).length;
- Log.d(TAG, "read length=" + length);
- } catch (Exception e) {
- Log.w(TAG, "Failed to read " + uri, e);
- } finally {
- IoUtils.closeQuietly(is);
+ mResult.setText(null);
+ String result = "resultCode=" + resultCode + ", data=" + String.valueOf(data);
+
+ if (requestCode == CODE_READ) {
+ final Uri uri = data != null ? data.getData() : null;
+ if (uri != null) {
+ InputStream is = null;
+ try {
+ is = getContentResolver().openInputStream(uri);
+ final int length = Streams.readFullyNoClose(is).length;
+ result += "; read length=" + length;
+ } catch (Exception e) {
+ result += "; ERROR";
+ Log.w(TAG, "Failed to read " + uri, e);
+ } finally {
+ IoUtils.closeQuietly(is);
+ }
+ } else {
+ result += "no uri?";
+ }
+ } else if (requestCode == CODE_WRITE) {
+ final Uri uri = data != null ? data.getData() : null;
+ if (uri != null) {
+ OutputStream os = null;
+ try {
+ os = getContentResolver().openOutputStream(uri);
+ os.write("THE COMPLETE WORKS OF SHAKESPEARE".getBytes());
+ } catch (Exception e) {
+ result += "; ERROR";
+ Log.w(TAG, "Failed to write " + uri, e);
+ } finally {
+ IoUtils.closeQuietly(os);
+ }
+ } else {
+ result += "no uri?";
}
}
+
+ Log.d(TAG, result);
+ mResult.setText(result);
}
}