Merge "Adding ContentProvider#refresh and ContentResolver#refresh."
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user