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

Commit 6d1b41d4 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Maintaining diff for all widget operations instead of just the Views update

When the widget starts lisnening again, it receives all the pending updates
until that point (restricted to the requested widgetIds). When startListening
is called for the first time, the widgetIds is empty and it does not get
any updated. Further calls to startlisting will give the missed updates
for the bound widgets

Bug: 23892701
Change-Id: I3aa06d3e33a0861c19cfd5ced567d5bb3b93d906
parent 27f5e091
Loading
Loading
Loading
Loading
+15 −7
Original line number Diff line number Diff line
@@ -34,7 +34,6 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.SparseArray;
import android.util.TypedValue;
@@ -187,19 +186,28 @@ public class AppWidgetHost {
                idsToUpdate[i] = mViews.keyAt(i);
            }
        }
        List<RemoteViews> updatedViews;
        int[] updatedIds = new int[idsToUpdate.length];
        List<PendingHostUpdate> updates;
        try {
            updatedViews = sService.startListening(
                    mCallbacks, mContextOpPackageName, mHostId, idsToUpdate, updatedIds).getList();
            updates = sService.startListening(
                    mCallbacks, mContextOpPackageName, mHostId, idsToUpdate).getList();
        }
        catch (RemoteException e) {
            throw new RuntimeException("system server dead?", e);
        }

        int N = updatedViews.size();
        int N = updates.size();
        for (int i = 0; i < N; i++) {
            updateAppWidgetView(updatedIds[i], updatedViews.get(i));
            PendingHostUpdate update = updates.get(i);
            switch (update.type) {
                case PendingHostUpdate.TYPE_VIEWS_UPDATE:
                    updateAppWidgetView(update.appWidgetId, update.views);
                    break;
                case PendingHostUpdate.TYPE_PROVIDER_CHANGED:
                    onProviderChanged(update.appWidgetId, update.widgetInfo);
                    break;
                case PendingHostUpdate.TYPE_VIEW_DATA_CHANGED:
                    viewDataChanged(update.appWidgetId, update.viewId);
            }
        }
    }

+125 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package android.appwidget;

import android.os.Parcel;
import android.os.Parcelable;
import android.widget.RemoteViews;

/**
 * @hide
 */
public class PendingHostUpdate implements Parcelable {

    static final int TYPE_VIEWS_UPDATE = 0;
    static final int TYPE_PROVIDER_CHANGED = 1;
    static final int TYPE_VIEW_DATA_CHANGED = 2;

    final int appWidgetId;
    final int type;
    RemoteViews views;
    AppWidgetProviderInfo widgetInfo;
    int viewId;

    public static PendingHostUpdate updateAppWidget(int appWidgetId, RemoteViews views) {
        PendingHostUpdate update = new PendingHostUpdate(appWidgetId, TYPE_VIEWS_UPDATE);
        update.views = views;
        return update;
    }

    public static PendingHostUpdate providerChanged(int appWidgetId, AppWidgetProviderInfo info) {
        PendingHostUpdate update = new PendingHostUpdate(appWidgetId, TYPE_PROVIDER_CHANGED);
        update.widgetInfo = info;
        return update;
    }

    public static PendingHostUpdate viewDataChanged(int appWidgetId, int viewId) {
        PendingHostUpdate update = new PendingHostUpdate(appWidgetId, TYPE_VIEW_DATA_CHANGED);
        update.viewId = viewId;
        return update;
    }

    private PendingHostUpdate(int appWidgetId, int type) {
        this.appWidgetId = appWidgetId;
        this.type = type;
    }

    private PendingHostUpdate(Parcel in) {
        appWidgetId = in.readInt();
        type = in.readInt();

        switch (type) {
            case TYPE_VIEWS_UPDATE:
                if (0 != in.readInt()) {
                    views = new RemoteViews(in);
                }
                break;
            case TYPE_PROVIDER_CHANGED:
                if (0 != in.readInt()) {
                    widgetInfo = new AppWidgetProviderInfo(in);
                }
                break;
            case TYPE_VIEW_DATA_CHANGED:
                viewId = in.readInt();
        }
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(appWidgetId);
        dest.writeInt(type);
        switch (type) {
            case TYPE_VIEWS_UPDATE:
                writeNullParcelable(views, dest, flags);
                break;
            case TYPE_PROVIDER_CHANGED:
                writeNullParcelable(widgetInfo, dest, flags);
                break;
            case TYPE_VIEW_DATA_CHANGED:
                dest.writeInt(viewId);
                break;
        }
    }

    private void writeNullParcelable(Parcelable p, Parcel dest, int flags) {
        if (p != null) {
            dest.writeInt(1);
            p.writeToParcel(dest, flags);
        } else {
            dest.writeInt(0);
        }
    }

    /**
     * Parcelable.Creator that instantiates PendingHostUpdate objects
     */
    public static final Parcelable.Creator<PendingHostUpdate> CREATOR
            = new Parcelable.Creator<PendingHostUpdate>() {
        public PendingHostUpdate createFromParcel(Parcel parcel) {
            return new PendingHostUpdate(parcel);
        }

        public PendingHostUpdate[] newArray(int size) {
            return new PendingHostUpdate[size];
        }
    };
}
+1 −1
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ interface IAppWidgetService {
    // for AppWidgetHost
    //
    ParceledListSlice startListening(IAppWidgetHost host, String callingPackage, int hostId,
            in int[] appWidgetIds, out int[] updatedIds);
            in int[] appWidgetIds);
    void stopListening(String callingPackage, int hostId);
    int allocateAppWidgetId(String callingPackage, int hostId);
    void deleteAppWidgetId(String callingPackage, int appWidgetId);
+76 −24
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicyManagerInternal.OnCrossProfileWidgetProvidersChangeListener;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.appwidget.PendingHostUpdate;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -70,10 +71,12 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.AttributeSet;
import android.util.LongSparseArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TypedValue;
import android.util.Xml;
import android.view.Display;
@@ -745,8 +748,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
    }

    @Override
    public ParceledListSlice<RemoteViews> startListening(IAppWidgetHost callbacks,
            String callingPackage, int hostId, int[] appWidgetIds, int[] updatedIds) {
    public ParceledListSlice<PendingHostUpdate> startListening(IAppWidgetHost callbacks,
            String callingPackage, int hostId, int[] appWidgetIds) {
        final int userId = UserHandle.getCallingUserId();

        if (DEBUG) {
@@ -766,18 +769,19 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
            host.callbacks = callbacks;

            int N = appWidgetIds.length;
            ArrayList<RemoteViews> outViews = new ArrayList<>(N);
            RemoteViews rv;
            int added = 0;
            ArrayList<PendingHostUpdate> outUpdates = new ArrayList<>(N);

            LongSparseArray<PendingHostUpdate> updatesMap = new LongSparseArray<>();
            for (int i = 0; i < N; i++) {
                rv = host.getPendingViewsForId(appWidgetIds[i]);
                if (rv != null) {
                    updatedIds[added] = appWidgetIds[i];
                    outViews.add(rv);
                    added++;
                if (host.getPendingUpdatesForId(appWidgetIds[i], updatesMap)) {
                    // We key the updates based on time, so that the values are sorted by time.
                    int M = updatesMap.size();
                    for (int j = 0; j < M; j++) {
                        outUpdates.add(updatesMap.valueAt(j));
                    }
                }
            return new ParceledListSlice<>(outViews);
            }
            return new ParceledListSlice<>(outUpdates);
        }
    }

@@ -1817,6 +1821,15 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
    }

    private void scheduleNotifyAppWidgetViewDataChanged(Widget widget, int viewId) {
        if (viewId == ID_VIEWS_UPDATE || viewId == ID_PROVIDER_CHANGED) {
            // A view id should never collide with these constants but a developer can call this
            // method with a wrong id. In that case, ignore the call.
            return;
        }
        long requestTime = SystemClock.uptimeMillis();
        if (widget != null) {
            widget.updateTimes.put(viewId, requestTime);
        }
        if (widget == null || widget.host == null || widget.host.zombie
                || widget.host.callbacks == null || widget.provider == null
                || widget.provider.zombie) {
@@ -1826,6 +1839,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = widget.host;
        args.arg2 = widget.host.callbacks;
        args.arg3 = requestTime;
        args.argi1 = widget.appWidgetId;
        args.argi2 = viewId;

@@ -1836,9 +1850,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku


    private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks,
            int appWidgetId, int viewId) {
            int appWidgetId, int viewId, long requestTime) {
        try {
            callbacks.viewDataChanged(appWidgetId, viewId);
            host.lastWidgetUpdateTime = requestTime;
        } catch (RemoteException re) {
            // It failed; remove the callback. No need to prune because
            // we know that this host is still referenced by this instance.
@@ -1887,7 +1902,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
    private void scheduleNotifyUpdateAppWidgetLocked(Widget widget, RemoteViews updateViews) {
        long requestTime = SystemClock.uptimeMillis();
        if (widget != null) {
            widget.lastUpdateTime = requestTime;
            widget.updateTimes.put(ID_VIEWS_UPDATE, requestTime);
        }
        if (widget == null || widget.provider == null || widget.provider.zombie
                || widget.host.callbacks == null || widget.host.zombie) {
@@ -1920,6 +1935,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
    }

    private void scheduleNotifyProviderChangedLocked(Widget widget) {
        long requestTime = SystemClock.uptimeMillis();
        if (widget != null) {
            // When the provider changes, reset everything else.
            widget.updateTimes.clear();
            widget.updateTimes.append(ID_PROVIDER_CHANGED, requestTime);
        }
        if (widget == null || widget.provider == null || widget.provider.zombie
                || widget.host.callbacks == null || widget.host.zombie) {
            return;
@@ -1929,6 +1950,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        args.arg1 = widget.host;
        args.arg2 = widget.host.callbacks;
        args.arg3 = widget.provider.info;
        args.arg4 = requestTime;
        args.argi1 = widget.appWidgetId;

        mCallbackHandler.obtainMessage(
@@ -1937,9 +1959,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
    }

    private void handleNotifyProviderChanged(Host host, IAppWidgetHost callbacks,
            int appWidgetId, AppWidgetProviderInfo info) {
            int appWidgetId, AppWidgetProviderInfo info, long requestTime) {
        try {
            callbacks.providerChanged(appWidgetId, info);
            host.lastWidgetUpdateTime = requestTime;
        } catch (RemoteException re) {
            synchronized (mLock){
                Slog.e(TAG, "Widget host dead: " + host.id, re);
@@ -3423,10 +3446,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
                    Host host = (Host) args.arg1;
                    IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
                    AppWidgetProviderInfo info = (AppWidgetProviderInfo)args.arg3;
                    long requestTime = (Long) args.arg4;
                    final int appWidgetId = args.argi1;
                    args.recycle();

                    handleNotifyProviderChanged(host, callbacks, appWidgetId, info);
                    handleNotifyProviderChanged(host, callbacks, appWidgetId, info, requestTime);
                } break;

                case MSG_NOTIFY_PROVIDERS_CHANGED: {
@@ -3442,11 +3466,13 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
                    SomeArgs args = (SomeArgs) message.obj;
                    Host host = (Host) args.arg1;
                    IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
                    long requestTime = (Long) args.arg3;
                    final int appWidgetId = args.argi1;
                    final int viewId = args.argi2;
                    args.recycle();

                    handleNotifyAppWidgetViewDataChanged(host, callbacks, appWidgetId, viewId);
                    handleNotifyAppWidgetViewDataChanged(host, callbacks, appWidgetId, viewId,
                            requestTime);
                } break;
            }
        }
@@ -3785,20 +3811,41 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        }

        /**
         * Returns the RemoveViews for the provided widget id if an update is pending
         * for that widget.
         * Adds all pending updates in {@param outUpdates} keys by the update time.
         */
        public RemoteViews getPendingViewsForId(int appWidgetId) {
        public boolean getPendingUpdatesForId(int appWidgetId,
                LongSparseArray<PendingHostUpdate> outUpdates) {
            long updateTime = lastWidgetUpdateTime;
            int N = widgets.size();
            for (int i = 0; i < N; i++) {
                Widget widget = widgets.get(i);
                if (widget.appWidgetId == appWidgetId
                        && widget.lastUpdateTime > updateTime) {
                    return cloneIfLocalBinder(widget.getEffectiveViewsLocked());
                if (widget.appWidgetId == appWidgetId) {
                    outUpdates.clear();
                    for (int j = widget.updateTimes.size() - 1; j >= 0; j--) {
                        long time = widget.updateTimes.valueAt(j);
                        if (time <= updateTime) {
                            continue;
                        }
                        int id = widget.updateTimes.keyAt(j);
                        final PendingHostUpdate update;
                        switch (id) {
                            case ID_PROVIDER_CHANGED:
                                update = PendingHostUpdate.providerChanged(
                                        appWidgetId, widget.provider.info);
                                break;
                            case ID_VIEWS_UPDATE:
                                update = PendingHostUpdate.updateAppWidget(appWidgetId,
                                        cloneIfLocalBinder(widget.getEffectiveViewsLocked()));
                                break;
                            default:
                                update = PendingHostUpdate.viewDataChanged(appWidgetId, id);
                        }
            return null;
                        outUpdates.put(time, update);
                    }
                    return true;
                }
            }
            return false;
        }

        @Override
@@ -3863,6 +3910,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        }
    }

    // These can be any constants that would not collide with a resource id.
    private static final int ID_VIEWS_UPDATE = 0;
    private static final int ID_PROVIDER_CHANGED = 1;

    private static final class Widget {
        int appWidgetId;
        int restoredId;  // tracking & remapping any restored state
@@ -3871,7 +3922,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        RemoteViews maskedViews;
        Bundle options;
        Host host;
        long lastUpdateTime;
        // timestamps for various operations
        SparseLongArray updateTimes = new SparseLongArray(2);

        @Override
        public String toString() {