diff options
author | Jeff Sharkey <jsharkey@google.com> | 2019-11-14 03:48:02 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-11-14 03:48:02 +0000 |
commit | 03263747d85e9237c2a729f54ae14856dafb4b9d (patch) | |
tree | 8b4a6f11a2906120452f77acfa320f495eda4ee7 | |
parent | 532431d1b2863b1650892ab4d99a4d736ef71830 (diff) | |
parent | 1307f428af43c0e40a909da0320ae61ba04d58d5 (diff) |
Merge "Allow sending of bulk Uri change notifications."
-rw-r--r-- | api/current.txt | 3 | ||||
-rw-r--r-- | core/java/android/content/ContentResolver.java | 93 | ||||
-rw-r--r-- | core/java/android/content/IContentService.aidl | 2 | ||||
-rw-r--r-- | services/core/java/com/android/server/content/ContentService.java | 9 | ||||
-rw-r--r-- | test-mock/src/android/test/mock/MockContentResolver.java | 48 |
5 files changed, 124 insertions, 31 deletions
diff --git a/api/current.txt b/api/current.txt index d0a1bcdd269e..0ddc42a38122 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9650,8 +9650,9 @@ package android.content { method public static boolean isSyncPending(android.accounts.Account, String); method @NonNull public android.graphics.Bitmap loadThumbnail(@NonNull android.net.Uri, @NonNull android.util.Size, @Nullable android.os.CancellationSignal) throws java.io.IOException; method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver); - method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, boolean); + method @Deprecated public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, boolean); method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, int); + method public void notifyChange(@NonNull Iterable<android.net.Uri>, @Nullable android.database.ContentObserver, int); method @Nullable public final android.content.res.AssetFileDescriptor openAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException; method @Nullable public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String) throws java.io.FileNotFoundException; method @Nullable public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException; diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 2657cc54b91a..61c8db5db124 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -65,6 +65,7 @@ import android.text.TextUtils; import android.util.EventLog; import android.util.Log; import android.util.Size; +import android.util.SparseArray; import com.android.internal.util.MimeIconUtils; import com.android.internal.util.Preconditions; @@ -2381,15 +2382,15 @@ public abstract class ContentResolver implements ContentInterface { * true. * @param syncToNetwork If true, same as {@link #NOTIFY_SYNC_TO_NETWORK}. * @see #requestSync(android.accounts.Account, String, android.os.Bundle) + * @deprecated callers should consider migrating to + * {@link #notifyChange(Uri, ContentObserver, int)}, as it + * offers support for many more options than just + * {@link #NOTIFY_SYNC_TO_NETWORK}. */ + @Deprecated public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer, boolean syncToNetwork) { - Preconditions.checkNotNull(uri, "uri"); - notifyChange( - ContentProvider.getUriWithoutUserId(uri), - observer, - syncToNetwork, - ContentProvider.getUserIdFromUri(uri, mContext.getUserId())); + notifyChange(uri, observer, syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0); } /** @@ -2398,10 +2399,10 @@ public abstract class ContentResolver implements ContentInterface { * To observe events sent through this call, use * {@link #registerContentObserver(Uri, boolean, ContentObserver)}. * <p> - * If syncToNetwork is true, this will attempt to schedule a local sync - * using the sync adapter that's registered for the authority of the - * provided uri. No account will be passed to the sync adapter, so all - * matching accounts will be synchronized. + * If {@link #NOTIFY_SYNC_TO_NETWORK} is set, this will attempt to schedule + * a local sync using the sync adapter that's registered for the authority + * of the provided uri. No account will be passed to the sync adapter, so + * all matching accounts will be synchronized. * <p> * Starting in {@link android.os.Build.VERSION_CODES#O}, all content * notifications must be backed by a valid {@link ContentProvider}. @@ -2427,21 +2428,71 @@ public abstract class ContentResolver implements ContentInterface { } /** + * Notify registered observers that several rows have been updated. + * <p> + * To observe events sent through this call, use + * {@link #registerContentObserver(Uri, boolean, ContentObserver)}. + * <p> + * If {@link #NOTIFY_SYNC_TO_NETWORK} is set, this will attempt to schedule + * a local sync using the sync adapter that's registered for the authority + * of the provided uri. No account will be passed to the sync adapter, so + * all matching accounts will be synchronized. + * <p> + * Starting in {@link android.os.Build.VERSION_CODES#O}, all content + * notifications must be backed by a valid {@link ContentProvider}. + * + * @param uris The uris of the content that was changed. + * @param observer The observer that originated the change, may be + * <code>null</null>. The observer that originated the change + * will only receive the notification if it has requested to + * receive self-change notifications by implementing + * {@link ContentObserver#deliverSelfNotifications()} to return + * true. + * @param flags Flags such as {@link #NOTIFY_SYNC_TO_NETWORK} or + * {@link #NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS}. + */ + public void notifyChange(@NonNull Iterable<Uri> uris, @Nullable ContentObserver observer, + @NotifyFlags int flags) { + Preconditions.checkNotNull(uris, "uris"); + + // Cluster based on user ID + final SparseArray<ArrayList<Uri>> clusteredByUser = new SparseArray<>(); + for (Uri uri : uris) { + final int userId = ContentProvider.getUserIdFromUri(uri, mContext.getUserId()); + ArrayList<Uri> list = clusteredByUser.get(userId); + if (list == null) { + list = new ArrayList<>(); + clusteredByUser.put(userId, list); + } + list.add(ContentProvider.getUriWithoutUserId(uri)); + } + + for (int i = 0; i < clusteredByUser.size(); i++) { + final int userId = clusteredByUser.keyAt(i); + final ArrayList<Uri> list = clusteredByUser.valueAt(i); + notifyChange(list.toArray(new Uri[list.size()]), observer, flags, userId); + } + } + + /** * Notify registered observers within the designated user(s) that a row was updated. * + * @deprecated callers should consider migrating to + * {@link #notifyChange(Uri, ContentObserver, int)}, as it + * offers support for many more options than just + * {@link #NOTIFY_SYNC_TO_NETWORK}. * @hide */ + @Deprecated public void notifyChange(@NonNull Uri uri, ContentObserver observer, boolean syncToNetwork, @UserIdInt int userHandle) { - try { - getContentService().notifyChange( - uri, observer == null ? null : observer.getContentObserver(), - observer != null && observer.deliverSelfNotifications(), - syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0, - userHandle, mTargetSdkVersion, mContext.getPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + notifyChange(uri, observer, syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0, userHandle); + } + + /** {@hide} */ + public void notifyChange(@NonNull Uri uri, ContentObserver observer, @NotifyFlags int flags, + @UserIdInt int userHandle) { + notifyChange(new Uri[] { uri }, observer, flags, userHandle); } /** @@ -2449,11 +2500,11 @@ public abstract class ContentResolver implements ContentInterface { * * @hide */ - public void notifyChange(@NonNull Uri uri, ContentObserver observer, @NotifyFlags int flags, + public void notifyChange(@NonNull Uri[] uris, ContentObserver observer, @NotifyFlags int flags, @UserIdInt int userHandle) { try { getContentService().notifyChange( - uri, observer == null ? null : observer.getContentObserver(), + uris, observer == null ? null : observer.getContentObserver(), observer != null && observer.deliverSelfNotifications(), flags, userHandle, mTargetSdkVersion, mContext.getPackageName()); } catch (RemoteException e) { diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl index a34a9951671c..03c99e1a2344 100644 --- a/core/java/android/content/IContentService.aidl +++ b/core/java/android/content/IContentService.aidl @@ -51,7 +51,7 @@ interface IContentService { * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL * USER_CURRENT are properly interpreted. */ - void notifyChange(in Uri uri, IContentObserver observer, + void notifyChange(in Uri[] uris, IContentObserver observer, boolean observerWantsSelfNotifications, int flags, int userHandle, int targetSdkVersion, String callingPackage); diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 4a62bc507d92..bc7307b3ee6c 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -394,6 +394,15 @@ public final class ContentService extends IContentService.Stub { * allowed. */ @Override + public void notifyChange(Uri[] uris, IContentObserver observer, + boolean observerWantsSelfNotifications, int flags, int userHandle, + int targetSdkVersion, String callingPackage) { + for (Uri uri : uris) { + notifyChange(uri, observer, observerWantsSelfNotifications, flags, userHandle, + targetSdkVersion, callingPackage); + } + } + public void notifyChange(Uri uri, IContentObserver observer, boolean observerWantsSelfNotifications, int flags, int userHandle, int targetSdkVersion, String callingPackage) { diff --git a/test-mock/src/android/test/mock/MockContentResolver.java b/test-mock/src/android/test/mock/MockContentResolver.java index a70152c8b732..8283019a10ec 100644 --- a/test-mock/src/android/test/mock/MockContentResolver.java +++ b/test-mock/src/android/test/mock/MockContentResolver.java @@ -16,6 +16,8 @@ package android.test.mock; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; @@ -130,17 +132,47 @@ public class MockContentResolver extends ContentResolver { } /** - * Overrides {@link android.content.ContentResolver#notifyChange(Uri, ContentObserver, boolean) - * ContentResolver.notifChange(Uri, ContentObserver, boolean)}. All parameters are ignored. - * The method hides providers linked to MockContentResolver from other observers in the system. + * Overrides the behavior from the parent class to completely ignore any + * content notifications sent to this object. This effectively hides clients + * from observers elsewhere in the system. + */ + @Override + public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer) { + } + + /** + * Overrides the behavior from the parent class to completely ignore any + * content notifications sent to this object. This effectively hides clients + * from observers elsewhere in the system. * - * @param uri (Ignored) The uri of the content provider. - * @param observer (Ignored) The observer that originated the change. - * @param syncToNetwork (Ignored) If true, attempt to sync the change to the network. + * @deprecated callers should consider migrating to + * {@link #notifyChange(Uri, ContentObserver, int)}, as it + * offers support for many more options than just + * {@link #NOTIFY_SYNC_TO_NETWORK}. */ @Override - public void notifyChange(Uri uri, - ContentObserver observer, + @Deprecated + public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer, boolean syncToNetwork) { } + + /** + * Overrides the behavior from the parent class to completely ignore any + * content notifications sent to this object. This effectively hides clients + * from observers elsewhere in the system. + */ + @Override + public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer, + @NotifyFlags int flags) { + } + + /** + * Overrides the behavior from the parent class to completely ignore any + * content notifications sent to this object. This effectively hides clients + * from observers elsewhere in the system. + */ + @Override + public void notifyChange(@NonNull Iterable<Uri> uris, @Nullable ContentObserver observer, + @NotifyFlags int flags) { + } } |