Merge "Adding ContentProvider#refresh and ContentResolver#refresh."

This commit is contained in:
Ben Lin
2016-11-11 19:13:35 +00:00
committed by Android (Google) Code Review
8 changed files with 193 additions and 0 deletions

View File

@@ -42,6 +42,7 @@ import android.os.ICancellationSignal;
import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -463,6 +464,23 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
}
@Override
public boolean refresh(String callingPkg, Uri uri, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return false;
}
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.refresh(uri, args,
CancellationSignal.fromTransport(cancellationSignal));
} finally {
setCallingPackage(original);
}
}
private void enforceFilePermission(String callingPkg, Uri uri, String mode,
IBinder callerToken) throws FileNotFoundException, SecurityException {
if (mode != null && mode.indexOf('w') != -1) {
@@ -1092,6 +1110,34 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
return url;
}
/**
* Implement this to support refresh of content identified by {@code uri}. By default, this
* method returns false; providers who wish to implement this should return true to signal the
* client that the provider has tried refreshing with its own implementation.
* <p>
* This allows clients to request an explicit refresh of content identified by {@code uri}.
* <p>
* Client code should only invoke this method when there is a strong indication (such as a user
* initiated pull to refresh gesture) that the content is stale.
* <p>
* Remember to send {@link ContentResolver#notifyChange(Uri, android.database.ContentObserver)}
* notifications when content changes.
*
* @param uri The Uri identifying the data to refresh.
* @param args Additional options from the client. The definitions of these are specific to the
* content provider being called.
* @param cancellationSignal A signal to cancel the operation in progress, or {@code null} if
* none. For example, if you called refresh on a particular uri, you should call
* {@link CancellationSignal#throwIfCanceled()} to check whether the client has
* canceled the refresh request.
* @return true if the provider actually tried refreshing.
* @hide
*/
public boolean refresh(Uri uri, @Nullable Bundle args,
@Nullable CancellationSignal cancellationSignal) {
return false;
}
/**
* @hide
* Implementation when a caller has performed an insert on the content

View File

@@ -234,6 +234,30 @@ public class ContentProviderClient implements AutoCloseable {
}
}
/** @hide */
public boolean refresh(Uri url, @Nullable Bundle args,
@Nullable CancellationSignal cancellationSignal) throws RemoteException {
Preconditions.checkNotNull(url, "url");
beforeRemote();
try {
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
remoteCancellationSignal = mContentProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
return mContentProvider.refresh(mPackageName, url, args, remoteCancellationSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
} finally {
afterRemote();
}
}
/** See {@link ContentProvider#insert ContentProvider.insert} */
public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues)
throws RemoteException {

View File

@@ -355,6 +355,20 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
Uri.writeToParcel(reply, out);
return true;
}
case REFRESH_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
Bundle args = data.readBundle();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
boolean out = refresh(callingPkg, url, args, signal);
reply.writeNoException();
reply.writeInt(out ? 0 : -1);
return true;
}
}
} catch (Exception e) {
DatabaseUtils.writeExceptionToParcel(reply, e);
@@ -761,5 +775,28 @@ final class ContentProviderProxy implements IContentProvider
}
}
public boolean refresh(String callingPkg, Uri url, Bundle args, ICancellationSignal signal)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
url.writeToParcel(data, 0);
data.writeBundle(args);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
mRemote.transact(IContentProvider.REFRESH_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
int success = reply.readInt();
return (success == 0);
} finally {
data.recycle();
reply.recycle();
}
}
private IBinder mRemote;
}

View File

@@ -193,6 +193,15 @@ public abstract class ContentResolver {
*/
public static final String EXTRA_SIZE = "android.content.extra.SIZE";
/**
* An extra boolean describing whether a particular provider supports refresh
* or not. If a provider supports refresh, it should include this key in its
* returned Cursor as part of its query call.
*
* @hide
*/
public static final String EXTRA_REFRESH_SUPPORTED = "android.content.extra.REFRESH_SUPPORTED";
/**
* This is the Android platform's base MIME type for a content: URI
* containing a Cursor of a single item. Applications should use this
@@ -663,6 +672,54 @@ public abstract class ContentResolver {
}
}
/**
* Implement this to support refresh of content identified by {@code uri}. By default, this
* method returns false; providers who wish to implement this should return true to signal the
* client that the provider has tried refreshing with its own implementation.
* <p>
* This allows clients to request an explicit refresh of content identified by {@code uri}.
* <p>
* Client code should only invoke this method when there is a strong indication (such as a user
* initiated pull to refresh gesture) that the content is stale.
* <p>
* Remember to send {@link ContentResolver#notifyChange(Uri, android.database.ContentObserver)}
* notifications when content changes.
*
* @param uri The Uri identifying the data to refresh.
* @param args Additional options from the client. The definitions of these are specific to the
* content provider being called.
* @param cancellationSignal A signal to cancel the operation in progress, or {@code null} if
* none. For example, if you called refresh on a particular uri, you should call
* {@link CancellationSignal#throwIfCanceled()} to check whether the client has
* canceled the refresh request.
* @return true if the provider actually tried refreshing.
* @hide
*/
public final boolean refresh(@NonNull Uri url, @Nullable Bundle args,
@Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(url, "url");
IContentProvider provider = acquireProvider(url);
if (provider == null) {
return false;
}
try {
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
remoteCancellationSignal = provider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
return provider.refresh(mPackageName, url, args, remoteCancellationSignal);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return false;
} finally {
releaseProvider(provider);
}
}
/**
* Open a stream on to the content associated with a content URI. If there
* is no data associated with the URI, FileNotFoundException is thrown.

View File

@@ -65,6 +65,9 @@ public interface IContentProvider extends IInterface {
public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException;
public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException;
public boolean refresh(String callingPkg, Uri url, @Nullable Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException;
// Data interchange.
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType,
@@ -88,4 +91,5 @@ public interface IContentProvider extends IInterface {
static final int CREATE_CANCELATION_SIGNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 23;
static final int CANONICALIZE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 24;
static final int UNCANONICALIZE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 25;
static final int REFRESH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 26;
}

View File

@@ -147,6 +147,12 @@ public class MockContentProvider extends ContentProvider {
public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException {
return MockContentProvider.this.uncanonicalize(uri);
}
@Override
public boolean refresh(String callingPkg, Uri url, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
return MockContentProvider.this.refresh(url, args);
}
}
private final InversionIContentProvider mIContentProvider = new InversionIContentProvider();
@@ -250,6 +256,13 @@ public class MockContentProvider extends ContentProvider {
throw new UnsupportedOperationException("unimplemented mock method call");
}
/**
* @hide
*/
public boolean refresh(Uri url, Bundle args) {
throw new UnsupportedOperationException("unimplemented mock method call");
}
/**
* Returns IContentProvider which calls back same methods in this class.
* By overriding this class, we avoid the mechanism hidden behind ContentProvider

View File

@@ -125,4 +125,10 @@ public class MockIContentProvider implements IContentProvider {
public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
public boolean refresh(String callingPkg, Uri url, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
}

View File

@@ -145,4 +145,10 @@ public final class BridgeContentProvider implements IContentProvider {
public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException {
return null;
}
@Override
public boolean refresh(String callingPkg, Uri url, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
return false;
}
}