Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7634fdc4 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Connnect to widget service only in AppWidgetManager" into main

parents e3be17ae ca2a4689
Loading
Loading
Loading
Loading
+42 −79
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ import android.widget.RemoteViews;

import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FunctionalUtils;

import java.util.ArrayList;
import java.util.Collections;
@@ -562,6 +563,40 @@ public class AppWidgetManager {
        });
    }

    private void tryAdapterConversion(
            FunctionalUtils.RemoteExceptionIgnoringConsumer<RemoteViews> action,
            RemoteViews original, String failureMsg) {
        final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
                && (mHasPostedLegacyLists = mHasPostedLegacyLists
                        || (original != null && original.hasLegacyLists()));

        if (isConvertingAdapter) {
            final RemoteViews viewsCopy = new RemoteViews(original);
            Runnable updateWidgetWithTask = () -> {
                try {
                    viewsCopy.collectAllIntents().get();
                    action.acceptOrThrow(viewsCopy);
                } catch (Exception e) {
                    Log.e(TAG, failureMsg, e);
                }
            };

            if (Looper.getMainLooper() == Looper.myLooper()) {
                createUpdateExecutorIfNull().execute(updateWidgetWithTask);
                return;
            }

            updateWidgetWithTask.run();
            return;
        }

        try {
            action.acceptOrThrow(original);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }

    /**
     * Set the RemoteViews to use for the specified appWidgetIds.
     * <p>
@@ -586,32 +621,8 @@ public class AppWidgetManager {
            return;
        }

        final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
                && (mHasPostedLegacyLists = mHasPostedLegacyLists
                        || (views != null && views.hasLegacyLists()));

        if (isConvertingAdapter) {
            views.collectAllIntents();

            if (Looper.getMainLooper() == Looper.myLooper()) {
                RemoteViews viewsCopy = new RemoteViews(views);
                createUpdateExecutorIfNull().execute(() -> {
                    try {
                        mService.updateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Error updating app widget views in background", e);
                    }
                });

                return;
            }
        }

        try {
            mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        tryAdapterConversion(view -> mService.updateAppWidgetIds(mPackageName, appWidgetIds,
                view), views, "Error updating app widget views in background");
    }

    /**
@@ -716,32 +727,9 @@ public class AppWidgetManager {
            return;
        }

        final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
                && (mHasPostedLegacyLists = mHasPostedLegacyLists
                        || (views != null && views.hasLegacyLists()));

        if (isConvertingAdapter) {
            views.collectAllIntents();

            if (Looper.getMainLooper() == Looper.myLooper()) {
                RemoteViews viewsCopy = new RemoteViews(views);
                createUpdateExecutorIfNull().execute(() -> {
                    try {
                        mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Error partially updating app widget views in background", e);
                    }
                });

                return;
            }
        }

        try {
            mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        tryAdapterConversion(view -> mService.partiallyUpdateAppWidgetIds(mPackageName,
                appWidgetIds, view), views,
                "Error partially updating app widget views in background");
    }

    /**
@@ -793,33 +781,8 @@ public class AppWidgetManager {
            return;
        }

        final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
                && (mHasPostedLegacyLists = mHasPostedLegacyLists
                        || (views != null && views.hasLegacyLists()));

        if (isConvertingAdapter) {
            views.collectAllIntents();

            if (Looper.getMainLooper() == Looper.myLooper()) {
                RemoteViews viewsCopy = new RemoteViews(views);
                createUpdateExecutorIfNull().execute(() -> {
                    try {
                        mService.updateAppWidgetProvider(provider, viewsCopy);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Error updating app widget view using provider in background",
                                e);
                    }
                });

                return;
            }
        }

        try {
            mService.updateAppWidgetProvider(provider, views);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        tryAdapterConversion(view -> mService.updateAppWidgetProvider(provider, view), views,
                "Error updating app widget view using provider in background");
    }

    /**
+102 −117
Original line number Diff line number Diff line
@@ -1054,8 +1054,7 @@ public class RemoteViews implements Parcelable, Filter {
    }

    private class SetRemoteCollectionItemListAdapterAction extends Action {
        @NonNull
        private CompletableFuture<RemoteCollectionItems> mItemsFuture;
        private @Nullable RemoteCollectionItems mItems;
        final Intent mServiceIntent;
        int mIntentId = -1;
        boolean mIsReplacedIntoAction = false;
@@ -1064,92 +1063,46 @@ public class RemoteViews implements Parcelable, Filter {
                @NonNull RemoteCollectionItems items) {
            mViewId = id;
            items.setHierarchyRootData(getHierarchyRootData());
            mItemsFuture = CompletableFuture.completedFuture(items);
            mItems = items;
            mServiceIntent = null;
        }

        SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) {
            mViewId = id;
            mItemsFuture = getItemsFutureFromIntentWithTimeout(intent);
            setHierarchyRootData(getHierarchyRootData());
            mItems = null;
            mServiceIntent = intent;
        }

        private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
                Intent intent) {
            if (intent == null) {
                Log.e(LOG_TAG, "Null intent received when generating adapter future");
                return CompletableFuture.completedFuture(new RemoteCollectionItems
                        .Builder().build());
            }

            final Context context = ActivityThread.currentApplication();
            final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();

            context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
                    result.defaultExecutor(), new ServiceConnection() {
                        @Override
                        public void onServiceConnected(ComponentName componentName,
                                IBinder iBinder) {
                            RemoteCollectionItems items;
                            try {
                                items = IRemoteViewsFactory.Stub.asInterface(iBinder)
                                        .getRemoteCollectionItems();
                            } catch (RemoteException re) {
                                items = new RemoteCollectionItems.Builder().build();
                                Log.e(LOG_TAG, "Error getting collection items from the factory",
                                        re);
                            } finally {
                                context.unbindService(this);
                            }

                            result.complete(items);
                        }

                        @Override
                        public void onServiceDisconnected(ComponentName componentName) { }
                    });

            result.completeOnTimeout(
                    new RemoteCollectionItems.Builder().build(),
                    MAX_ADAPTER_CONVERSION_WAITING_TIME_MS, TimeUnit.MILLISECONDS);

            return result;
        }

        SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
            mViewId = parcel.readInt();
            mIntentId = parcel.readInt();
            mItemsFuture = CompletableFuture.completedFuture(mIntentId != -1
                    ? null
                    : new RemoteCollectionItems(parcel, getHierarchyRootData()));
            mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
            mItems = mServiceIntent != null
                    ? null
                    : new RemoteCollectionItems(parcel, getHierarchyRootData());
        }

        @Override
        public void setHierarchyRootData(HierarchyRootData rootData) {
            if (mIntentId == -1) {
                mItemsFuture = mItemsFuture
                        .thenApply(rc -> {
                            rc.setHierarchyRootData(rootData);
                            return rc;
                        });
            if (mItems != null) {
                mItems.setHierarchyRootData(rootData);
                return;
            }

            if (mIntentId != -1) {
                // Set the root data for items in the cache instead
                mCollectionCache.setHierarchyDataForId(mIntentId, rootData);
            }
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(mViewId);
            dest.writeInt(mIntentId);
            if (mIntentId == -1) {
                RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
                items.writeToParcel(dest, flags, /* attached= */ true);
            }
            dest.writeTypedObject(mServiceIntent, flags);
            if (mItems != null) {
                mItems.writeToParcel(dest, flags, /* attached= */ true);
            }
        }

        @Override
@@ -1159,7 +1112,9 @@ public class RemoteViews implements Parcelable, Filter {
            if (target == null) return;

            RemoteCollectionItems items = mIntentId == -1
                    ? getCollectionItemsFromFuture(mItemsFuture)
                    ? mItems == null
                            ? new RemoteCollectionItems.Builder().build()
                            : mItems
                    : mCollectionCache.getItemsForId(mIntentId);

            // Ensure that we are applying to an AppWidget root
@@ -1216,53 +1171,34 @@ public class RemoteViews implements Parcelable, Filter {

        @Override
        public void visitUris(@NonNull Consumer<Uri> visitor) {
            RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
            items.visitUris(visitor);
        }
            if (mIntentId != -1 || mItems == null) {
                return;
            }

    private static RemoteCollectionItems getCollectionItemsFromFuture(
            CompletableFuture<RemoteCollectionItems> itemsFuture) {
        RemoteCollectionItems items;
        try {
            items = itemsFuture.get();
        } catch (Exception e) {
            Log.e(LOG_TAG, "Error getting collection items from future", e);
            items = new RemoteCollectionItems.Builder().build();
            mItems.visitUris(visitor);
        }

        return items;
    }

    /**
     * @hide
     */
    public void collectAllIntents() {
        mCollectionCache.collectAllIntentsNoComplete(this);
    public CompletableFuture<Void> collectAllIntents() {
        return mCollectionCache.collectAllIntentsNoComplete(this);
    }

    private class RemoteCollectionCache {
        private SparseArray<String> mIdToUriMapping = new SparseArray<>();
        private HashMap<String, RemoteCollectionItems> mUriToCollectionMapping = new HashMap<>();

        // We don't put this into the parcel
        private HashMap<String, CompletableFuture<RemoteCollectionItems>> mTempUriToFutureMapping =
                new HashMap<>();

        RemoteCollectionCache() { }

        RemoteCollectionCache(RemoteCollectionCache src) {
            boolean isWaitingCache = src.mTempUriToFutureMapping.size() != 0;
            for (int i = 0; i < src.mIdToUriMapping.size(); i++) {
                String uri = src.mIdToUriMapping.valueAt(i);
                mIdToUriMapping.put(src.mIdToUriMapping.keyAt(i), uri);
                if (isWaitingCache) {
                    mTempUriToFutureMapping.put(uri, src.mTempUriToFutureMapping.get(uri));
                } else {
                mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri));
            }
        }
        }

        RemoteCollectionCache(Parcel in) {
            int cacheSize = in.readInt();
@@ -1281,14 +1217,8 @@ public class RemoteViews implements Parcelable, Filter {

        void setHierarchyDataForId(int intentId, HierarchyRootData data) {
            String uri = mIdToUriMapping.get(intentId);
            if (mTempUriToFutureMapping.get(uri) != null) {
                CompletableFuture<RemoteCollectionItems> itemsFuture =
                        mTempUriToFutureMapping.get(uri);
                mTempUriToFutureMapping.put(uri, itemsFuture.thenApply(rc -> {
                    rc.setHierarchyRootData(data);
                    return rc;
                }));

            if (mUriToCollectionMapping.get(uri) == null) {
                Log.e(LOG_TAG, "Error setting hierarchy data for id=" + intentId);
                return;
            }

@@ -1301,14 +1231,17 @@ public class RemoteViews implements Parcelable, Filter {
            return mUriToCollectionMapping.get(uri);
        }

        void collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
        CompletableFuture<Void> collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
            CompletableFuture<Void> collectionFuture = CompletableFuture.completedFuture(null);
            if (inViews.hasSizedRemoteViews()) {
                for (RemoteViews remoteViews : inViews.mSizedRemoteViews) {
                    remoteViews.collectAllIntents();
                    collectionFuture = CompletableFuture.allOf(collectionFuture,
                            collectAllIntentsNoComplete(remoteViews));
                }
            } else if (inViews.hasLandscapeAndPortraitLayouts()) {
                inViews.mLandscape.collectAllIntents();
                inViews.mPortrait.collectAllIntents();
                collectionFuture = CompletableFuture.allOf(
                        collectAllIntentsNoComplete(inViews.mLandscape),
                        collectAllIntentsNoComplete(inViews.mPortrait));
            } else if (inViews.mActions != null) {
                for (Action action : inViews.mActions) {
                    if (action instanceof SetRemoteCollectionItemListAdapterAction rca) {
@@ -1318,40 +1251,95 @@ public class RemoteViews implements Parcelable, Filter {
                        }

                        if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) {
                            String uri = mIdToUriMapping.get(rca.mIntentId);
                            mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
                            rca.mItemsFuture = CompletableFuture.completedFuture(null);
                            final String uri = mIdToUriMapping.get(rca.mIntentId);
                            collectionFuture = CompletableFuture.allOf(collectionFuture,
                                    getItemsFutureFromIntentWithTimeout(rca.mServiceIntent)
                                            .thenAccept(rc -> {
                                                rc.setHierarchyRootData(getHierarchyRootData());
                                                mUriToCollectionMapping.put(uri, rc);
                                            }));
                            rca.mItems = null;
                            continue;
                        }

                        // Differentiate between the normal collection actions and the ones with
                        // intents.
                        if (rca.mServiceIntent != null) {
                            String uri = rca.mServiceIntent.toUri(0);
                            final String uri = rca.mServiceIntent.toUri(0);
                            int index = mIdToUriMapping.indexOfValue(uri);
                            if (index == -1) {
                                int newIntentId = mIdToUriMapping.size();
                                rca.mIntentId = newIntentId;
                                mIdToUriMapping.put(newIntentId, uri);
                                // mUriToIntentMapping.put(uri, mServiceIntent);
                                mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
                            } else {
                                rca.mIntentId = mIdToUriMapping.keyAt(index);
                                rca.mItems = null;
                                continue;
                            }
                            rca.mItemsFuture = CompletableFuture.completedFuture(null);
                            collectionFuture = CompletableFuture.allOf(collectionFuture,
                                    getItemsFutureFromIntentWithTimeout(rca.mServiceIntent)
                                            .thenAccept(rc -> {
                                                rc.setHierarchyRootData(getHierarchyRootData());
                                                mUriToCollectionMapping.put(uri, rc);
                                            }));
                            rca.mItems = null;
                        } else {
                            RemoteCollectionItems items = getCollectionItemsFromFuture(
                                    rca.mItemsFuture);
                            for (RemoteViews views : items.mViews) {
                                views.collectAllIntents();
                            for (RemoteViews views : rca.mItems.mViews) {
                                collectionFuture = CompletableFuture.allOf(collectionFuture,
                                        collectAllIntentsNoComplete(views));
                            }
                        }
                    } else if (action instanceof ViewGroupActionAdd vgaa
                            && vgaa.mNestedViews != null) {
                        vgaa.mNestedViews.collectAllIntents();
                        collectionFuture = CompletableFuture.allOf(collectionFuture,
                                collectAllIntentsNoComplete(vgaa.mNestedViews));
                    }
                }
            }

            return collectionFuture;
        }

        private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
                Intent intent) {
            if (intent == null) {
                Log.e(LOG_TAG, "Null intent received when generating adapter future");
                return CompletableFuture.completedFuture(new RemoteCollectionItems
                    .Builder().build());
            }

            final Context context = ActivityThread.currentApplication();
            final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();

            context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
                    result.defaultExecutor(), new ServiceConnection() {
                        @Override
                        public void onServiceConnected(ComponentName componentName,
                                IBinder iBinder) {
                            RemoteCollectionItems items;
                            try {
                                items = IRemoteViewsFactory.Stub.asInterface(iBinder)
                                    .getRemoteCollectionItems();
                            } catch (RemoteException re) {
                                items = new RemoteCollectionItems.Builder().build();
                                Log.e(LOG_TAG, "Error getting collection items from the factory",
                                        re);
                            } finally {
                                context.unbindService(this);
                            }

                            result.complete(items);
                        }

                        @Override
                        public void onServiceDisconnected(ComponentName componentName) { }
                    });

            result.completeOnTimeout(
                    new RemoteCollectionItems.Builder().build(),
                    MAX_ADAPTER_CONVERSION_WAITING_TIME_MS, TimeUnit.MILLISECONDS);

            return result;
        }

        public void writeToParcel(Parcel out, int flags) {
@@ -1360,10 +1348,7 @@ public class RemoteViews implements Parcelable, Filter {
                out.writeInt(mIdToUriMapping.keyAt(i));
                String intentUri = mIdToUriMapping.valueAt(i);
                out.writeString8(intentUri);
                RemoteCollectionItems items = mTempUriToFutureMapping.get(intentUri) != null
                        ? getCollectionItemsFromFuture(mTempUriToFutureMapping.get(intentUri))
                        : mUriToCollectionMapping.get(intentUri);
                items.writeToParcel(out, flags, true);
                mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true);
            }
        }
    }