summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/widget/RemoteViewsAdapter.java3
-rw-r--r--core/java/android/widget/RemoteViewsService.java24
-rw-r--r--core/java/com/android/internal/widget/IRemoteViewsFactory.aidl2
-rw-r--r--services/java/com/android/server/AppWidgetService.java86
4 files changed, 100 insertions, 15 deletions
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 13a911b..2d2165b 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -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;
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index fb2c416..e0b08d4 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -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);
diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
index 60eca00..5857acb 100644
--- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
+++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
@@ -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();
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index ad25657..62b26c4 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -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) {