From 8b0cff710816999fddd33d3591bc52c87600c095 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Mon, 9 Mar 2020 15:49:01 -0600 Subject: [PATCH] Give hidden API behavior to legacy apps. Starting in R, there is a new public API overload that delivers a flags argument. Some apps may be relying on a previous hidden API that delivered a userId argument, and this change is used to control delivery of the new flags argument in its place. There are dozens of these hidden API users are in the system UID, either in the system process or the Settings app, so we hard-code giving them the legacy behavior, since refactoring would be messy between internal and AOSP branches. Also adjust incoming and outgoing method signatures to use slightly more flexible Collection, which has handy methods like contains() and isEmpty(). Bug: 150939131 Test: atest --test-mapping packages/providers/MediaProvider Test: atest FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest Exempt-From-Owner-Approval: trivial refactoring Change-Id: If6a77449e19215cf1c60d4217e62fc04b0959bfc --- api/current.txt | 6 +-- api/removed.txt | 4 ++ .../java/android/content/ContentResolver.java | 12 ++++- .../android/database/ContentObserver.java | 45 +++++++++++++++---- .../database/CursorToBulkCursorAdaptor.java | 8 ++-- .../AccessibilityShortcutController.java | 3 +- ...KernelCpuThreadReaderSettingsObserver.java | 3 +- .../util/NotificationMessagingUtil.java | 5 ++- .../AbstractCrossUserContentResolverTest.java | 5 ++- .../android/keyguard/clock/ClockManager.java | 5 ++- .../controller/ControlsControllerImpl.kt | 2 +- .../android/systemui/doze/DozeSensors.java | 3 +- .../theme/ThemeOverlayController.java | 3 +- .../systemui/tuner/TunerServiceImpl.java | 3 +- .../server/wm/ActivityTaskManagerService.java | 3 +- .../test/mock/MockContentResolver.java | 3 +- 16 files changed, 83 insertions(+), 30 deletions(-) diff --git a/api/current.txt b/api/current.txt index 9935e6d352215..70b0d8a5294a1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9882,7 +9882,7 @@ package android.content { method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver); 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, @Nullable android.database.ContentObserver, int); + method public void notifyChange(@NonNull java.util.Collection, @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; @@ -12972,11 +12972,11 @@ package android.database { method @Deprecated public final void dispatchChange(boolean); method public final void dispatchChange(boolean, @Nullable android.net.Uri); method public final void dispatchChange(boolean, @Nullable android.net.Uri, int); - method public final void dispatchChange(boolean, @NonNull Iterable, int); + method public final void dispatchChange(boolean, @NonNull java.util.Collection, int); method public void onChange(boolean); method public void onChange(boolean, @Nullable android.net.Uri); method public void onChange(boolean, @Nullable android.net.Uri, int); - method public void onChange(boolean, @NonNull Iterable, int); + method public void onChange(boolean, @NonNull java.util.Collection, int); } public interface CrossProcessCursor extends android.database.Cursor { diff --git a/api/removed.txt b/api/removed.txt index fb6d57694c78b..8537b21eb4383 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -69,6 +69,10 @@ package android.app.usage { package android.content { + public abstract class ContentResolver { + method @Deprecated public void notifyChange(@NonNull Iterable, @Nullable android.database.ContentObserver, int); + } + public abstract class Context { method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); method public abstract java.io.File getSharedPreferencesPath(String); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 911ffa06ed386..31e1fc824ed22 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -83,6 +83,7 @@ import java.io.OutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Random; @@ -2670,6 +2671,15 @@ public abstract class ContentResolver implements ContentInterface { ContentProvider.getUserIdFromUri(uri, mContext.getUserId())); } + /** @removed */ + @Deprecated + public void notifyChange(@NonNull Iterable uris, @Nullable ContentObserver observer, + @NotifyFlags int flags) { + final Collection asCollection = new ArrayList<>(); + uris.forEach(asCollection::add); + notifyChange(asCollection, observer, flags); + } + /** * Notify registered observers that several rows have been updated. *

@@ -2694,7 +2704,7 @@ public abstract class ContentResolver implements ContentInterface { * @param flags Flags such as {@link #NOTIFY_SYNC_TO_NETWORK} or * {@link #NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS}. */ - public void notifyChange(@NonNull Iterable uris, @Nullable ContentObserver observer, + public void notifyChange(@NonNull Collection uris, @Nullable ContentObserver observer, @NotifyFlags int flags) { Objects.requireNonNull(uris, "uris"); diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java index ede264d042cea..578d53b17bf96 100644 --- a/core/java/android/database/ContentObserver.java +++ b/core/java/android/database/ContentObserver.java @@ -19,6 +19,9 @@ package android.database; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver.NotifyFlags; import android.net.Uri; @@ -26,12 +29,26 @@ import android.os.Handler; import android.os.UserHandle; import java.util.Arrays; +import java.util.Collection; /** * Receives call backs for changes to content. * Must be implemented by objects which are added to a {@link ContentObservable}. */ public abstract class ContentObserver { + /** + * Starting in {@link android.os.Build.VERSION_CODES#R}, there is a new + * public API overload {@link #onChange(boolean, Uri, int)} that delivers a + * {@code int flags} argument. + *

+ * Some apps may be relying on a previous hidden API that delivered a + * {@code int userId} argument, and this change is used to control delivery + * of the new {@code int flags} argument in its place. + */ + @ChangeId + @EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q) + private static final long ADD_CONTENT_OBSERVER_FLAGS = 150939131L; + private final Object mLock = new Object(); private Transport mTransport; // guarded by mLock @@ -164,16 +181,26 @@ public abstract class ContentObserver { * @param uris The Uris of the changed content. * @param flags Flags indicating details about this change. */ - public void onChange(boolean selfChange, @NonNull Iterable uris, @NotifyFlags int flags) { + public void onChange(boolean selfChange, @NonNull Collection uris, + @NotifyFlags int flags) { for (Uri uri : uris) { onChange(selfChange, uri, flags); } } /** @hide */ - public void onChange(boolean selfChange, @NonNull Iterable uris, @NotifyFlags int flags, - @UserIdInt int userId) { - onChange(selfChange, uris, flags); + public void onChange(boolean selfChange, @NonNull Collection uris, + @NotifyFlags int flags, @UserIdInt int userId) { + // There are dozens of people relying on the hidden API inside the + // system UID, so hard-code the old behavior for all of them; for + // everyone else we gate based on a specific change + if (!CompatChanges.isChangeEnabled(ADD_CONTENT_OBSERVER_FLAGS) + || android.os.Process.myUid() == android.os.Process.SYSTEM_UID) { + // Deliver userId through argument to preserve hidden API behavior + onChange(selfChange, uris, userId); + } else { + onChange(selfChange, uris, flags); + } } /** @@ -186,7 +213,7 @@ public abstract class ContentObserver { * * @deprecated Callers should migrate towards using a richer overload that * provides more details about the change, such as - * {@link #dispatchChange(boolean, Iterable, int)}. + * {@link #dispatchChange(boolean, Collection, int)}. */ @Deprecated public final void dispatchChange(boolean selfChange) { @@ -206,7 +233,7 @@ public abstract class ContentObserver { * @param uri The Uri of the changed content. */ public final void dispatchChange(boolean selfChange, @Nullable Uri uri) { - dispatchChange(selfChange, Arrays.asList(uri), 0, UserHandle.getCallingUserId()); + dispatchChange(selfChange, uri, 0); } /** @@ -224,7 +251,7 @@ public abstract class ContentObserver { */ public final void dispatchChange(boolean selfChange, @Nullable Uri uri, @NotifyFlags int flags) { - dispatchChange(selfChange, Arrays.asList(uri), flags, UserHandle.getCallingUserId()); + dispatchChange(selfChange, Arrays.asList(uri), flags); } /** @@ -240,13 +267,13 @@ public abstract class ContentObserver { * @param uris The Uri of the changed content. * @param flags Flags indicating details about this change. */ - public final void dispatchChange(boolean selfChange, @NonNull Iterable uris, + public final void dispatchChange(boolean selfChange, @NonNull Collection uris, @NotifyFlags int flags) { dispatchChange(selfChange, uris, flags, UserHandle.getCallingUserId()); } /** @hide */ - public final void dispatchChange(boolean selfChange, @NonNull Iterable uris, + public final void dispatchChange(boolean selfChange, @NonNull Collection uris, @NotifyFlags int flags, @UserIdInt int userId) { if (mHandler == null) { onChange(selfChange, uris, flags, userId); diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java index 1855dd254ad47..ce86807ebf7a8 100644 --- a/core/java/android/database/CursorToBulkCursorAdaptor.java +++ b/core/java/android/database/CursorToBulkCursorAdaptor.java @@ -20,10 +20,12 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.content.ContentResolver.NotifyFlags; import android.net.Uri; -import android.os.*; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; import java.util.ArrayList; - +import java.util.Collection; /** * Wraps a BulkCursor around an existing Cursor making it remotable. @@ -81,7 +83,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative } @Override - public void onChange(boolean selfChange, @NonNull Iterable uris, + public void onChange(boolean selfChange, @NonNull Collection uris, @NotifyFlags int flags, @UserIdInt int userId) { // Since we deliver changes from the most-specific to least-specific // overloads, we only need to redirect from the most-specific local diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index 5cdcab0298773..54ea57a6cae4b 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -56,6 +56,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -133,7 +134,7 @@ public class AccessibilityShortcutController { // Keep track of state of shortcut settings final ContentObserver co = new ContentObserver(handler) { @Override - public void onChange(boolean selfChange, Uri uri, int userId) { + public void onChange(boolean selfChange, Collection uris, int flags, int userId) { if (userId == mUserId) { onSettingsChanged(); } diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java index f8c0d9e4a27ee..fdcc8a8c9cbf4 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java @@ -29,6 +29,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.function.Predicate; import java.util.regex.Matcher; @@ -99,7 +100,7 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { } @Override - public void onChange(boolean selfChange, Uri uri, int userId) { + public void onChange(boolean selfChange, Collection uris, int flags, int userId) { updateReader(); } diff --git a/core/java/com/android/internal/util/NotificationMessagingUtil.java b/core/java/com/android/internal/util/NotificationMessagingUtil.java index bf796cddd89e0..28994fd521262 100644 --- a/core/java/com/android/internal/util/NotificationMessagingUtil.java +++ b/core/java/com/android/internal/util/NotificationMessagingUtil.java @@ -28,6 +28,7 @@ import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; +import java.util.Collection; import java.util.Objects; /** @@ -77,8 +78,8 @@ public class NotificationMessagingUtil { private final ContentObserver mSmsContentObserver = new ContentObserver( new Handler(Looper.getMainLooper())) { @Override - public void onChange(boolean selfChange, Uri uri, int userId) { - if (Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING).equals(uri)) { + public void onChange(boolean selfChange, Collection uris, int flags, int userId) { + if (uris.contains(Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING))) { cacheDefaultSmsApp(userId); } } diff --git a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java index c307e648752db..328429c6f96e7 100644 --- a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java +++ b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java @@ -39,6 +39,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Collection; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -194,8 +195,8 @@ abstract class AbstractCrossUserContentResolverTest { } @Override - public void onChange(boolean selfChange, Uri uri, int userId) { - if (mExpectedUri.equals(uri) && mExpectedUserId == userId) { + public void onChange(boolean selfChange, Collection uris, int flags, int userId) { + if (uris.contains(mExpectedUri) && mExpectedUserId == userId) { mLatch.countDown(); } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 03674648d1e47..2200b22b8b27e 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -43,6 +43,7 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.InjectionInflationController; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; @@ -74,8 +75,8 @@ public final class ClockManager { private final ContentObserver mContentObserver = new ContentObserver(mMainHandler) { @Override - public void onChange(boolean selfChange, Uri uri, int userId) { - super.onChange(selfChange, uri, userId); + public void onChange(boolean selfChange, Collection uris, + int flags, int userId) { if (Objects.equals(userId, mCurrentUserObservable.getCurrentUser().getValue())) { reload(); diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index 989b7cf2423b6..e5d36f942ac85 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -127,7 +127,7 @@ class ControlsControllerImpl @Inject constructor ( internal val settingObserver = object : ContentObserver(null) { override fun onChange( selfChange: Boolean, - uris: MutableIterable, + uris: Collection, flags: Int, userId: Int ) { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index c28a719b9826f..700a8611c8bd7 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -49,6 +49,7 @@ import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.wakelock.WakeLock; import java.io.PrintWriter; +import java.util.Collection; import java.util.List; import java.util.function.Consumer; @@ -261,7 +262,7 @@ public class DozeSensors { private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { @Override - public void onChange(boolean selfChange, Iterable uris, int flags, int userId) { + public void onChange(boolean selfChange, Collection uris, int flags, int userId) { if (userId != ActivityManager.getCurrentUser()) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 30db37c4f128a..f31f8eb0b20d5 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -43,6 +43,7 @@ import com.google.android.collect.Sets; import org.json.JSONException; import org.json.JSONObject; +import java.util.Collection; import java.util.Map; import java.util.Set; @@ -101,7 +102,7 @@ public class ThemeOverlayController extends SystemUI { new ContentObserver(mBgHandler) { @Override - public void onChange(boolean selfChange, Iterable uris, int flags, + public void onChange(boolean selfChange, Collection uris, int flags, int userId) { if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId); if (ActivityManager.getCurrentUser() == userId) { diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index b2a5f5bee5434..2452218226bb8 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -262,7 +262,8 @@ public class TunerServiceImpl extends TunerService { } @Override - public void onChange(boolean selfChange, Iterable uris, int flags, int userId) { + public void onChange(boolean selfChange, java.util.Collection uris, + int flags, int userId) { if (userId == ActivityManager.getCurrentUser()) { for (Uri u : uris) { reloadSetting(u); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index d3ff912ea327a..21d300aba1b9e 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -288,6 +288,7 @@ import java.lang.ref.WeakReference; import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -680,7 +681,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void onChange(boolean selfChange, Iterable uris, int flags, + public void onChange(boolean selfChange, Collection uris, int flags, @UserIdInt int userId) { for (Uri uri : uris) { if (mFontScaleUri.equals(uri)) { diff --git a/test-mock/src/android/test/mock/MockContentResolver.java b/test-mock/src/android/test/mock/MockContentResolver.java index 8283019a10ec4..8f4bcccb0cbaf 100644 --- a/test-mock/src/android/test/mock/MockContentResolver.java +++ b/test-mock/src/android/test/mock/MockContentResolver.java @@ -25,6 +25,7 @@ import android.content.IContentProvider; import android.database.ContentObserver; import android.net.Uri; +import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -172,7 +173,7 @@ public class MockContentResolver extends ContentResolver { * from observers elsewhere in the system. */ @Override - public void notifyChange(@NonNull Iterable uris, @Nullable ContentObserver observer, + public void notifyChange(@NonNull Collection uris, @Nullable ContentObserver observer, @NotifyFlags int flags) { } }