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

Commit 84bbb020 authored by Winson Chung's avatar Winson Chung
Browse files

Fixing issue where RemoteViewsService.onDestroy() was never being called.

Change-Id: I15ad04eee18bc3e09d4846c7f2f2d2d8d168e518
parent e4ce7376
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;

import android.appwidget.AppWidgetManager;
import android.content.Context;
@@ -33,8 +32,8 @@ import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;

import com.android.internal.widget.IRemoteViewsAdapterConnection;
import com.android.internal.widget.IRemoteViewsFactory;
+16 −8
Original line number Diff line number Diff line
@@ -21,8 +21,6 @@ import java.util.HashMap;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import android.util.Pair;

import com.android.internal.widget.IRemoteViewsFactory;

@@ -40,9 +38,9 @@ public abstract class RemoteViewsService extends Service {
    // reclaimed), the references to the factories that are created need to be stored and used when
    // the service is restarted (in response to user input for example).  When the process is
    // destroyed, so is this static cache of RemoteViewsFactories.
    private static final HashMap<Intent.FilterComparison, RemoteViewsFactory> mRemoteViewFactories =
    private static final HashMap<Intent.FilterComparison, RemoteViewsFactory> sRemoteViewFactories =
            new HashMap<Intent.FilterComparison, RemoteViewsFactory>();
    private final Object mLock = new Object();
    private static final Object sLock = new Object();

    /**
     * An interface for an adapter between a remote collection view (ListView, GridView, etc) and
@@ -162,6 +160,16 @@ public abstract class RemoteViewsService extends Service {
        public synchronized boolean hasStableIds() {
            return mFactory.hasStableIds();
        }
        public void onDestroy(Intent intent) {
            synchronized (sLock) {
                Intent.FilterComparison fc = new Intent.FilterComparison(intent);
                if (RemoteViewsService.sRemoteViewFactories.containsKey(fc)) {
                    RemoteViewsFactory factory = RemoteViewsService.sRemoteViewFactories.get(fc);
                    factory.onDestroy();
                    RemoteViewsService.sRemoteViewFactories.remove(fc);
                }
            }
        }

        private RemoteViewsFactory mFactory;
        private boolean mIsCreated;
@@ -169,17 +177,17 @@ public abstract class RemoteViewsService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        synchronized (mLock) {
        synchronized (sLock) {
            Intent.FilterComparison fc = new Intent.FilterComparison(intent);
            RemoteViewsFactory factory = null;
            boolean isCreated = false;
            if (!mRemoteViewFactories.containsKey(fc)) {
            if (!sRemoteViewFactories.containsKey(fc)) {
                factory = onGetViewFactory(intent);
                mRemoteViewFactories.put(fc, factory);
                sRemoteViewFactories.put(fc, factory);
                factory.onCreate();
                isCreated = false;
            } else {
                factory = mRemoteViewFactories.get(fc);
                factory = sRemoteViewFactories.get(fc);
                isCreated = true;
            }
            return new RemoteViewsFactoryAdapter(factory, isCreated);
+2 −0
Original line number Diff line number Diff line
@@ -16,11 +16,13 @@

package com.android.internal.widget;

import android.content.Intent;
import android.widget.RemoteViews;

/** {@hide} */
interface IRemoteViewsFactory {
    void onDataSetChanged();
    void onDestroy(in Intent intent);
    int getCount();
    RemoteViews getViewAt(int position);
    RemoteViews getLoadingView();
+81 −5
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -57,7 +56,6 @@ import android.content.res.XmlResourceParser;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
@@ -69,11 +67,13 @@ import android.util.Slog;
import android.util.TypedValue;
import android.util.Xml;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;

import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsAdapterConnection;
import com.android.internal.widget.IRemoteViewsFactory;

class AppWidgetService extends IAppWidgetService.Stub
{
@@ -153,9 +153,12 @@ class AppWidgetService extends IAppWidgetService.Stub
        }
    }

    // Manages connections to RemoteViewsServices
    // Manages active connections to RemoteViewsServices
    private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
        mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
    // Manages persistent references to RemoteViewsServices from different App Widgets
    private final HashMap<FilterComparison, HashSet<Integer>>
        mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();

    Context mContext;
    Locale mLocale;
@@ -429,6 +432,7 @@ class AppWidgetService extends IAppWidgetService.Stub
        }
    }

    // Binds to a specific RemoteViewsService
    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
        synchronized (mAppWidgetIds) {
            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
@@ -452,8 +456,8 @@ class AppWidgetService extends IAppWidgetService.Stub
            // that first.  (This does not allow multiple connections to the same service under
            // the same key)
            ServiceConnectionProxy conn = null;
            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
                    new FilterComparison(intent));
            FilterComparison fc = new FilterComparison(intent);
            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
            if (mBoundRemoteViewsServices.containsKey(key)) {
                conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
                conn.disconnect();
@@ -471,9 +475,15 @@ class AppWidgetService extends IAppWidgetService.Stub
            } finally {
                Binder.restoreCallingIdentity(token);
            }

            // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
            // when we can call back to the RemoteViewsService later to destroy associated
            // factories.
             WidgetRemoteViewsServiceBinding(appWidgetId, fc);
        }
    }

    // Unbinds from a specific RemoteViewsService
    public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
        synchronized (mAppWidgetIds) {
            // Unbind from the RemoteViewsService (which will trigger a callback to the bound
@@ -500,6 +510,7 @@ class AppWidgetService extends IAppWidgetService.Stub
        }
    }

    // Unbinds from a RemoteViewsService when we delete an app widget
    private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
        int appWidgetId = id.appWidgetId;
        // Unbind all connections to Services bound to this AppWidgetId
@@ -515,6 +526,71 @@ class AppWidgetService extends IAppWidgetService.Stub
                it.remove();
            }
        }

        // Check if we need to destroy any services (if no other app widgets are
        // referencing the same service)
        decrementAppWidgetServiceRefCount(appWidgetId);
    }

    // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
    private void destroyRemoteViewsService(final Intent intent) {
        final ServiceConnection conn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                final IRemoteViewsFactory cb =
                    IRemoteViewsFactory.Stub.asInterface(service);
                try {
                    cb.onDestroy(intent);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                mContext.unbindService(this);
            }
            @Override
            public void onServiceDisconnected(android.content.ComponentName name) {
                // Do nothing
            }
        };

        // Bind to the service and remove the static intent->factory mapping in the
        // RemoteViewsService.
        final long token = Binder.clearCallingIdentity();
        try {
            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    // Adds to the ref-count for a given RemoteViewsService intent
    private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
        HashSet<Integer> appWidgetIds = null;
        if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
            appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
        } else {
            appWidgetIds = new HashSet<Integer>();
            mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
        }
        appWidgetIds.add(appWidgetId);
    }

    // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
    // the ref-count reaches zero.
    private void decrementAppWidgetServiceRefCount(int appWidgetId) {
        Iterator<FilterComparison> it =
            mRemoteViewsServicesAppWidgets.keySet().iterator();
        while (it.hasNext()) {
            final FilterComparison key = it.next();
            final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
            if (ids.remove(appWidgetId)) {
                // If we have removed the last app widget referencing this service, then we
                // should destroy it and remove it from this set
                if (ids.isEmpty()) {
                    destroyRemoteViewsService(key.getIntent());
                    it.remove();
                }
            }
        }
    }

    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {