Loading core/java/android/widget/RemoteViews.java +107 −37 Original line number Diff line number Diff line Loading @@ -1105,6 +1105,7 @@ public class RemoteViews implements Parcelable, Filter { SetRemoteCollectionItemListAdapterAction(Parcel parcel) { mViewId = parcel.readInt(); mIntentId = parcel.readInt(); mIsReplacedIntoAction = parcel.readBoolean(); mServiceIntent = parcel.readTypedObject(Intent.CREATOR); mItems = mServiceIntent != null ? null Loading @@ -1128,6 +1129,7 @@ public class RemoteViews implements Parcelable, Filter { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mViewId); dest.writeInt(mIntentId); dest.writeBoolean(mIsReplacedIntoAction); dest.writeTypedObject(mServiceIntent, flags); if (mItems != null) { mItems.writeToParcel(dest, flags, /* attached= */ true); Loading Loading @@ -1208,6 +1210,19 @@ public class RemoteViews implements Parcelable, Filter { } } /** * The maximum size for RemoteViews with converted RemoteCollectionItemsAdapter. * When converting RemoteViewsAdapter to RemoteCollectionItemsAdapter, we want to put size * limits on each unique RemoteCollectionItems in order to not exceed the transaction size limit * for each parcel (typically 1 MB). We leave a certain ratio of the maximum size as a buffer * for missing calculations of certain parameters (e.g. writing a RemoteCollectionItems to the * parcel will write its Id array as well, but that is missing when writing itschild RemoteViews * directly to the parcel as we did in RemoteViewsService) * * @hide */ private static final int MAX_SINGLE_PARCEL_SIZE = (int) (1_000_000 * 0.8); /** * @hide */ Loading Loading @@ -1260,17 +1275,47 @@ public class RemoteViews implements Parcelable, Filter { return mUriToCollectionMapping.get(uri); } CompletableFuture<Void> collectAllIntentsNoComplete(@NonNull RemoteViews inViews) { CompletableFuture<Void> collectionFuture = CompletableFuture.completedFuture(null); public @NonNull CompletableFuture<Void> collectAllIntentsNoComplete( @NonNull RemoteViews inViews) { SparseArray<Intent> idToIntentMapping = new SparseArray<>(); // Collect the number of uinque Intent (which is equal to the number of new connections // to make) for size allocation and exclude certain collections from being written to // the parcel to better estimate the space left for reallocation. collectAllIntentsInternal(inViews, idToIntentMapping); // Calculate the individual size here int numOfIntents = idToIntentMapping.size(); if (numOfIntents == 0) { Log.e(LOG_TAG, "Possibly notifying updates for nonexistent view Id"); return CompletableFuture.completedFuture(null); } Parcel sizeTestParcel = Parcel.obtain(); // Write self RemoteViews to the parcel, which includes the actions/bitmaps/collection // cache to see how much space is left for the RemoteCollectionItems that are to be // updated. RemoteViews.this.writeToParcel(sizeTestParcel, /* flags= */ 0, /* intentsToIgnore= */ idToIntentMapping); int remainingSize = MAX_SINGLE_PARCEL_SIZE - sizeTestParcel.dataSize(); sizeTestParcel.recycle(); int individualSize = remainingSize < 0 ? 0 : remainingSize / numOfIntents; return connectAllUniqueIntents(individualSize, idToIntentMapping); } private void collectAllIntentsInternal(@NonNull RemoteViews inViews, @NonNull SparseArray<Intent> idToIntentMapping) { if (inViews.hasSizedRemoteViews()) { for (RemoteViews remoteViews : inViews.mSizedRemoteViews) { collectionFuture = CompletableFuture.allOf(collectionFuture, collectAllIntentsNoComplete(remoteViews)); collectAllIntentsInternal(remoteViews, idToIntentMapping); } } else if (inViews.hasLandscapeAndPortraitLayouts()) { collectionFuture = CompletableFuture.allOf( collectAllIntentsNoComplete(inViews.mLandscape), collectAllIntentsNoComplete(inViews.mPortrait)); collectAllIntentsInternal(inViews.mLandscape, idToIntentMapping); collectAllIntentsInternal(inViews.mPortrait, idToIntentMapping); } else if (inViews.mActions != null) { for (Action action : inViews.mActions) { if (action instanceof SetRemoteCollectionItemListAdapterAction rca) { Loading @@ -1280,13 +1325,16 @@ public class RemoteViews implements Parcelable, Filter { } if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) { final String uri = mIdToUriMapping.get(rca.mIntentId); collectionFuture = CompletableFuture.allOf(collectionFuture, getItemsFutureFromIntentWithTimeout(rca.mServiceIntent) .thenAccept(rc -> { rc.setHierarchyRootData(getHierarchyRootData()); mUriToCollectionMapping.put(uri, rc); })); rca.mIsReplacedIntoAction = false; // Avoid redundant connections for the same intent. Also making sure // that the number of connections we are making is always equal to the // nmuber of unique intents that are being used for the updates. if (idToIntentMapping.contains(rca.mIntentId)) { continue; } idToIntentMapping.put(rca.mIntentId, rca.mServiceIntent); rca.mItems = null; continue; } Loading @@ -1295,7 +1343,7 @@ public class RemoteViews implements Parcelable, Filter { // intents. if (rca.mServiceIntent != null) { final String uri = rca.mServiceIntent.toUri(0); int index = mIdToUriMapping.indexOfValue(uri); int index = mIdToUriMapping.indexOfValueByValue(uri); if (index == -1) { int newIntentId = mIdToUriMapping.size(); rca.mIntentId = newIntentId; Loading @@ -1305,32 +1353,41 @@ public class RemoteViews implements Parcelable, Filter { rca.mItems = null; continue; } collectionFuture = CompletableFuture.allOf(collectionFuture, getItemsFutureFromIntentWithTimeout(rca.mServiceIntent) .thenAccept(rc -> { rc.setHierarchyRootData(getHierarchyRootData()); mUriToCollectionMapping.put(uri, rc); })); idToIntentMapping.put(rca.mIntentId, rca.mServiceIntent); rca.mItems = null; } else { for (RemoteViews views : rca.mItems.mViews) { collectionFuture = CompletableFuture.allOf(collectionFuture, collectAllIntentsNoComplete(views)); collectAllIntentsInternal(views, idToIntentMapping); } } } else if (action instanceof ViewGroupActionAdd vgaa && vgaa.mNestedViews != null) { collectionFuture = CompletableFuture.allOf(collectionFuture, collectAllIntentsNoComplete(vgaa.mNestedViews)); collectAllIntentsInternal(vgaa.mNestedViews, idToIntentMapping); } } } } return collectionFuture; private @NonNull CompletableFuture<Void> connectAllUniqueIntents(int individualSize, @NonNull SparseArray<Intent> idToIntentMapping) { List<CompletableFuture<Void>> intentFutureList = new ArrayList<>(); for (int i = 0; i < idToIntentMapping.size(); i++) { String currentIntentUri = mIdToUriMapping.get(idToIntentMapping.keyAt(i)); Intent currentIntent = idToIntentMapping.valueAt(i); intentFutureList.add(getItemsFutureFromIntentWithTimeout(currentIntent, individualSize) .thenAccept(items -> { items.setHierarchyRootData(getHierarchyRootData()); mUriToCollectionMapping.put(currentIntentUri, items); })); } return CompletableFuture.allOf(intentFutureList.toArray(CompletableFuture[]::new)); } private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout( Intent intent) { Intent intent, int individualSize) { if (intent == null) { Log.e(LOG_TAG, "Null intent received when generating adapter future"); return CompletableFuture.completedFuture(new RemoteCollectionItems Loading @@ -1338,8 +1395,8 @@ public class RemoteViews implements Parcelable, Filter { } final Context context = ActivityThread.currentApplication(); final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>(); final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>(); context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE), result.defaultExecutor(), new ServiceConnection() { @Override Loading @@ -1348,11 +1405,11 @@ public class RemoteViews implements Parcelable, Filter { RemoteCollectionItems items; try { items = IRemoteViewsFactory.Stub.asInterface(iBinder) .getRemoteCollectionItems(); .getRemoteCollectionItems(individualSize); } catch (RemoteException re) { items = new RemoteCollectionItems.Builder().build(); Log.e(LOG_TAG, "Error getting collection items from the factory", re); Log.e(LOG_TAG, "Error getting collection items from the" + " factory", re); } finally { context.unbindService(this); } Loading @@ -1371,10 +1428,17 @@ public class RemoteViews implements Parcelable, Filter { return result; } public void writeToParcel(Parcel out, int flags) { public void writeToParcel(Parcel out, int flags, @Nullable SparseArray<Intent> intentsToIgnore) { out.writeInt(mIdToUriMapping.size()); for (int i = 0; i < mIdToUriMapping.size(); i++) { out.writeInt(mIdToUriMapping.keyAt(i)); int currentIntentId = mIdToUriMapping.keyAt(i); if (intentsToIgnore != null && intentsToIgnore.contains(currentIntentId)) { // Skip writing collections that are to be updated in the following steps to // better estimate the RemoteViews size. continue; } out.writeInt(currentIntentId); String intentUri = mIdToUriMapping.valueAt(i); out.writeString8(intentUri); mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true); Loading Loading @@ -6724,7 +6788,13 @@ public class RemoteViews implements Parcelable, Filter { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { writeToParcel(dest, flags, /* intentsToIgnore= */ null); } private void writeToParcel(Parcel dest, int flags, @Nullable SparseArray<Intent> intentsToIgnore) { boolean prevSquashingAllowed = dest.allowSquashing(); if (!hasMultipleLayouts()) { Loading @@ -6733,7 +6803,7 @@ public class RemoteViews implements Parcelable, Filter { // is shared by all children. if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags, intentsToIgnore); } mApplication.writeToParcel(dest, flags); if (mIsRoot || mIdealSize == null) { Loading @@ -6750,7 +6820,7 @@ public class RemoteViews implements Parcelable, Filter { dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS); if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags, intentsToIgnore); } dest.writeInt(mSizedRemoteViews.size()); for (RemoteViews view : mSizedRemoteViews) { Loading @@ -6762,7 +6832,7 @@ public class RemoteViews implements Parcelable, Filter { // is shared by all children. if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags, intentsToIgnore); } mLandscape.writeToParcel(dest, flags); // Both RemoteViews already share the same package and user Loading core/java/android/widget/RemoteViewsService.java +15 −10 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.widget; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.Parcel; import com.android.internal.widget.IRemoteViewsFactory; Loading @@ -42,13 +43,6 @@ public abstract class RemoteViewsService extends Service { new HashMap<Intent.FilterComparison, RemoteViewsFactory>(); private static final Object sLock = new Object(); /** * Used for determining the maximum number of entries to retrieve from RemoteViewsFactory * * @hide */ private static final int MAX_NUM_ENTRY = 10; /** * An interface for an adapter between a remote collection view (ListView, GridView, etc) and * the underlying data for that view. The implementor is responsible for making a RemoteView Loading Loading @@ -235,9 +229,10 @@ public abstract class RemoteViewsService extends Service { } @Override public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() { public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) { RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems .Builder().build(); Parcel capSizeTestParcel = Parcel.obtain(); try { RemoteViews.RemoteCollectionItems.Builder itemsBuilder = Loading @@ -245,15 +240,25 @@ public abstract class RemoteViewsService extends Service { mFactory.onDataSetChanged(); itemsBuilder.setHasStableIds(mFactory.hasStableIds()); final int numOfEntries = Math.min(mFactory.getCount(), MAX_NUM_ENTRY); final int numOfEntries = mFactory.getCount(); for (int i = 0; i < numOfEntries; i++) { itemsBuilder.addItem(mFactory.getItemId(i), mFactory.getViewAt(i)); final long currentItemId = mFactory.getItemId(i); final RemoteViews currentView = mFactory.getViewAt(i); currentView.writeToParcel(capSizeTestParcel, 0); if (capSizeTestParcel.dataSize() > capSize) { break; } itemsBuilder.addItem(currentItemId, currentView); } items = itemsBuilder.build(); } catch (Exception ex) { Thread t = Thread.currentThread(); Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex); } finally { // Recycle the parcel capSizeTestParcel.recycle(); } return items; } Loading core/java/com/android/internal/widget/IRemoteViewsFactory.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -39,6 +39,6 @@ interface IRemoteViewsFactory { boolean hasStableIds(); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) boolean isCreated(); RemoteViews.RemoteCollectionItems getRemoteCollectionItems(); RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize); } core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -355,7 +355,7 @@ public class RemoteViewsAdapterTest { } @Override public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() { public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) { RemoteViews.RemoteCollectionItems.Builder itemsBuilder = new RemoteViews.RemoteCollectionItems.Builder(); itemsBuilder.setHasStableIds(hasStableIds()) Loading services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +7 −0 Original line number Diff line number Diff line Loading @@ -2074,6 +2074,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks, int appWidgetId, int viewId, long requestId) { try { Slog.d(TAG, "Trying to notify widget view data changed"); callbacks.viewDataChanged(appWidgetId, viewId); host.lastWidgetUpdateSequenceNo = requestId; } catch (RemoteException re) { Loading Loading @@ -2158,6 +2159,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks, int appWidgetId, RemoteViews views, long requestId) { try { Slog.d(TAG, "Trying to notify widget update for package " + (views == null ? "null" : views.getPackage()) + " with widget id: " + appWidgetId); callbacks.updateAppWidget(appWidgetId, views); host.lastWidgetUpdateSequenceNo = requestId; } catch (RemoteException re) { Loading Loading @@ -2196,6 +2200,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyProviderChanged(Host host, IAppWidgetHost callbacks, int appWidgetId, AppWidgetProviderInfo info, long requestId) { try { Slog.d(TAG, "Trying to notify provider update"); callbacks.providerChanged(appWidgetId, info); host.lastWidgetUpdateSequenceNo = requestId; } catch (RemoteException re) { Loading Loading @@ -2239,6 +2244,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyAppWidgetRemoved(Host host, IAppWidgetHost callbacks, int appWidgetId, long requestId) { try { Slog.d(TAG, "Trying to notify widget removed"); callbacks.appWidgetRemoved(appWidgetId); host.lastWidgetUpdateSequenceNo = requestId; } catch (RemoteException re) { Loading Loading @@ -2286,6 +2292,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyProvidersChanged(Host host, IAppWidgetHost callbacks) { try { Slog.d(TAG, "Trying to notify widget providers changed"); callbacks.providersChanged(); } catch (RemoteException re) { synchronized (mLock) { Loading Loading
core/java/android/widget/RemoteViews.java +107 −37 Original line number Diff line number Diff line Loading @@ -1105,6 +1105,7 @@ public class RemoteViews implements Parcelable, Filter { SetRemoteCollectionItemListAdapterAction(Parcel parcel) { mViewId = parcel.readInt(); mIntentId = parcel.readInt(); mIsReplacedIntoAction = parcel.readBoolean(); mServiceIntent = parcel.readTypedObject(Intent.CREATOR); mItems = mServiceIntent != null ? null Loading @@ -1128,6 +1129,7 @@ public class RemoteViews implements Parcelable, Filter { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mViewId); dest.writeInt(mIntentId); dest.writeBoolean(mIsReplacedIntoAction); dest.writeTypedObject(mServiceIntent, flags); if (mItems != null) { mItems.writeToParcel(dest, flags, /* attached= */ true); Loading Loading @@ -1208,6 +1210,19 @@ public class RemoteViews implements Parcelable, Filter { } } /** * The maximum size for RemoteViews with converted RemoteCollectionItemsAdapter. * When converting RemoteViewsAdapter to RemoteCollectionItemsAdapter, we want to put size * limits on each unique RemoteCollectionItems in order to not exceed the transaction size limit * for each parcel (typically 1 MB). We leave a certain ratio of the maximum size as a buffer * for missing calculations of certain parameters (e.g. writing a RemoteCollectionItems to the * parcel will write its Id array as well, but that is missing when writing itschild RemoteViews * directly to the parcel as we did in RemoteViewsService) * * @hide */ private static final int MAX_SINGLE_PARCEL_SIZE = (int) (1_000_000 * 0.8); /** * @hide */ Loading Loading @@ -1260,17 +1275,47 @@ public class RemoteViews implements Parcelable, Filter { return mUriToCollectionMapping.get(uri); } CompletableFuture<Void> collectAllIntentsNoComplete(@NonNull RemoteViews inViews) { CompletableFuture<Void> collectionFuture = CompletableFuture.completedFuture(null); public @NonNull CompletableFuture<Void> collectAllIntentsNoComplete( @NonNull RemoteViews inViews) { SparseArray<Intent> idToIntentMapping = new SparseArray<>(); // Collect the number of uinque Intent (which is equal to the number of new connections // to make) for size allocation and exclude certain collections from being written to // the parcel to better estimate the space left for reallocation. collectAllIntentsInternal(inViews, idToIntentMapping); // Calculate the individual size here int numOfIntents = idToIntentMapping.size(); if (numOfIntents == 0) { Log.e(LOG_TAG, "Possibly notifying updates for nonexistent view Id"); return CompletableFuture.completedFuture(null); } Parcel sizeTestParcel = Parcel.obtain(); // Write self RemoteViews to the parcel, which includes the actions/bitmaps/collection // cache to see how much space is left for the RemoteCollectionItems that are to be // updated. RemoteViews.this.writeToParcel(sizeTestParcel, /* flags= */ 0, /* intentsToIgnore= */ idToIntentMapping); int remainingSize = MAX_SINGLE_PARCEL_SIZE - sizeTestParcel.dataSize(); sizeTestParcel.recycle(); int individualSize = remainingSize < 0 ? 0 : remainingSize / numOfIntents; return connectAllUniqueIntents(individualSize, idToIntentMapping); } private void collectAllIntentsInternal(@NonNull RemoteViews inViews, @NonNull SparseArray<Intent> idToIntentMapping) { if (inViews.hasSizedRemoteViews()) { for (RemoteViews remoteViews : inViews.mSizedRemoteViews) { collectionFuture = CompletableFuture.allOf(collectionFuture, collectAllIntentsNoComplete(remoteViews)); collectAllIntentsInternal(remoteViews, idToIntentMapping); } } else if (inViews.hasLandscapeAndPortraitLayouts()) { collectionFuture = CompletableFuture.allOf( collectAllIntentsNoComplete(inViews.mLandscape), collectAllIntentsNoComplete(inViews.mPortrait)); collectAllIntentsInternal(inViews.mLandscape, idToIntentMapping); collectAllIntentsInternal(inViews.mPortrait, idToIntentMapping); } else if (inViews.mActions != null) { for (Action action : inViews.mActions) { if (action instanceof SetRemoteCollectionItemListAdapterAction rca) { Loading @@ -1280,13 +1325,16 @@ public class RemoteViews implements Parcelable, Filter { } if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) { final String uri = mIdToUriMapping.get(rca.mIntentId); collectionFuture = CompletableFuture.allOf(collectionFuture, getItemsFutureFromIntentWithTimeout(rca.mServiceIntent) .thenAccept(rc -> { rc.setHierarchyRootData(getHierarchyRootData()); mUriToCollectionMapping.put(uri, rc); })); rca.mIsReplacedIntoAction = false; // Avoid redundant connections for the same intent. Also making sure // that the number of connections we are making is always equal to the // nmuber of unique intents that are being used for the updates. if (idToIntentMapping.contains(rca.mIntentId)) { continue; } idToIntentMapping.put(rca.mIntentId, rca.mServiceIntent); rca.mItems = null; continue; } Loading @@ -1295,7 +1343,7 @@ public class RemoteViews implements Parcelable, Filter { // intents. if (rca.mServiceIntent != null) { final String uri = rca.mServiceIntent.toUri(0); int index = mIdToUriMapping.indexOfValue(uri); int index = mIdToUriMapping.indexOfValueByValue(uri); if (index == -1) { int newIntentId = mIdToUriMapping.size(); rca.mIntentId = newIntentId; Loading @@ -1305,32 +1353,41 @@ public class RemoteViews implements Parcelable, Filter { rca.mItems = null; continue; } collectionFuture = CompletableFuture.allOf(collectionFuture, getItemsFutureFromIntentWithTimeout(rca.mServiceIntent) .thenAccept(rc -> { rc.setHierarchyRootData(getHierarchyRootData()); mUriToCollectionMapping.put(uri, rc); })); idToIntentMapping.put(rca.mIntentId, rca.mServiceIntent); rca.mItems = null; } else { for (RemoteViews views : rca.mItems.mViews) { collectionFuture = CompletableFuture.allOf(collectionFuture, collectAllIntentsNoComplete(views)); collectAllIntentsInternal(views, idToIntentMapping); } } } else if (action instanceof ViewGroupActionAdd vgaa && vgaa.mNestedViews != null) { collectionFuture = CompletableFuture.allOf(collectionFuture, collectAllIntentsNoComplete(vgaa.mNestedViews)); collectAllIntentsInternal(vgaa.mNestedViews, idToIntentMapping); } } } } return collectionFuture; private @NonNull CompletableFuture<Void> connectAllUniqueIntents(int individualSize, @NonNull SparseArray<Intent> idToIntentMapping) { List<CompletableFuture<Void>> intentFutureList = new ArrayList<>(); for (int i = 0; i < idToIntentMapping.size(); i++) { String currentIntentUri = mIdToUriMapping.get(idToIntentMapping.keyAt(i)); Intent currentIntent = idToIntentMapping.valueAt(i); intentFutureList.add(getItemsFutureFromIntentWithTimeout(currentIntent, individualSize) .thenAccept(items -> { items.setHierarchyRootData(getHierarchyRootData()); mUriToCollectionMapping.put(currentIntentUri, items); })); } return CompletableFuture.allOf(intentFutureList.toArray(CompletableFuture[]::new)); } private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout( Intent intent) { Intent intent, int individualSize) { if (intent == null) { Log.e(LOG_TAG, "Null intent received when generating adapter future"); return CompletableFuture.completedFuture(new RemoteCollectionItems Loading @@ -1338,8 +1395,8 @@ public class RemoteViews implements Parcelable, Filter { } final Context context = ActivityThread.currentApplication(); final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>(); final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>(); context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE), result.defaultExecutor(), new ServiceConnection() { @Override Loading @@ -1348,11 +1405,11 @@ public class RemoteViews implements Parcelable, Filter { RemoteCollectionItems items; try { items = IRemoteViewsFactory.Stub.asInterface(iBinder) .getRemoteCollectionItems(); .getRemoteCollectionItems(individualSize); } catch (RemoteException re) { items = new RemoteCollectionItems.Builder().build(); Log.e(LOG_TAG, "Error getting collection items from the factory", re); Log.e(LOG_TAG, "Error getting collection items from the" + " factory", re); } finally { context.unbindService(this); } Loading @@ -1371,10 +1428,17 @@ public class RemoteViews implements Parcelable, Filter { return result; } public void writeToParcel(Parcel out, int flags) { public void writeToParcel(Parcel out, int flags, @Nullable SparseArray<Intent> intentsToIgnore) { out.writeInt(mIdToUriMapping.size()); for (int i = 0; i < mIdToUriMapping.size(); i++) { out.writeInt(mIdToUriMapping.keyAt(i)); int currentIntentId = mIdToUriMapping.keyAt(i); if (intentsToIgnore != null && intentsToIgnore.contains(currentIntentId)) { // Skip writing collections that are to be updated in the following steps to // better estimate the RemoteViews size. continue; } out.writeInt(currentIntentId); String intentUri = mIdToUriMapping.valueAt(i); out.writeString8(intentUri); mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true); Loading Loading @@ -6724,7 +6788,13 @@ public class RemoteViews implements Parcelable, Filter { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { writeToParcel(dest, flags, /* intentsToIgnore= */ null); } private void writeToParcel(Parcel dest, int flags, @Nullable SparseArray<Intent> intentsToIgnore) { boolean prevSquashingAllowed = dest.allowSquashing(); if (!hasMultipleLayouts()) { Loading @@ -6733,7 +6803,7 @@ public class RemoteViews implements Parcelable, Filter { // is shared by all children. if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags, intentsToIgnore); } mApplication.writeToParcel(dest, flags); if (mIsRoot || mIdealSize == null) { Loading @@ -6750,7 +6820,7 @@ public class RemoteViews implements Parcelable, Filter { dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS); if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags, intentsToIgnore); } dest.writeInt(mSizedRemoteViews.size()); for (RemoteViews view : mSizedRemoteViews) { Loading @@ -6762,7 +6832,7 @@ public class RemoteViews implements Parcelable, Filter { // is shared by all children. if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags, intentsToIgnore); } mLandscape.writeToParcel(dest, flags); // Both RemoteViews already share the same package and user Loading
core/java/android/widget/RemoteViewsService.java +15 −10 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.widget; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.Parcel; import com.android.internal.widget.IRemoteViewsFactory; Loading @@ -42,13 +43,6 @@ public abstract class RemoteViewsService extends Service { new HashMap<Intent.FilterComparison, RemoteViewsFactory>(); private static final Object sLock = new Object(); /** * Used for determining the maximum number of entries to retrieve from RemoteViewsFactory * * @hide */ private static final int MAX_NUM_ENTRY = 10; /** * An interface for an adapter between a remote collection view (ListView, GridView, etc) and * the underlying data for that view. The implementor is responsible for making a RemoteView Loading Loading @@ -235,9 +229,10 @@ public abstract class RemoteViewsService extends Service { } @Override public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() { public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) { RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems .Builder().build(); Parcel capSizeTestParcel = Parcel.obtain(); try { RemoteViews.RemoteCollectionItems.Builder itemsBuilder = Loading @@ -245,15 +240,25 @@ public abstract class RemoteViewsService extends Service { mFactory.onDataSetChanged(); itemsBuilder.setHasStableIds(mFactory.hasStableIds()); final int numOfEntries = Math.min(mFactory.getCount(), MAX_NUM_ENTRY); final int numOfEntries = mFactory.getCount(); for (int i = 0; i < numOfEntries; i++) { itemsBuilder.addItem(mFactory.getItemId(i), mFactory.getViewAt(i)); final long currentItemId = mFactory.getItemId(i); final RemoteViews currentView = mFactory.getViewAt(i); currentView.writeToParcel(capSizeTestParcel, 0); if (capSizeTestParcel.dataSize() > capSize) { break; } itemsBuilder.addItem(currentItemId, currentView); } items = itemsBuilder.build(); } catch (Exception ex) { Thread t = Thread.currentThread(); Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex); } finally { // Recycle the parcel capSizeTestParcel.recycle(); } return items; } Loading
core/java/com/android/internal/widget/IRemoteViewsFactory.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -39,6 +39,6 @@ interface IRemoteViewsFactory { boolean hasStableIds(); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) boolean isCreated(); RemoteViews.RemoteCollectionItems getRemoteCollectionItems(); RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize); }
core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -355,7 +355,7 @@ public class RemoteViewsAdapterTest { } @Override public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() { public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) { RemoteViews.RemoteCollectionItems.Builder itemsBuilder = new RemoteViews.RemoteCollectionItems.Builder(); itemsBuilder.setHasStableIds(hasStableIds()) Loading
services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +7 −0 Original line number Diff line number Diff line Loading @@ -2074,6 +2074,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks, int appWidgetId, int viewId, long requestId) { try { Slog.d(TAG, "Trying to notify widget view data changed"); callbacks.viewDataChanged(appWidgetId, viewId); host.lastWidgetUpdateSequenceNo = requestId; } catch (RemoteException re) { Loading Loading @@ -2158,6 +2159,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks, int appWidgetId, RemoteViews views, long requestId) { try { Slog.d(TAG, "Trying to notify widget update for package " + (views == null ? "null" : views.getPackage()) + " with widget id: " + appWidgetId); callbacks.updateAppWidget(appWidgetId, views); host.lastWidgetUpdateSequenceNo = requestId; } catch (RemoteException re) { Loading Loading @@ -2196,6 +2200,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyProviderChanged(Host host, IAppWidgetHost callbacks, int appWidgetId, AppWidgetProviderInfo info, long requestId) { try { Slog.d(TAG, "Trying to notify provider update"); callbacks.providerChanged(appWidgetId, info); host.lastWidgetUpdateSequenceNo = requestId; } catch (RemoteException re) { Loading Loading @@ -2239,6 +2244,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyAppWidgetRemoved(Host host, IAppWidgetHost callbacks, int appWidgetId, long requestId) { try { Slog.d(TAG, "Trying to notify widget removed"); callbacks.appWidgetRemoved(appWidgetId); host.lastWidgetUpdateSequenceNo = requestId; } catch (RemoteException re) { Loading Loading @@ -2286,6 +2292,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private void handleNotifyProvidersChanged(Host host, IAppWidgetHost callbacks) { try { Slog.d(TAG, "Trying to notify widget providers changed"); callbacks.providersChanged(); } catch (RemoteException re) { synchronized (mLock) { Loading