From 0bf76b6296e7095ab98ea175452e5697e9afcc66 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 19 Jul 2021 17:05:53 -0700 Subject: [PATCH] Apply overlay updates to widget provider info When Runtime Resource Overlays (RROs) are applied to an application, the ApplicationInfo of the app in package manager is updated to have overlay paths. Widgets create the context used to inflate their remote views using a snapshot of the ApplicationInfo of the widget provider. This snapshot is taken when the widget RemoteView is initially created and sent to system server. This change updates the snapshot of the widget provider ApplicationInfo with the new overlay paths and notifies apps hosting widgets to re-inflate their remote views in order to have RROs apply to widgets correctly. Bug: 193866093 Test: Repeatedly change wallpaper & style color to a "basic" color and go back to launcher to observe overlay correct color is applied Change-Id: I0b9b8c0d32d83a52e9ea5bc8ba1635cf99e4b921 --- .../appwidget/AppWidgetManagerInternal.java | 14 +++++ core/java/android/widget/RemoteViews.java | 19 ++++++ .../appwidget/AppWidgetServiceImpl.java | 60 +++++++++++++++++++ .../server/am/ActivityManagerService.java | 8 +++ 4 files changed, 101 insertions(+) diff --git a/core/java/android/appwidget/AppWidgetManagerInternal.java b/core/java/android/appwidget/AppWidgetManagerInternal.java index 5694ca8604536..266e33af0e22b 100644 --- a/core/java/android/appwidget/AppWidgetManagerInternal.java +++ b/core/java/android/appwidget/AppWidgetManagerInternal.java @@ -19,6 +19,8 @@ package android.appwidget; import android.annotation.Nullable; import android.util.ArraySet; +import java.util.Set; + /** * App widget manager local system service interface. * @@ -42,4 +44,16 @@ public abstract class AppWidgetManagerInternal { * @param userId The user that is being unlocked. */ public abstract void unlockUser(int userId); + + /** + * Updates all widgets, applying changes to Runtime Resource Overlay affecting the specified + * target packages. + * + * @param packageNames The names of all target packages for which an overlay was modified + * @param userId The user for which overlay modifications occurred. + * @param updateFrameworkRes Whether or not an overlay affected the values of framework + * resources. + */ + public abstract void applyResourceOverlaysToWidgets(Set packageNames, int userId, + boolean updateFrameworkRes); } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index e827f0a31bfd4..91fc5a56d9795 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -5824,6 +5824,25 @@ public class RemoteViews implements Parcelable, Filter { return false; } + /** @hide */ + public void updateAppInfo(@NonNull ApplicationInfo info) { + if (mApplication != null && mApplication.sourceDir.equals(info.sourceDir)) { + // Overlay paths are generated against a particular version of an application. + // The overlays paths of a newly upgraded application are incompatible with the + // old version of the application. + mApplication = info; + } + if (hasSizedRemoteViews()) { + for (RemoteViews layout : mSizedRemoteViews) { + layout.updateAppInfo(info); + } + } + if (hasLandscapeAndPortraitLayouts()) { + mLandscape.updateAppInfo(info); + mPortrait.updateAppInfo(info); + } + } + private Context getContextForResources(Context context) { if (mApplication != null) { if (context.getUserId() == UserHandle.getUserId(mApplication.uid) diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 5aec6aa99c128..a56b1db1494c8 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -3285,6 +3285,57 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } + private void applyResourceOverlaysToWidgetsLocked(Set packageNames, int userId, + boolean updateFrameworkRes) { + for (int i = 0, N = mProviders.size(); i < N; i++) { + Provider provider = mProviders.get(i); + if (provider.getUserId() != userId) { + continue; + } + + final String packageName = provider.id.componentName.getPackageName(); + if (!updateFrameworkRes && !packageNames.contains(packageName)) { + continue; + } + + ApplicationInfo newAppInfo = null; + try { + newAppInfo = mPackageManager.getApplicationInfo(packageName, + PackageManager.GET_SHARED_LIBRARY_FILES, userId); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to retrieve app info for " + packageName + + " userId=" + userId, e); + } + if (newAppInfo == null) { + continue; + } + ApplicationInfo oldAppInfo = provider.info.providerInfo.applicationInfo; + if (!newAppInfo.sourceDir.equals(oldAppInfo.sourceDir)) { + // Overlay paths are generated against a particular version of an application. + // The overlays paths of a newly upgraded application are incompatible with the + // old version of the application. + continue; + } + + // Isolate the changes relating to RROs. The app info must be copied to prevent + // affecting other parts of system server that may have cached this app info. + oldAppInfo = new ApplicationInfo(oldAppInfo); + oldAppInfo.overlayPaths = newAppInfo.overlayPaths.clone(); + oldAppInfo.resourceDirs = newAppInfo.resourceDirs.clone(); + provider.info.providerInfo.applicationInfo = oldAppInfo; + + for (int j = 0, M = provider.widgets.size(); j < M; j++) { + Widget widget = provider.widgets.get(j); + if (widget.views != null) { + widget.views.updateAppInfo(oldAppInfo); + } + if (widget.maskedViews != null) { + widget.maskedViews.updateAppInfo(oldAppInfo); + } + } + } + } + /** * Updates all providers with the specified package names, and records any providers that were * pruned. @@ -4875,5 +4926,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku public void unlockUser(int userId) { handleUserUnlocked(userId); } + + @Override + public void applyResourceOverlaysToWidgets(Set packageNames, int userId, + boolean updateFrameworkRes) { + synchronized (mLock) { + applyResourceOverlaysToWidgetsLocked(new HashSet<>(packageNames), userId, + updateFrameworkRes); + } + } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 0d35bb1805141..a03d147df9ec0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -193,6 +193,7 @@ import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetManagerInternal; import android.content.AttributionSource; import android.content.AutofillOptions; import android.content.BroadcastReceiver; @@ -16588,6 +16589,13 @@ public class ActivityManagerService extends IActivityManager.Stub if (updateFrameworkRes) { ParsingPackageUtils.readConfigUseRoundIcon(null); } + + AppWidgetManagerInternal widgets = LocalServices.getService(AppWidgetManagerInternal.class); + if (widgets != null) { + widgets.applyResourceOverlaysToWidgets(new HashSet<>(packagesToUpdate), userId, + updateFrameworkRes); + } + mProcessList.updateApplicationInfoLOSP(packagesToUpdate, userId, updateFrameworkRes); if (updateFrameworkRes) {