Loading core/java/android/widget/RemoteViewsAdapter.java +1 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading core/java/android/widget/RemoteViewsService.java +16 −8 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading Loading @@ -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; Loading @@ -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); Loading core/java/com/android/internal/widget/IRemoteViewsFactory.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading services/java/com/android/server/AppWidgetService.java +81 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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 { Loading Loading @@ -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; Loading Loading @@ -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); Loading @@ -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(); Loading @@ -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 Loading @@ -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 Loading @@ -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) { Loading Loading
core/java/android/widget/RemoteViewsAdapter.java +1 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading
core/java/android/widget/RemoteViewsService.java +16 −8 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading Loading @@ -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; Loading @@ -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); Loading
core/java/com/android/internal/widget/IRemoteViewsFactory.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading
services/java/com/android/server/AppWidgetService.java +81 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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 { Loading Loading @@ -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; Loading Loading @@ -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); Loading @@ -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(); Loading @@ -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 Loading @@ -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 Loading @@ -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) { Loading