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

Commit 8649ca70 authored by Sihua Ma's avatar Sihua Ma
Browse files

Create a fetcher for converted legacy list widgets

We create a fetcher in AppWidgetHost dedicated for fetching latest
RemoteViews from the service for converted legacy list widgets since
they usually contain a large number of entries and could potentially
cause TransactionTooLargeException, as a result dropping the host
connection in the widget service and blocking future updates.

Test: N/A
Bug: 245950570
Change-Id: I65d5a675b34adff1db6463e513395207d542f766
parent 86444fc8
Loading
Loading
Loading
Loading
+46 −2
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.widget.RemoteViews;
import android.widget.RemoteViews.InteractionHandler;
@@ -52,12 +53,15 @@ import java.util.List;
 */
public class AppWidgetHost {

    private static final String TAG = "AppWidgetHost";

    static final int HANDLE_UPDATE = 1;
    static final int HANDLE_PROVIDER_CHANGED = 2;
    static final int HANDLE_PROVIDERS_CHANGED = 3;
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    static final int HANDLE_VIEW_DATA_CHANGED = 4;
    static final int HANDLE_APP_WIDGET_REMOVED = 5;
    static final int HANDLE_VIEW_UPDATE_DEFERRED = 6;

    final static Object sServiceLock = new Object();
    @UnsupportedAppUsage
@@ -131,6 +135,15 @@ public class AppWidgetHost {
            msg.sendToTarget();
        }

        public void updateAppWidgetDeferred(int appWidgetId) {
            Handler handler = mWeakHandler.get();
            if (handler == null) {
                return;
            }
            Message msg = handler.obtainMessage(HANDLE_VIEW_UPDATE_DEFERRED, appWidgetId, 0, null);
            msg.sendToTarget();
        }

        private static boolean isLocalBinder() {
            return Process.myPid() == Binder.getCallingPid();
        }
@@ -163,6 +176,10 @@ public class AppWidgetHost {
                    viewDataChanged(msg.arg1, msg.arg2);
                    break;
                }
                case HANDLE_VIEW_UPDATE_DEFERRED: {
                    updateAppWidgetDeferred(msg.arg1);
                    break;
                }
            }
        }
    }
@@ -480,13 +497,31 @@ public class AppWidgetHost {
        void onUpdateProviderInfo(@Nullable AppWidgetProviderInfo appWidget);

        /**
         * This function is called when the RemoteViews of the app widget is updated
         * @param views The new RemoteViews to be set for the app widget
         * This function is called when the {@code RemoteViews} of the app widget is updated
         * @param views The new {@code RemoteViews} to be set for the app widget
         *
         * @hide
         */
        void updateAppWidget(@Nullable RemoteViews views);

        /**
         * Called for the listener to handle deferred {@code RemoteViews} updates. Default
         * implementation is to update the widget directly.
         * @param packageName The package name used for uid verification on the service side
         * @param appWidgetId The widget id of the listener
         *
         * @hide
         */
        default void updateAppWidgetDeferred(String packageName, int appWidgetId) {
            RemoteViews latestViews = null;
            try {
                latestViews = sService.getAppWidgetViews(packageName, appWidgetId);
            } catch (Exception e) {
                Log.e(TAG, "updateAppWidgetDeferred: ", e);
            }
            updateAppWidget(latestViews);
        }

        /**
         * This function is called when the view ID is changed for the app widget
         * @param viewId The new view ID to be be set for the widget
@@ -563,6 +598,15 @@ public class AppWidgetHost {
        }
    }

    private void updateAppWidgetDeferred(int appWidgetId) {
        AppWidgetHostListener v = getListener(appWidgetId);
        if (v == null) {
            Log.e(TAG, "updateAppWidgetDeferred: null listener for id: " + appWidgetId);
            return;
        }
        v.updateAppWidgetDeferred(mContextOpPackageName, appWidgetId);
    }

    /**
     * Clear the list of Views that have been created by this AppWidgetHost.
     */
+13 −3
Original line number Diff line number Diff line
@@ -382,7 +382,7 @@ public class RemoteViews implements Parcelable, Filter {
    /**
     * Maps Intent ID to RemoteCollectionItems to avoid duplicate items
     */
    private RemoteCollectionCache mCollectionCache = new RemoteCollectionCache();
    private @NonNull RemoteCollectionCache mCollectionCache = new RemoteCollectionCache();

    /** Cache of ApplicationInfos used by collection items. */
    private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache();
@@ -774,6 +774,16 @@ public class RemoteViews implements Parcelable, Filter {
        reconstructCaches();
    }

    /**
     * Return {@code true} only if this {@code RemoteViews} is a legacy list widget that uses
     * {@code Intent} for inflating child entries.
     *
     * @hide
     */
    public boolean isLegacyListRemoteViews() {
        return mCollectionCache.mIdToUriMapping.size() > 0;
    }

    /**
     * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission
     * grants will need to be issued to ensure the recipient of this object is able to render its
@@ -1231,8 +1241,8 @@ public class RemoteViews implements Parcelable, Filter {
    }

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

        RemoteCollectionCache() { }

+1 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.widget.RemoteViews;

/** {@hide} */
oneway interface IAppWidgetHost {
    void updateAppWidgetDeferred(int appWidgetId);
    void updateAppWidget(int appWidgetId, in RemoteViews views);
    void providerChanged(int appWidgetId, in AppWidgetProviderInfo info);
    void providersChanged();
+33 −0
Original line number Diff line number Diff line
@@ -2292,11 +2292,32 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        args.arg4 = requestId;
        args.argi1 = widget.appWidgetId;

        if (updateViews.isLegacyListRemoteViews()) {
            mCallbackHandler.obtainMessage(
                    CallbackHandler.MSG_NOTIFY_UPDATE_APP_WIDGET_DEFERRED,
                    args).sendToTarget();
            return;
        }

        mCallbackHandler.obtainMessage(
                CallbackHandler.MSG_NOTIFY_UPDATE_APP_WIDGET,
                args).sendToTarget();
    }

    private void handleNotifyUpdateAppWidgetDeferred(Host host, IAppWidgetHost callbacks,
            int appWidgetId, long requestId) {
        try {
            Slog.d(TAG, "Trying to notify widget update deferred for id: " + appWidgetId);
            callbacks.updateAppWidgetDeferred(appWidgetId);
            host.lastWidgetUpdateSequenceNo = requestId;
        } catch (RemoteException re) {
            synchronized (mLock) {
                Slog.e(TAG, "Widget host dead: " + host.id, re);
                host.callbacks = null;
            }
        }
    }

    private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks,
            int appWidgetId, RemoteViews views, long requestId) {
        try {
@@ -4277,6 +4298,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        public static final int MSG_NOTIFY_PROVIDERS_CHANGED = 3;
        public static final int MSG_NOTIFY_VIEW_DATA_CHANGED = 4;
        public static final int MSG_NOTIFY_APP_WIDGET_REMOVED = 5;
        public static final int MSG_NOTIFY_UPDATE_APP_WIDGET_DEFERRED = 6;

        public CallbackHandler(Looper looper) {
            super(looper, null, false);
@@ -4340,6 +4362,17 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
                    handleNotifyAppWidgetViewDataChanged(host, callbacks, appWidgetId, viewId,
                            requestId);
                } break;

                case MSG_NOTIFY_UPDATE_APP_WIDGET_DEFERRED: {
                    SomeArgs args = (SomeArgs) message.obj;
                    Host host = (Host) args.arg1;
                    IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
                    long requestId = (Long) args.arg4;
                    final int appWidgetId = args.argi1;
                    args.recycle();

                    handleNotifyUpdateAppWidgetDeferred(host, callbacks, appWidgetId, requestId);
                } break;
            }
        }
    }