am aac19783
: Merge "Refactoring app widgets to address security/performance issues." into honeycomb
* commit 'aac197833f3b2deddc6b3da5c144be36721d9547': Refactoring app widgets to address security/performance issues.
This commit is contained in:
@ -167,6 +167,7 @@ LOCAL_SRC_FILES += \
|
||||
core/java/com/android/internal/view/IInputMethodManager.aidl \
|
||||
core/java/com/android/internal/view/IInputMethodSession.aidl \
|
||||
core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
|
||||
core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
|
||||
location/java/android/location/ICountryDetector.aidl \
|
||||
location/java/android/location/ICountryListener.aidl \
|
||||
location/java/android/location/IGeocodeProvider.aidl \
|
||||
|
28
api/11.xml
28
api/11.xml
@ -188,6 +188,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="BIND_REMOTEVIEWS"
|
||||
type="java.lang.String"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value=""android.permission.BIND_REMOTEVIEWS""
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="BIND_WALLPAPER"
|
||||
type="java.lang.String"
|
||||
transient="false"
|
||||
@ -252332,6 +252343,23 @@
|
||||
<parameter name="intent" type="android.content.Intent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="setRemoteAdapter"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="appWidgetId" type="int">
|
||||
</parameter>
|
||||
<parameter name="viewId" type="int">
|
||||
</parameter>
|
||||
<parameter name="intent" type="android.content.Intent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="setScrollPosition"
|
||||
return="void"
|
||||
abstract="false"
|
||||
|
@ -188,6 +188,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="BIND_REMOTEVIEWS"
|
||||
type="java.lang.String"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value=""android.permission.BIND_REMOTEVIEWS""
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="BIND_WALLPAPER"
|
||||
type="java.lang.String"
|
||||
transient="false"
|
||||
@ -253918,6 +253929,23 @@
|
||||
<parameter name="intent" type="android.content.Intent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="setRemoteAdapter"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="appWidgetId" type="int">
|
||||
</parameter>
|
||||
<parameter name="viewId" type="int">
|
||||
</parameter>
|
||||
<parameter name="intent" type="android.content.Intent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="setScrollPosition"
|
||||
return="void"
|
||||
abstract="false"
|
||||
@ -261711,7 +261739,7 @@
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="arg0" type="T">
|
||||
<parameter name="t" type="T">
|
||||
</parameter>
|
||||
</method>
|
||||
</interface>
|
||||
|
@ -18,6 +18,7 @@ package android.appwidget;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
@ -437,6 +438,47 @@ public class AppWidgetManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the RemoteViewsService for a given appWidgetId and intent.
|
||||
*
|
||||
* The appWidgetId specified must already be bound to the calling AppWidgetHost via
|
||||
* {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
|
||||
*
|
||||
* @param appWidgetId The AppWidget instance for which to bind the RemoteViewsService.
|
||||
* @param intent The intent of the service which will be providing the data to the
|
||||
* RemoteViewsAdapter.
|
||||
* @param connection The callback interface to be notified when a connection is made or lost.
|
||||
* @hide
|
||||
*/
|
||||
public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
|
||||
try {
|
||||
sService.bindRemoteViewsService(appWidgetId, intent, connection);
|
||||
}
|
||||
catch (RemoteException e) {
|
||||
throw new RuntimeException("system server dead?", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbinds the RemoteViewsService for a given appWidgetId and intent.
|
||||
*
|
||||
* The appWidgetId specified muse already be bound to the calling AppWidgetHost via
|
||||
* {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
|
||||
*
|
||||
* @param appWidgetId The AppWidget instance for which to bind the RemoteViewsService.
|
||||
* @param intent The intent of the service which will be providing the data to the
|
||||
* RemoteViewsAdapter.
|
||||
* @hide
|
||||
*/
|
||||
public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
|
||||
try {
|
||||
sService.unbindRemoteViewsService(appWidgetId, intent);
|
||||
}
|
||||
catch (RemoteException e) {
|
||||
throw new RuntimeException("system server dead?", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of appWidgetIds that have been bound to the given AppWidget
|
||||
* provider.
|
||||
|
@ -58,6 +58,12 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
|
||||
private static final String LOG_TAG = "RemoteViews";
|
||||
|
||||
/**
|
||||
* The intent extra that contains the appWidgetId.
|
||||
* @hide
|
||||
*/
|
||||
static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
|
||||
|
||||
/**
|
||||
* The package name of the package containing the layout
|
||||
* resource. (Added to the parcel)
|
||||
@ -1276,6 +1282,22 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
* providing data to the RemoteViewsAdapter
|
||||
*/
|
||||
public void setRemoteAdapter(int viewId, Intent intent) {
|
||||
// Do nothing. This method will be removed after all widgets have been updated to the
|
||||
// new API.
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
|
||||
*
|
||||
* @param appWidgetId The id of the app widget which contains the specified view
|
||||
* @param viewId The id of the view whose text should change
|
||||
* @param intent The intent of the service which will be
|
||||
* providing data to the RemoteViewsAdapter
|
||||
*/
|
||||
public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
|
||||
// Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
|
||||
// RemoteViewsService
|
||||
intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, appWidgetId);
|
||||
setIntent(viewId, "setRemoteViewsAdapter", intent);
|
||||
}
|
||||
|
||||
|
@ -22,20 +22,21 @@ import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
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 com.android.internal.widget.IRemoteViewsAdapterConnection;
|
||||
import com.android.internal.widget.IRemoteViewsFactory;
|
||||
|
||||
/**
|
||||
@ -43,11 +44,22 @@ import com.android.internal.widget.IRemoteViewsFactory;
|
||||
* to be later inflated as child views.
|
||||
*/
|
||||
/** @hide */
|
||||
public class RemoteViewsAdapter extends BaseAdapter {
|
||||
public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback {
|
||||
private static final String TAG = "RemoteViewsAdapter";
|
||||
|
||||
private Context mContext;
|
||||
private Intent mIntent;
|
||||
// The max number of items in the cache
|
||||
private static final int sDefaultCacheSize = 36;
|
||||
// The delay (in millis) to wait until attempting to unbind from a service after a request.
|
||||
// This ensures that we don't stay continually bound to the service and that it can be destroyed
|
||||
// if we need the memory elsewhere in the system.
|
||||
private static final int sUnbindServiceDelay = 5000;
|
||||
// Type defs for controlling different messages across the main and worker message queues
|
||||
private static final int sDefaultMessageType = 0;
|
||||
private static final int sUnbindServiceMessageType = 1;
|
||||
|
||||
private final Context mContext;
|
||||
private final Intent mIntent;
|
||||
private final int mAppWidgetId;
|
||||
private LayoutInflater mLayoutInflater;
|
||||
private RemoteViewsAdapterServiceConnection mServiceConnection;
|
||||
private WeakReference<RemoteAdapterConnectionCallback> mCallback;
|
||||
@ -79,7 +91,8 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
||||
* garbage collected, and would cause us to leak activities due to the caching mechanism for
|
||||
* FrameLayouts in the adapter).
|
||||
*/
|
||||
private static class RemoteViewsAdapterServiceConnection implements ServiceConnection {
|
||||
private static class RemoteViewsAdapterServiceConnection extends
|
||||
IRemoteViewsAdapterConnection.Stub {
|
||||
private boolean mConnected;
|
||||
private WeakReference<RemoteViewsAdapter> mAdapter;
|
||||
private IRemoteViewsFactory mRemoteViewsFactory;
|
||||
@ -88,8 +101,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
||||
mAdapter = new WeakReference<RemoteViewsAdapter>(adapter);
|
||||
}
|
||||
|
||||
public void onServiceConnected(ComponentName name,
|
||||
IBinder service) {
|
||||
public void onServiceConnected(IBinder service) {
|
||||
mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
|
||||
mConnected = true;
|
||||
|
||||
@ -137,7 +149,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
||||
});
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
public void onServiceDisconnected() {
|
||||
mConnected = false;
|
||||
mRemoteViewsFactory = null;
|
||||
|
||||
@ -145,8 +157,9 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
||||
if (adapter == null) return;
|
||||
|
||||
// Clear the main/worker queues
|
||||
adapter.mMainQueue.removeMessages(0);
|
||||
adapter.mWorkerQueue.removeMessages(0);
|
||||
adapter.mMainQueue.removeMessages(sUnbindServiceMessageType);
|
||||
adapter.mMainQueue.removeMessages(sDefaultMessageType);
|
||||
adapter.mWorkerQueue.removeMessages(sDefaultMessageType);
|
||||
|
||||
final RemoteAdapterConnectionCallback callback = adapter.mCallback.get();
|
||||
if (callback != null) {
|
||||
@ -574,20 +587,26 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
||||
public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
|
||||
mContext = context;
|
||||
mIntent = intent;
|
||||
mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
|
||||
mLayoutInflater = LayoutInflater.from(context);
|
||||
if (mIntent == null) {
|
||||
throw new IllegalArgumentException("Non-null Intent must be specified.");
|
||||
}
|
||||
mRequestedViews = new RemoteViewsFrameLayoutRefSet();
|
||||
|
||||
// initialize the worker thread
|
||||
// Strip the previously injected app widget id from service intent
|
||||
if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) {
|
||||
intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID);
|
||||
}
|
||||
|
||||
// Initialize the worker thread
|
||||
mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
|
||||
mWorkerThread.start();
|
||||
mWorkerQueue = new Handler(mWorkerThread.getLooper());
|
||||
mMainQueue = new Handler(Looper.myLooper());
|
||||
mMainQueue = new Handler(Looper.myLooper(), this);
|
||||
|
||||
// initialize the cache and the service connection on startup
|
||||
mCache = new FixedSizeRemoteViewsCache(50);
|
||||
// Initialize the cache and the service connection on startup
|
||||
mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize);
|
||||
mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback);
|
||||
mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
|
||||
requestBindService();
|
||||
@ -687,6 +706,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
||||
@Override
|
||||
public void run() {
|
||||
mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId);
|
||||
enqueueDeferredUnbindServiceMessage();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -879,10 +899,34 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
||||
super.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
boolean result = false;
|
||||
switch (msg.what) {
|
||||
case sUnbindServiceMessageType:
|
||||
final AppWidgetManager mgr = AppWidgetManager.getInstance(mContext);
|
||||
if (mServiceConnection.isConnected()) {
|
||||
mgr.unbindRemoteViewsService(mAppWidgetId, mIntent);
|
||||
}
|
||||
result = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void enqueueDeferredUnbindServiceMessage() {
|
||||
// Remove any existing deferred-unbind messages
|
||||
mMainQueue.removeMessages(sUnbindServiceMessageType);
|
||||
mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay);
|
||||
}
|
||||
|
||||
private boolean requestBindService() {
|
||||
// try binding the service (which will start it if it's not already running)
|
||||
// Try binding the service (which will start it if it's not already running)
|
||||
if (!mServiceConnection.isConnected()) {
|
||||
mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
|
||||
final AppWidgetManager mgr = AppWidgetManager.getInstance(mContext);
|
||||
mgr.bindRemoteViewsService(mAppWidgetId, mIntent, mServiceConnection.asBinder());
|
||||
}
|
||||
|
||||
return mServiceConnection.isConnected();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* Copyright (C) 2011 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.
|
||||
@ -17,8 +17,10 @@
|
||||
package com.android.internal.appwidget;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import com.android.internal.appwidget.IAppWidgetHost;
|
||||
import android.os.IBinder;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
/** {@hide} */
|
||||
@ -46,6 +48,8 @@ interface IAppWidgetService {
|
||||
List<AppWidgetProviderInfo> getInstalledProviders();
|
||||
AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId);
|
||||
void bindAppWidgetId(int appWidgetId, in ComponentName provider);
|
||||
void bindRemoteViewsService(int appWidgetId, in Intent intent, in IBinder connection);
|
||||
void unbindRemoteViewsService(int appWidgetId, in Intent intent);
|
||||
int[] getAppWidgetIds(in ComponentName provider);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.internal.widget;
|
||||
|
||||
import android.os.IBinder;
|
||||
|
||||
/** {@hide} */
|
||||
interface IRemoteViewsAdapterConnection {
|
||||
void onServiceConnected(IBinder service);
|
||||
void onServiceDisconnected();
|
||||
}
|
@ -1210,6 +1210,13 @@
|
||||
android:description="@string/permdesc_backup"
|
||||
android:protectionLevel="signatureOrSystem" />
|
||||
|
||||
<!-- Must be required by a {@link android.widget.RemoteViewsService},
|
||||
to ensure that only the system can bind to it. -->
|
||||
<permission android:name="android.permission.BIND_REMOTEVIEWS"
|
||||
android:label="@string/permlab_bindRemoteViews"
|
||||
android:description="@string/permdesc_bindRemoteViews"
|
||||
android:protectionLevel="signatureOrSystem" />
|
||||
|
||||
<!-- Allows an application to tell the AppWidget service which application
|
||||
can access AppWidget's data. The normal user flow is that a user
|
||||
picks an AppWidget to go into a particular host, thereby giving that
|
||||
|
@ -680,6 +680,12 @@
|
||||
<string name="permdesc_bindWallpaper">Allows the holder to bind to the top-level
|
||||
interface of a wallpaper. Should never be needed for normal applications.</string>
|
||||
|
||||
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
<string name="permlab_bindRemoteViews">bind to a widget service</string>
|
||||
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
<string name="permdesc_bindRemoteViews">Allows the holder to bind to the top-level
|
||||
interface of a widget service. Should never be needed for normal applications.</string>
|
||||
|
||||
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
<string name="permlab_bindDeviceAdmin">interact with a device admin</string>
|
||||
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
|
@ -16,6 +16,23 @@
|
||||
|
||||
package com.android.server;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
@ -24,46 +41,37 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.Intent.FilterComparison;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
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;
|
||||
import android.os.SystemClock;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Pair;
|
||||
import android.util.Slog;
|
||||
import android.util.TypedValue;
|
||||
import android.util.Xml;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.android.internal.appwidget.IAppWidgetService;
|
||||
import com.android.internal.appwidget.IAppWidgetHost;
|
||||
import com.android.internal.appwidget.IAppWidgetService;
|
||||
import com.android.internal.util.FastXmlSerializer;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
import com.android.internal.widget.IRemoteViewsAdapterConnection;
|
||||
|
||||
class AppWidgetService extends IAppWidgetService.Stub
|
||||
{
|
||||
@ -107,6 +115,56 @@ class AppWidgetService extends IAppWidgetService.Stub
|
||||
Host host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
|
||||
* This needs to be a static inner class since a reference to the ServiceConnection is held
|
||||
* globally and may lead us to leak AppWidgetService instances (if there were more than one).
|
||||
*/
|
||||
static class ServiceConnectionProxy implements ServiceConnection {
|
||||
private final AppWidgetService mAppWidgetService;
|
||||
private final Pair<Integer, Intent.FilterComparison> mKey;
|
||||
private final IBinder mConnectionCb;
|
||||
|
||||
ServiceConnectionProxy(AppWidgetService appWidgetService,
|
||||
Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
|
||||
mAppWidgetService = appWidgetService;
|
||||
mKey = key;
|
||||
mConnectionCb = connectionCb;
|
||||
}
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
IRemoteViewsAdapterConnection cb =
|
||||
IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
|
||||
try {
|
||||
cb.onServiceConnected(service);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
IRemoteViewsAdapterConnection cb =
|
||||
IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
|
||||
try {
|
||||
cb.onServiceDisconnected();
|
||||
mAppWidgetService.mServiceConnectionUpdateHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
// We don't want to touch mBoundRemoteViewsServices from any other thread
|
||||
// so queue this to run on the main thread.
|
||||
if (mAppWidgetService.mBoundRemoteViewsServices.containsKey(mKey)) {
|
||||
mAppWidgetService.mBoundRemoteViewsServices.remove(mKey);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Manages connections to RemoteViewsServices
|
||||
private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
|
||||
mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
|
||||
private final Handler mServiceConnectionUpdateHandler = new Handler();
|
||||
|
||||
Context mContext;
|
||||
Locale mLocale;
|
||||
PackageManager mPackageManager;
|
||||
@ -294,6 +352,9 @@ class AppWidgetService extends IAppWidgetService.Stub
|
||||
}
|
||||
|
||||
void deleteAppWidgetLocked(AppWidgetId id) {
|
||||
// We first unbind all services that are bound to this id
|
||||
unbindAppWidgetRemoteViewsServicesLocked(id);
|
||||
|
||||
Host host = id.host;
|
||||
host.instances.remove(id);
|
||||
pruneHostLocked(host);
|
||||
@ -376,6 +437,77 @@ class AppWidgetService extends IAppWidgetService.Stub
|
||||
}
|
||||
}
|
||||
|
||||
public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
|
||||
synchronized (mAppWidgetIds) {
|
||||
AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("bad appWidgetId");
|
||||
}
|
||||
final ComponentName componentName = intent.getComponent();
|
||||
try {
|
||||
final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
|
||||
PackageManager.GET_PERMISSIONS);
|
||||
if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
|
||||
throw new SecurityException("Selected service does not require "
|
||||
+ android.Manifest.permission.BIND_REMOTEVIEWS
|
||||
+ ": " + componentName);
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new IllegalArgumentException("Unknown component " + componentName);
|
||||
}
|
||||
|
||||
// Bind to the RemoteViewsService (which will trigger a callback to the
|
||||
// RemoteViewsAdapter)
|
||||
Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
|
||||
new FilterComparison(intent));
|
||||
final ServiceConnection conn = new ServiceConnectionProxy(this, key, connection);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
|
||||
mBoundRemoteViewsServices.put(key, conn);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
|
||||
synchronized (mAppWidgetIds) {
|
||||
AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("bad appWidgetId");
|
||||
}
|
||||
|
||||
// Unbind from the RemoteViewsService (which will trigger a callback to the bound
|
||||
// RemoteViewsAdapter)
|
||||
Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
|
||||
new FilterComparison(intent));
|
||||
if (mBoundRemoteViewsServices.containsKey(key)) {
|
||||
final ServiceConnection conn = mBoundRemoteViewsServices.get(key);
|
||||
conn.onServiceDisconnected(null);
|
||||
mContext.unbindService(conn);
|
||||
mBoundRemoteViewsServices.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
|
||||
Iterator<Pair<Integer, Intent.FilterComparison>> it =
|
||||
mBoundRemoteViewsServices.keySet().iterator();
|
||||
int appWidgetId = id.appWidgetId;
|
||||
|
||||
// Unbind all connections to AppWidgets bound to this id
|
||||
while (it.hasNext()) {
|
||||
final Pair<Integer, Intent.FilterComparison> key = it.next();
|
||||
if (key.first.intValue() == appWidgetId) {
|
||||
final ServiceConnection conn = mBoundRemoteViewsServices.get(key);
|
||||
it.remove();
|
||||
conn.onServiceDisconnected(null);
|
||||
mContext.unbindService(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
|
||||
synchronized (mAppWidgetIds) {
|
||||
AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
|
||||
|
Reference in New Issue
Block a user