summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java28
-rw-r--r--core/java/com/android/internal/appwidget/IAppWidgetService.aidl1
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java124
4 files changed, 131 insertions, 23 deletions
diff --git a/api/current.txt b/api/current.txt
index b2121a85313d..5f4115b9bda2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7362,6 +7362,7 @@ package android.appwidget {
method public void updateAppWidget(int, android.widget.RemoteViews);
method public void updateAppWidget(android.content.ComponentName, android.widget.RemoteViews);
method public void updateAppWidgetOptions(int, android.os.Bundle);
+ method public void updateAppWidgetProviderInfo(android.content.ComponentName, java.lang.String);
field public static final java.lang.String ACTION_APPWIDGET_BIND = "android.appwidget.action.APPWIDGET_BIND";
field public static final java.lang.String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE";
field public static final java.lang.String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 37bb6b05c3e7..a55bbdaea56f 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -677,6 +677,34 @@ public class AppWidgetManager {
}
/**
+ * Updates the info for the supplied AppWidget provider.
+ *
+ * <p>
+ * The manifest entry of the provider should contain an additional meta-data tag similar to
+ * {@link #META_DATA_APPWIDGET_PROVIDER} which should point to any additional definitions for
+ * the provider.
+ *
+ * <p>
+ * This is persisted across device reboots and app updates. If this meta-data key is not
+ * present in the manifest entry, the info reverts to default.
+ *
+ * @param provider {@link ComponentName} for the {@link
+ * android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget.
+ * @param metaDataKey key for the meta-data tag pointing to the new provider info. Use null
+ * to reset any previously set info.
+ */
+ public void updateAppWidgetProviderInfo(ComponentName provider, @Nullable String metaDataKey) {
+ if (mService == null) {
+ return;
+ }
+ try {
+ mService.updateAppWidgetProviderInfo(provider, metaDataKey);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Notifies the specified collection view in all the specified AppWidget instances
* to invalidate their data.
*
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index a4da6b9cea18..f9bf3736422b 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -56,6 +56,7 @@ interface IAppWidgetService {
void partiallyUpdateAppWidgetIds(String callingPackage, in int[] appWidgetIds,
in RemoteViews views);
void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views);
+ void updateAppWidgetProviderInfo(in ComponentName provider, in String metadataKey);
void notifyAppWidgetViewDataChanged(String packageName, in int[] appWidgetIds, int viewId);
ParceledListSlice getInstalledProvidersForProfile(int categoryFilter, int profileId,
String packageName);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 54cf726c6b0e..85b02206a594 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -120,7 +120,6 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -134,6 +133,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
@@ -1568,6 +1568,57 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
@Override
+ public void updateAppWidgetProviderInfo(ComponentName componentName, String metadataKey) {
+ final int userId = UserHandle.getCallingUserId();
+ if (DEBUG) {
+ Slog.i(TAG, "updateAppWidgetProvider() " + userId);
+ }
+
+ // Make sure the package runs under the caller uid.
+ mSecurityPolicy.enforceCallFromPackage(componentName.getPackageName());
+
+ synchronized (mLock) {
+ ensureGroupStateLoadedLocked(userId);
+
+ // NOTE: The lookup is enforcing security across users by making
+ // sure the caller can access only its providers.
+ ProviderId providerId = new ProviderId(Binder.getCallingUid(), componentName);
+ Provider provider = lookupProviderLocked(providerId);
+ if (provider == null) {
+ throw new IllegalArgumentException(
+ componentName + " is not a valid AppWidget provider");
+ }
+ if (Objects.equals(provider.infoTag, metadataKey)) {
+ // No change
+ return;
+ }
+
+ String keyToUse = metadataKey == null
+ ? AppWidgetManager.META_DATA_APPWIDGET_PROVIDER : metadataKey;
+ AppWidgetProviderInfo info =
+ parseAppWidgetProviderInfo(providerId, provider.info.providerInfo, keyToUse);
+ if (info == null) {
+ throw new IllegalArgumentException("Unable to parse " + keyToUse
+ + " meta-data to a valid AppWidget provider");
+ }
+
+ provider.info = info;
+ provider.infoTag = metadataKey;
+
+ // Update all widgets for this provider
+ final int N = provider.widgets.size();
+ for (int i = 0; i < N; i++) {
+ Widget widget = provider.widgets.get(i);
+ scheduleNotifyProviderChangedLocked(widget);
+ updateAppWidgetInstanceLocked(widget, widget.views, false /* isPartialUpdate */);
+ }
+
+ saveGroupStateAsync(userId);
+ scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
+ }
+ }
+
+ @Override
public boolean isRequestPinAppWidgetSupported() {
return LocalServices.getService(ShortcutServiceInternal.class)
.isRequestPinItemSupported(UserHandle.getCallingUserId(),
@@ -2168,7 +2219,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
ri.activityInfo.name);
ProviderId providerId = new ProviderId(ri.activityInfo.applicationInfo.uid, componentName);
- Provider provider = parseProviderInfoXml(providerId, ri);
+ Provider provider = parseProviderInfoXml(providerId, ri, null);
if (provider != null) {
// we might have an inactive entry for this provider already due to
// a preceding restore operation. if so, fix it up in place; otherwise
@@ -2362,6 +2413,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
out.attribute(null, "pkg", p.info.provider.getPackageName());
out.attribute(null, "cl", p.info.provider.getClassName());
out.attribute(null, "tag", Integer.toHexString(p.tag));
+ if (!TextUtils.isEmpty(p.infoTag)) {
+ out.attribute(null, "info_tag", p.infoTag);
+ }
out.endTag(null, "p");
}
@@ -2422,17 +2476,33 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
@SuppressWarnings("deprecation")
- private Provider parseProviderInfoXml(ProviderId providerId, ResolveInfo ri) {
- Provider provider = null;
-
- ActivityInfo activityInfo = ri.activityInfo;
- XmlResourceParser parser = null;
- try {
- parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
+ private Provider parseProviderInfoXml(ProviderId providerId, ResolveInfo ri,
+ Provider oldProvider) {
+ AppWidgetProviderInfo info = null;
+ if (oldProvider != null && !TextUtils.isEmpty(oldProvider.infoTag)) {
+ info = parseAppWidgetProviderInfo(providerId, ri.activityInfo, oldProvider.infoTag);
+ }
+ if (info == null) {
+ info = parseAppWidgetProviderInfo(providerId, ri.activityInfo,
AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
+ }
+ if (info == null) {
+ return null;
+ }
+
+ Provider provider = new Provider();
+ provider.id = providerId;
+ provider.info = info;
+ return provider;
+ }
+
+ private AppWidgetProviderInfo parseAppWidgetProviderInfo(
+ ProviderId providerId, ActivityInfo activityInfo, String metadataKey) {
+ try (XmlResourceParser parser =
+ activityInfo.loadXmlMetaData(mContext.getPackageManager(), metadataKey)) {
if (parser == null) {
- Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
- + " meta-data for " + "AppWidget provider '" + providerId + '\'');
+ Slog.w(TAG, "No " + metadataKey + " meta-data for AppWidget provider '"
+ + providerId + '\'');
return null;
}
@@ -2452,9 +2522,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
return null;
}
- provider = new Provider();
- provider.id = providerId;
- AppWidgetProviderInfo info = provider.info = new AppWidgetProviderInfo();
+ AppWidgetProviderInfo info = new AppWidgetProviderInfo();
info.provider = providerId.componentName;
info.providerInfo = activityInfo;
@@ -2501,7 +2569,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
className);
}
info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
- info.icon = ri.getIconResource();
+ info.icon = activityInfo.getIconResource();
info.previewImage = sa.getResourceId(
com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
info.autoAdvanceViewId = sa.getResourceId(
@@ -2516,6 +2584,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures, 0);
sa.recycle();
+ return info;
} catch (IOException | PackageManager.NameNotFoundException | XmlPullParserException e) {
// Ok to catch Exception here, because anything going wrong because
// of what a client process passes to us should not be fatal for the
@@ -2523,12 +2592,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
Slog.w(TAG, "XML parsing failed for AppWidget provider "
+ providerId.componentName + " for user " + providerId.uid, e);
return null;
- } finally {
- if (parser != null) {
- parser.close();
- }
}
- return provider;
}
private int getUidForPackage(String packageName, int userId) {
@@ -2891,7 +2955,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
if (provider.getUserId() != userId) {
continue;
}
- if (provider.widgets.size() > 0) {
+ if (provider.shouldBePersisted()) {
serializeProvider(out, provider);
}
}
@@ -3000,6 +3064,15 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
final int providerTag = !TextUtils.isEmpty(tagAttribute)
? Integer.parseInt(tagAttribute, 16) : legacyProviderIndex;
provider.tag = providerTag;
+
+ provider.infoTag = parser.getAttributeValue(null, "info_tag");
+ if (!TextUtils.isEmpty(provider.infoTag) && !mSafeMode) {
+ AppWidgetProviderInfo info = parseAppWidgetProviderInfo(
+ providerId, providerInfo, provider.infoTag);
+ if (info != null) {
+ provider.info = info;
+ }
+ }
} else if ("h".equals(tag)) {
legacyHostIndex++;
Host host = new Host();
@@ -3254,7 +3327,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
providersUpdated = true;
}
} else {
- Provider parsed = parseProviderInfoXml(providerId, ri);
+ Provider parsed = parseProviderInfoXml(providerId, ri, provider);
if (parsed != null) {
keep.add(providerId);
// Use the new AppWidgetProviderInfo.
@@ -3725,6 +3798,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
AppWidgetProviderInfo info;
ArrayList<Widget> widgets = new ArrayList<>();
PendingIntent broadcast;
+ String infoTag;
boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
boolean maskedByLockedProfile;
@@ -3784,6 +3858,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
public boolean isMaskedLocked() {
return maskedByQuietProfile || maskedByLockedProfile || maskedBySuspendedPackage;
}
+
+ public boolean shouldBePersisted() {
+ return !widgets.isEmpty() || !TextUtils.isEmpty(infoTag);
+ }
}
private static final class ProviderId {
@@ -4114,7 +4192,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
for (int i = 0; i < N; i++) {
Provider provider = mProviders.get(i);
- if (!provider.widgets.isEmpty()
+ if (provider.shouldBePersisted()
&& (provider.isInPackageForUser(backedupPackage, userId)
|| provider.hostedByPackageForUser(backedupPackage, userId))) {
provider.tag = index;